Project

General

Profile

Improvements #14550 ยป wt-handler-factory-no-minified.patch

Will Johnson, 05/20/2026 07:54 PM

View differences:

src/web/DomElement.C
// events on the dom root container are events received by the whole
// document when no element has focus
// Fast path: most exposed-signal handlers are produced by setEvent() from a
// small set of deterministic templates. Recognize the three common shapes
// (simple update, guarded update, anchor-guarded update) and emit a call
// into a shared closure factory in Wt.js instead of a unique per-element
// `function fNNN(event) { ... }` declaration. The factory closures
// reproduce the inlined handler body exactly; this is a payload-size
// optimization, not a behavior change. Other handler shapes fall through
// to the unchanged per-element function emission below.
if (!globalUnfocused_ && !handler.signalName.empty()) {
static const std::string prelude = "var e=event||window.event,o=this;";
const std::string& body = handler.jsCode;
if (body.size() > prelude.size()
&& body.compare(0, prelude.size(), prelude) == 0) {
static const std::string anchorOpen =
"if(e.ctrlKey||e.metaKey||e.shiftKey||(" WT_CLASS ".button(e) > 1))"
"return true;else{";
std::size_t bodyStart = prelude.size();
std::size_t bodyEnd = body.size();
bool anchor = false;
if (body.size() >= bodyStart + anchorOpen.size() + 1
&& body.compare(bodyStart, anchorOpen.size(), anchorOpen) == 0
&& body.back() == '}') {
anchor = true;
bodyStart += anchorOpen.size();
--bodyEnd;
}
// Keep the "Wt-no-default" string and the cancelEvent/preventDefault
// structure in sync with the HandlerChecks built by
// WInteractWidget::updateSignalConnection() and with the same string
// in the _mkgu / _mkau factories in src/web/skeleton/Wt.{,min.}js.
// Detection deliberately uses the literal here rather than the
// WInteractWidget::noDefault constant so that a future rename of the
// constant fails closed (handlers fall through to per-element
// function emission, payload regresses) rather than silently
// running a factory whose Wt.js body still checks the old class.
const std::string handlerChecks =
"if(o.classList.contains('" + app->theme()->disabledClass() + "')){"
WT_CLASS ".cancelEvent(e);return;}"
"if(o.classList.contains('Wt-no-default')){e.preventDefault();}";
bool guarded = false;
if (bodyEnd >= bodyStart + handlerChecks.size()
&& body.compare(bodyStart, handlerChecks.size(), handlerChecks) == 0) {
guarded = true;
bodyStart += handlerChecks.size();
}
// The anchor-without-guard shape is reachable only from direct
// DomElement::setEvent callers on a raw <a> element (stock Wt anchor
// clicks always go through WInteractWidget, which injects HandlerChecks
// before setEvent runs). Skip optimizing that case; the AnchorGuarded
// factory adds a disabled-class check the original handler did not run,
// which would be observable behavior change.
if (anchor && !guarded) {
// fall through to per-element function emission
} else {
const std::string updateCall =
app->javaScriptClass() + "._p_.update(o,'" + handler.signalName +
"',e,true);";
if (bodyEnd - bodyStart == updateCall.size()
&& body.compare(bodyStart, updateCall.size(), updateCall) == 0) {
// Factory contract: _mksu(app, sig) takes two arguments;
// _mkgu(app, dc, sig) and _mkau(app, dc, sig) take three. Keep
// emission below in sync with these signatures.
const char *factory =
anchor ? "._mkau(" :
guarded ? "._mkgu(" :
"._mksu(";
const bool wheelOnIE =
(eventName == WInteractWidget::WHEEL_SIGNAL &&
app->environment().agentIsIE() &&
static_cast<unsigned int>(app->environment().agent()) >=
static_cast<unsigned int>(UserAgent::IE9));
declare(out);
if (wheelOnIE)
out << var_ << ".addEventListener('wheel',";
else
out << var_ << ".on" << eventName << '=';
out << WT_CLASS << factory << app->javaScriptClass();
if (anchor || guarded)
out << ",'" << app->theme()->disabledClass() << '\'';
out << ",'" << handler.signalName << "')";
if (wheelOnIE)
out << ",false)";
out << ";\n";
return;
}
}
}
}
unsigned fid = nextId_++;
out << "function f" << fid << "(event) { ";
src/web/skeleton/Wt.js
}
};
// Closure factories used by DomElement::setJavaScriptEvent() to avoid
// emitting a unique `function fNNN(event){...}` declaration for every
// exposed-signal event handler. Each factory returns a per-element
// closure whose body reproduces, byte-for-byte at runtime, the inlined
// handler that DomElement::setEvent() would otherwise have built.
//
// _mksu โ€” simple update: just the signal dispatch.
// _mkgu โ€” guarded update: disabled-class + no-default-class checks,
// then signal dispatch (non-anchor click pattern).
// _mkau โ€” anchor-guarded update: ctrl/meta/shift / non-primary-button
// bypass, then guarded update (anchor click pattern).
//
// The literal "Wt-no-default" below must match WInteractWidget::noDefault
// (src/Wt/WInteractWidget.C) and the matching literal in
// DomElement::setJavaScriptEvent(). All three sites must be updated
// together if that class name ever changes.
this._mksu = function(app, sig) {
return function(event) {
const e = event || window.event, o = this;
app._p_.update(o, sig, e, true);
};
};
this._mkgu = function(app, dc, sig) {
return function(event) {
const e = event || window.event, o = this;
if (o.classList.contains(dc)) { WT.cancelEvent(e); return; }
if (o.classList.contains("Wt-no-default")) { e.preventDefault(); }
app._p_.update(o, sig, e, true);
};
};
this._mkau = function(app, dc, sig) {
return function(event) {
const e = event || window.event, o = this;
if (e.ctrlKey || e.metaKey || e.shiftKey || (WT.button(e) > 1)) {
return true;
}
if (o.classList.contains(dc)) { WT.cancelEvent(e); return; }
if (o.classList.contains("Wt-no-default")) { e.preventDefault(); }
app._p_.update(o, sig, e, true);
};
};
this.getElement = function(id) {
let el = document.getElementById(id);
if (!el) {
    (1-1/1)