mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 17:34:47 -05:00
refactor: deduplicate defineEventHandler
util (#12367)
This commit is contained in:
parent
7e38ae17ea
commit
74e5b68682
7 changed files with 64 additions and 198 deletions
|
@ -7,7 +7,7 @@
|
||||||
((window) => {
|
((window) => {
|
||||||
const core = window.Deno.core;
|
const core = window.Deno.core;
|
||||||
const webidl = window.__bootstrap.webidl;
|
const webidl = window.__bootstrap.webidl;
|
||||||
const { setTarget } = window.__bootstrap.event;
|
const { defineEventHandler, setTarget } = window.__bootstrap.event;
|
||||||
const { DOMException } = window.__bootstrap.domException;
|
const { DOMException } = window.__bootstrap.domException;
|
||||||
const {
|
const {
|
||||||
ArrayPrototypeIndexOf,
|
ArrayPrototypeIndexOf,
|
||||||
|
@ -15,55 +15,8 @@
|
||||||
ArrayPrototypePush,
|
ArrayPrototypePush,
|
||||||
Symbol,
|
Symbol,
|
||||||
Uint8Array,
|
Uint8Array,
|
||||||
ObjectDefineProperty,
|
|
||||||
Map,
|
|
||||||
MapPrototypeSet,
|
|
||||||
MapPrototypeGet,
|
|
||||||
FunctionPrototypeCall,
|
|
||||||
} = window.__bootstrap.primordials;
|
} = window.__bootstrap.primordials;
|
||||||
|
|
||||||
const handlerSymbol = Symbol("eventHandlers");
|
|
||||||
function makeWrappedHandler(handler) {
|
|
||||||
function wrappedHandler(...args) {
|
|
||||||
if (typeof wrappedHandler.handler !== "function") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return FunctionPrototypeCall(wrappedHandler.handler, this, ...args);
|
|
||||||
}
|
|
||||||
wrappedHandler.handler = handler;
|
|
||||||
return wrappedHandler;
|
|
||||||
}
|
|
||||||
// TODO(lucacasonato) reuse when we can reuse code between web crates
|
|
||||||
function defineEventHandler(emitter, name) {
|
|
||||||
// HTML specification section 8.1.5.1
|
|
||||||
ObjectDefineProperty(emitter, `on${name}`, {
|
|
||||||
get() {
|
|
||||||
// TODO(bnoordhuis) The "BroadcastChannel should have an onmessage
|
|
||||||
// event" WPT test expects that .onmessage !== undefined. Returning
|
|
||||||
// null makes it pass but is perhaps not exactly in the spirit.
|
|
||||||
if (!this[handlerSymbol]) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return MapPrototypeGet(this[handlerSymbol], name)?.handler ?? null;
|
|
||||||
},
|
|
||||||
set(value) {
|
|
||||||
if (!this[handlerSymbol]) {
|
|
||||||
this[handlerSymbol] = new Map();
|
|
||||||
}
|
|
||||||
let handlerWrapper = MapPrototypeGet(this[handlerSymbol], name);
|
|
||||||
if (handlerWrapper) {
|
|
||||||
handlerWrapper.handler = value;
|
|
||||||
} else {
|
|
||||||
handlerWrapper = makeWrappedHandler(value);
|
|
||||||
this.addEventListener(name, handlerWrapper);
|
|
||||||
}
|
|
||||||
MapPrototypeSet(this[handlerSymbol], name, handlerWrapper);
|
|
||||||
},
|
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const _name = Symbol("[[name]]");
|
const _name = Symbol("[[name]]");
|
||||||
const _closed = Symbol("[[closed]]");
|
const _closed = Symbol("[[closed]]");
|
||||||
|
|
||||||
|
|
|
@ -1226,36 +1226,76 @@
|
||||||
|
|
||||||
const _eventHandlers = Symbol("eventHandlers");
|
const _eventHandlers = Symbol("eventHandlers");
|
||||||
|
|
||||||
function makeWrappedHandler(handler) {
|
function makeWrappedHandler(handler, isSpecialErrorEventHandler) {
|
||||||
function wrappedHandler(...args) {
|
function wrappedHandler(evt) {
|
||||||
if (typeof wrappedHandler.handler !== "function") {
|
if (typeof wrappedHandler.handler !== "function") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return FunctionPrototypeCall(wrappedHandler.handler, this, ...args);
|
|
||||||
|
if (
|
||||||
|
isSpecialErrorEventHandler &&
|
||||||
|
evt instanceof ErrorEvent && evt.type === "error"
|
||||||
|
) {
|
||||||
|
const ret = FunctionPrototypeCall(
|
||||||
|
wrappedHandler.handler,
|
||||||
|
this,
|
||||||
|
evt.message,
|
||||||
|
evt.filename,
|
||||||
|
evt.lineno,
|
||||||
|
evt.colno,
|
||||||
|
evt.error,
|
||||||
|
);
|
||||||
|
if (ret === true) {
|
||||||
|
evt.preventDefault();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FunctionPrototypeCall(wrappedHandler.handler, this, evt);
|
||||||
}
|
}
|
||||||
wrappedHandler.handler = handler;
|
wrappedHandler.handler = handler;
|
||||||
return wrappedHandler;
|
return wrappedHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(benjamingr) reuse this here and websocket where possible
|
// `init` is an optional function that will be called the first time that the
|
||||||
function defineEventHandler(emitter, name, init) {
|
// event handler property is set. It will be called with the object on which
|
||||||
// HTML specification section 8.1.5.1
|
// the property is set as its argument.
|
||||||
|
// `isSpecialErrorEventHandler` can be set to true to opt into the special
|
||||||
|
// behavior of event handlers for the "error" event in a global scope.
|
||||||
|
function defineEventHandler(
|
||||||
|
emitter,
|
||||||
|
name,
|
||||||
|
init = undefined,
|
||||||
|
isSpecialErrorEventHandler = false,
|
||||||
|
) {
|
||||||
|
// HTML specification section 8.1.7.1
|
||||||
ObjectDefineProperty(emitter, `on${name}`, {
|
ObjectDefineProperty(emitter, `on${name}`, {
|
||||||
get() {
|
get() {
|
||||||
const map = this[_eventHandlers];
|
if (!this[_eventHandlers]) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (!map) return undefined;
|
return MapPrototypeGet(this[_eventHandlers], name)?.handler ?? null;
|
||||||
return MapPrototypeGet(map, name)?.handler;
|
|
||||||
},
|
},
|
||||||
set(value) {
|
set(value) {
|
||||||
|
// All three Web IDL event handler types are nullable callback functions
|
||||||
|
// with the [LegacyTreatNonObjectAsNull] extended attribute, meaning
|
||||||
|
// anything other than an object is treated as null.
|
||||||
|
if (typeof value !== "object" && typeof value !== "function") {
|
||||||
|
value = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (!this[_eventHandlers]) {
|
if (!this[_eventHandlers]) {
|
||||||
this[_eventHandlers] = new Map();
|
this[_eventHandlers] = new Map();
|
||||||
}
|
}
|
||||||
let handlerWrapper = MapPrototypeGet(this[_eventHandlers], name);
|
let handlerWrapper = MapPrototypeGet(this[_eventHandlers], name);
|
||||||
if (handlerWrapper) {
|
if (handlerWrapper) {
|
||||||
handlerWrapper.handler = value;
|
handlerWrapper.handler = value;
|
||||||
} else {
|
} else if (value !== null) {
|
||||||
handlerWrapper = makeWrappedHandler(value);
|
handlerWrapper = makeWrappedHandler(
|
||||||
|
value,
|
||||||
|
isSpecialErrorEventHandler,
|
||||||
|
);
|
||||||
this.addEventListener(name, handlerWrapper);
|
this.addEventListener(name, handlerWrapper);
|
||||||
init?.(this);
|
init?.(this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
const webidl = window.__bootstrap.webidl;
|
const webidl = window.__bootstrap.webidl;
|
||||||
const { HTTP_TOKEN_CODE_POINT_RE } = window.__bootstrap.infra;
|
const { HTTP_TOKEN_CODE_POINT_RE } = window.__bootstrap.infra;
|
||||||
const { DOMException } = window.__bootstrap.domException;
|
const { DOMException } = window.__bootstrap.domException;
|
||||||
|
const { defineEventHandler } = window.__bootstrap.event;
|
||||||
const { Blob } = globalThis.__bootstrap.file;
|
const { Blob } = globalThis.__bootstrap.file;
|
||||||
const {
|
const {
|
||||||
ArrayBuffer,
|
ArrayBuffer,
|
||||||
|
@ -16,16 +17,11 @@
|
||||||
ArrayPrototypeJoin,
|
ArrayPrototypeJoin,
|
||||||
DataView,
|
DataView,
|
||||||
ErrorPrototypeToString,
|
ErrorPrototypeToString,
|
||||||
ObjectDefineProperty,
|
|
||||||
Map,
|
|
||||||
MapPrototypeGet,
|
|
||||||
MapPrototypeSet,
|
|
||||||
Set,
|
Set,
|
||||||
Symbol,
|
Symbol,
|
||||||
String,
|
String,
|
||||||
StringPrototypeToLowerCase,
|
StringPrototypeToLowerCase,
|
||||||
StringPrototypeEndsWith,
|
StringPrototypeEndsWith,
|
||||||
FunctionPrototypeCall,
|
|
||||||
RegExpPrototypeTest,
|
RegExpPrototypeTest,
|
||||||
ObjectDefineProperties,
|
ObjectDefineProperties,
|
||||||
ArrayPrototypeMap,
|
ArrayPrototypeMap,
|
||||||
|
@ -65,45 +61,6 @@
|
||||||
const CLOSING = 2;
|
const CLOSING = 2;
|
||||||
const CLOSED = 3;
|
const CLOSED = 3;
|
||||||
|
|
||||||
const handlerSymbol = Symbol("eventHandlers");
|
|
||||||
function makeWrappedHandler(handler) {
|
|
||||||
function wrappedHandler(...args) {
|
|
||||||
if (typeof wrappedHandler.handler !== "function") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return FunctionPrototypeCall(wrappedHandler.handler, this, ...args);
|
|
||||||
}
|
|
||||||
wrappedHandler.handler = handler;
|
|
||||||
return wrappedHandler;
|
|
||||||
}
|
|
||||||
// TODO(lucacasonato) reuse when we can reuse code between web crates
|
|
||||||
function defineEventHandler(emitter, name) {
|
|
||||||
// HTML specification section 8.1.5.1
|
|
||||||
ObjectDefineProperty(emitter, `on${name}`, {
|
|
||||||
get() {
|
|
||||||
if (!this[handlerSymbol]) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return MapPrototypeGet(this[handlerSymbol], name)?.handler;
|
|
||||||
},
|
|
||||||
set(value) {
|
|
||||||
if (!this[handlerSymbol]) {
|
|
||||||
this[handlerSymbol] = new Map();
|
|
||||||
}
|
|
||||||
let handlerWrapper = MapPrototypeGet(this[handlerSymbol], name);
|
|
||||||
if (handlerWrapper) {
|
|
||||||
handlerWrapper.handler = value;
|
|
||||||
} else {
|
|
||||||
handlerWrapper = makeWrappedHandler(value);
|
|
||||||
this.addEventListener(name, handlerWrapper);
|
|
||||||
}
|
|
||||||
MapPrototypeSet(this[handlerSymbol], name, handlerWrapper);
|
|
||||||
},
|
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const _readyState = Symbol("[[readyState]]");
|
const _readyState = Symbol("[[readyState]]");
|
||||||
const _url = Symbol("[[url]]");
|
const _url = Symbol("[[url]]");
|
||||||
const _rid = Symbol("[[rid]]");
|
const _rid = Symbol("[[rid]]");
|
||||||
|
|
|
@ -2,15 +2,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
((window) => {
|
((window) => {
|
||||||
const {
|
const { TypeError, Symbol } = window.__bootstrap.primordials;
|
||||||
FunctionPrototypeCall,
|
|
||||||
Map,
|
|
||||||
MapPrototypeGet,
|
|
||||||
MapPrototypeSet,
|
|
||||||
ObjectDefineProperty,
|
|
||||||
TypeError,
|
|
||||||
Symbol,
|
|
||||||
} = window.__bootstrap.primordials;
|
|
||||||
const illegalConstructorKey = Symbol("illegalConstructorKey");
|
const illegalConstructorKey = Symbol("illegalConstructorKey");
|
||||||
|
|
||||||
function requiredArguments(
|
function requiredArguments(
|
||||||
|
@ -26,75 +18,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlerSymbol = Symbol("eventHandlers");
|
|
||||||
function makeWrappedHandler(handler, isSpecialErrorEventHandler) {
|
|
||||||
function wrappedHandler(...args) {
|
|
||||||
if (typeof wrappedHandler.handler !== "function") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (isSpecialErrorEventHandler) {
|
|
||||||
const evt = args[0];
|
|
||||||
if (evt instanceof ErrorEvent && evt.type === "error") {
|
|
||||||
const ret = FunctionPrototypeCall(
|
|
||||||
wrappedHandler.handler,
|
|
||||||
this,
|
|
||||||
evt.message,
|
|
||||||
evt.filename,
|
|
||||||
evt.lineno,
|
|
||||||
evt.colno,
|
|
||||||
evt.error,
|
|
||||||
);
|
|
||||||
if (ret === true) {
|
|
||||||
evt.preventDefault();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return FunctionPrototypeCall(wrappedHandler.handler, this, ...args);
|
|
||||||
}
|
|
||||||
wrappedHandler.handler = handler;
|
|
||||||
return wrappedHandler;
|
|
||||||
}
|
|
||||||
function defineEventHandler(
|
|
||||||
emitter,
|
|
||||||
name,
|
|
||||||
defaultValue = undefined,
|
|
||||||
isSpecialErrorEventHandler = false,
|
|
||||||
) {
|
|
||||||
// HTML specification section 8.1.5.1
|
|
||||||
ObjectDefineProperty(emitter, `on${name}`, {
|
|
||||||
get() {
|
|
||||||
if (!this[handlerSymbol]) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return MapPrototypeGet(this[handlerSymbol], name)?.handler ??
|
|
||||||
defaultValue;
|
|
||||||
},
|
|
||||||
set(value) {
|
|
||||||
if (!this[handlerSymbol]) {
|
|
||||||
this[handlerSymbol] = new Map();
|
|
||||||
}
|
|
||||||
let handlerWrapper = MapPrototypeGet(this[handlerSymbol], name);
|
|
||||||
if (handlerWrapper) {
|
|
||||||
handlerWrapper.handler = value;
|
|
||||||
} else {
|
|
||||||
handlerWrapper = makeWrappedHandler(
|
|
||||||
value,
|
|
||||||
isSpecialErrorEventHandler,
|
|
||||||
);
|
|
||||||
this.addEventListener(name, handlerWrapper);
|
|
||||||
}
|
|
||||||
MapPrototypeSet(this[handlerSymbol], name, handlerWrapper);
|
|
||||||
},
|
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
window.__bootstrap.webUtil = {
|
window.__bootstrap.webUtil = {
|
||||||
illegalConstructorKey,
|
illegalConstructorKey,
|
||||||
requiredArguments,
|
requiredArguments,
|
||||||
defineEventHandler,
|
|
||||||
};
|
};
|
||||||
})(this);
|
})(this);
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
const { URL } = window.__bootstrap.url;
|
const { URL } = window.__bootstrap.url;
|
||||||
const { getLocationHref } = window.__bootstrap.location;
|
const { getLocationHref } = window.__bootstrap.location;
|
||||||
const { log, pathFromURL } = window.__bootstrap.util;
|
const { log, pathFromURL } = window.__bootstrap.util;
|
||||||
const { defineEventHandler } = window.__bootstrap.webUtil;
|
const { defineEventHandler } = window.__bootstrap.event;
|
||||||
const { deserializeJsMessageData, serializeJsMessageData } =
|
const { deserializeJsMessageData, serializeJsMessageData } =
|
||||||
window.__bootstrap.messagePort;
|
window.__bootstrap.messagePort;
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@ delete Object.prototype.__proto__;
|
||||||
const errors = window.__bootstrap.errors.errors;
|
const errors = window.__bootstrap.errors.errors;
|
||||||
const webidl = window.__bootstrap.webidl;
|
const webidl = window.__bootstrap.webidl;
|
||||||
const domException = window.__bootstrap.domException;
|
const domException = window.__bootstrap.domException;
|
||||||
const { defineEventHandler } = window.__bootstrap.webUtil;
|
const { defineEventHandler } = window.__bootstrap.event;
|
||||||
const { deserializeJsMessageData, serializeJsMessageData } =
|
const { deserializeJsMessageData, serializeJsMessageData } =
|
||||||
window.__bootstrap.messagePort;
|
window.__bootstrap.messagePort;
|
||||||
|
|
||||||
|
@ -479,10 +479,6 @@ delete Object.prototype.__proto__;
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
get: () => navigator,
|
get: () => navigator,
|
||||||
},
|
},
|
||||||
// TODO(bartlomieju): from MDN docs (https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope)
|
|
||||||
// it seems those two properties should be available to workers as well
|
|
||||||
onload: util.writable(null),
|
|
||||||
onunload: util.writable(null),
|
|
||||||
close: util.writable(windowClose),
|
close: util.writable(windowClose),
|
||||||
closed: util.getterOnly(() => windowIsClosing),
|
closed: util.getterOnly(() => windowIsClosing),
|
||||||
alert: util.writable(prompt.alert),
|
alert: util.writable(prompt.alert),
|
||||||
|
@ -514,8 +510,6 @@ delete Object.prototype.__proto__;
|
||||||
get: () => workerNavigator,
|
get: () => workerNavigator,
|
||||||
},
|
},
|
||||||
self: util.readOnly(globalThis),
|
self: util.readOnly(globalThis),
|
||||||
onmessage: util.writable(null),
|
|
||||||
onerror: util.writable(null),
|
|
||||||
// TODO(bartlomieju): should be readonly?
|
// TODO(bartlomieju): should be readonly?
|
||||||
close: util.nonEnumerable(workerClose),
|
close: util.nonEnumerable(workerClose),
|
||||||
postMessage: util.writable(postMessage),
|
postMessage: util.writable(postMessage),
|
||||||
|
@ -548,8 +542,8 @@ delete Object.prototype.__proto__;
|
||||||
|
|
||||||
eventTarget.setEventTargetData(globalThis);
|
eventTarget.setEventTargetData(globalThis);
|
||||||
|
|
||||||
defineEventHandler(window, "load", null);
|
defineEventHandler(window, "load");
|
||||||
defineEventHandler(window, "unload", null);
|
defineEventHandler(window, "unload");
|
||||||
|
|
||||||
const isUnloadDispatched = SymbolFor("isUnloadDispatched");
|
const isUnloadDispatched = SymbolFor("isUnloadDispatched");
|
||||||
// Stores the flag for checking whether unload is dispatched or not.
|
// Stores the flag for checking whether unload is dispatched or not.
|
||||||
|
@ -646,8 +640,8 @@ delete Object.prototype.__proto__;
|
||||||
|
|
||||||
eventTarget.setEventTargetData(globalThis);
|
eventTarget.setEventTargetData(globalThis);
|
||||||
|
|
||||||
defineEventHandler(self, "message", null);
|
defineEventHandler(self, "message");
|
||||||
defineEventHandler(self, "error", null, true);
|
defineEventHandler(self, "error", undefined, true);
|
||||||
|
|
||||||
runtimeStart(
|
runtimeStart(
|
||||||
runtimeOptions,
|
runtimeOptions,
|
||||||
|
|
|
@ -14979,9 +14979,9 @@
|
||||||
"constructor.any.html": true,
|
"constructor.any.html": true,
|
||||||
"constructor.any.html?wpt_flags=h2": true,
|
"constructor.any.html?wpt_flags=h2": true,
|
||||||
"constructor.any.html?wss": true,
|
"constructor.any.html?wss": true,
|
||||||
"eventhandlers.any.html": false,
|
"eventhandlers.any.html": true,
|
||||||
"eventhandlers.any.html?wpt_flags=h2": false,
|
"eventhandlers.any.html?wpt_flags=h2": true,
|
||||||
"eventhandlers.any.html?wss": false,
|
"eventhandlers.any.html?wss": true,
|
||||||
"referrer.any.html": true,
|
"referrer.any.html": true,
|
||||||
"Close-delayed.any.html": false,
|
"Close-delayed.any.html": false,
|
||||||
"Close-delayed.any.html?wpt_flags=h2": false,
|
"Close-delayed.any.html?wpt_flags=h2": false,
|
||||||
|
@ -15056,10 +15056,7 @@
|
||||||
"interfaces": {
|
"interfaces": {
|
||||||
"DedicatedWorkerGlobalScope": {
|
"DedicatedWorkerGlobalScope": {
|
||||||
"EventTarget.worker.html": true,
|
"EventTarget.worker.html": true,
|
||||||
"onmessage.worker.html": [
|
"onmessage.worker.html": true,
|
||||||
"Setting onmessage to 1",
|
|
||||||
"Setting onmessage to 1 (again)"
|
|
||||||
],
|
|
||||||
"postMessage": {
|
"postMessage": {
|
||||||
"return-value.worker.html": true
|
"return-value.worker.html": true
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue