fix(js): make Event/CustomEvent constructors match WebIDL
The Event and CustomEvent constructors diverged from Chrome in three observable ways: - new Event() / new CustomEvent() with no argument did not throw, though the type argument is required (Chrome: "1 argument required, but only 0 present."). initEvent/initMouseEvent already had this check; the constructors did not. - the type argument was not coerced to a string, so new Event(123).type was the number 123 instead of "123". - CustomEvent.detail defaulted to undefined; WebIDL CustomEventInit specifies `any detail = null`. Add the required-argument guard to both constructors, coerce type via String(t) (inherited by every subclass through super), and default detail to null. createEvent() builds events with an explicit '' type, so the arity guard leaves the type-"" uninitialized-event path intact. Fixes #552
This commit is contained in:
4
crates/obscura-js/js/bootstrap.js
vendored
4
crates/obscura-js/js/bootstrap.js
vendored
@@ -5086,7 +5086,7 @@ globalThis.__obscura_setInputFiles = function(el, specs) {
|
||||
try { el.dispatchEvent(globalThis.__obscura_markTrusted(new Event("change", { bubbles: true }))); } catch (_e) {}
|
||||
};
|
||||
globalThis.Event = class Event {
|
||||
constructor(t,o={}) { this.type=t;this.bubbles=!!o.bubbles;this.cancelable=!!o.cancelable;this.composed=!!o.composed;this.defaultPrevented=false;this.target=null;this.currentTarget=null;this.eventPhase=0;this.timeStamp=Date.now();this._propagationStopped=false;this._immediatePropagationStopped=false; }
|
||||
constructor(t,o={}) { if (arguments.length < 1) throw new TypeError("Failed to construct 'Event': 1 argument required, but only 0 present."); this.type=String(t);this.bubbles=!!o.bubbles;this.cancelable=!!o.cancelable;this.composed=!!o.composed;this.defaultPrevented=false;this.target=null;this.currentTarget=null;this.eventPhase=0;this.timeStamp=Date.now();this._propagationStopped=false;this._immediatePropagationStopped=false; }
|
||||
get isTrusted() { return _trustedEvents.has(this); }
|
||||
preventDefault() { if (this.cancelable) this.defaultPrevented=true; } stopPropagation(){ this._propagationStopped=true; } stopImmediatePropagation(){ this._propagationStopped=true; this._immediatePropagationStopped=true; }
|
||||
initEvent(type,bubbles,cancelable) { if (arguments.length < 1) throw new TypeError("Failed to execute 'initEvent' on 'Event': 1 argument required, but only 0 present."); this.type=String(type);this.bubbles=!!bubbles;this.cancelable=!!cancelable;this.defaultPrevented=false;this._propagationStopped=false;this._immediatePropagationStopped=false; }
|
||||
@@ -5101,7 +5101,7 @@ globalThis.Event = class Event {
|
||||
};
|
||||
_markNative(Event);
|
||||
globalThis.CustomEvent = class extends Event {
|
||||
constructor(t,o={}) { super(t,o);this.detail=o.detail; }
|
||||
constructor(t,o={}) { if (arguments.length < 1) throw new TypeError("Failed to construct 'CustomEvent': 1 argument required, but only 0 present."); super(t,o);this.detail=o.detail!==undefined?o.detail:null; }
|
||||
// Legacy DOM Level 2 init; some libraries (Starbucks China bundle, older
|
||||
// analytics shims) still call createEvent('CustomEvent') + initCustomEvent
|
||||
// instead of new CustomEvent(...). See issue #41.
|
||||
|
||||
@@ -3493,6 +3493,34 @@ mod tests {
|
||||
assert_eq!(kind, serde_json::json!(true));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn event_constructor_matches_webidl_conformance() {
|
||||
// new Event()/new CustomEvent() must throw (type is a required arg),
|
||||
// the type argument must be coerced to a string, CustomEvent.detail must
|
||||
// default to null (not undefined), createEvent must still build a
|
||||
// type-"" event, and an explicit detail must be preserved.
|
||||
let mut rt = setup_runtime("<html><body></body></html>");
|
||||
let v = rt
|
||||
.evaluate(
|
||||
"(function(){\
|
||||
var out=[];\
|
||||
try{new Event();out.push('no-throw')}catch(e){out.push(e.name)}\
|
||||
try{new CustomEvent();out.push('no-throw')}catch(e){out.push(e.name)}\
|
||||
out.push(new Event(123).type+':'+typeof new Event(123).type);\
|
||||
out.push(String(new CustomEvent('x').detail));\
|
||||
out.push(String(new CustomEvent('x',{detail:7}).detail));\
|
||||
out.push(new Event('click').type);\
|
||||
out.push(JSON.stringify(document.createEvent('Event').type));\
|
||||
return out.join('|');\
|
||||
})()",
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
v,
|
||||
serde_json::json!("TypeError|TypeError|123:string|null|7|click|\"\"")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_promise_rejection_event_requires_promise() {
|
||||
let mut rt = setup_runtime("<html><body></body></html>");
|
||||
|
||||
Reference in New Issue
Block a user