summaryrefslogtreecommitdiff
path: root/ext/web/02_event.js
diff options
context:
space:
mode:
Diffstat (limited to 'ext/web/02_event.js')
-rw-r--r--ext/web/02_event.js2559
1 files changed, 1282 insertions, 1277 deletions
diff --git a/ext/web/02_event.js b/ext/web/02_event.js
index c99eb8f6e..de5210d33 100644
--- a/ext/web/02_event.js
+++ b/ext/web/02_event.js
@@ -4,1520 +4,1525 @@
// Many parts of the DOM are not implemented in Deno, but the logic for those
// parts still exists. This means you will observe a lot of strange structures
// and impossible logic branches based on what Deno currently supports.
-"use strict";
-
-((window) => {
- const core = window.Deno.core;
- const ops = core.ops;
- const webidl = window.__bootstrap.webidl;
- const { DOMException } = window.__bootstrap.domException;
- const consoleInternal = window.__bootstrap.console;
- const {
- ArrayPrototypeFilter,
- ArrayPrototypeIncludes,
- ArrayPrototypeIndexOf,
- ArrayPrototypeMap,
- ArrayPrototypePush,
- ArrayPrototypeSlice,
- ArrayPrototypeSplice,
- ArrayPrototypeUnshift,
- Boolean,
- DateNow,
- Error,
- FunctionPrototypeCall,
- Map,
- MapPrototypeGet,
- MapPrototypeSet,
- ObjectCreate,
- ObjectDefineProperty,
- ObjectGetOwnPropertyDescriptor,
- ObjectPrototypeIsPrototypeOf,
- ReflectDefineProperty,
- ReflectHas,
- SafeArrayIterator,
- StringPrototypeStartsWith,
- Symbol,
- SymbolFor,
- SymbolToStringTag,
- TypeError,
- } = window.__bootstrap.primordials;
-
- // accessors for non runtime visible data
-
- function getDispatched(event) {
- return Boolean(event[_dispatched]);
- }
-
- function getPath(event) {
- return event[_path] ?? [];
- }
-
- function getStopImmediatePropagation(event) {
- return Boolean(event[_stopImmediatePropagationFlag]);
- }
-
- function setCurrentTarget(
- event,
- value,
- ) {
- event[_attributes].currentTarget = value;
- }
-
- function setIsTrusted(event, value) {
- event[_isTrusted] = value;
- }
- function setDispatched(event, value) {
- event[_dispatched] = value;
- }
-
- function setEventPhase(event, value) {
- event[_attributes].eventPhase = value;
+const core = globalThis.Deno.core;
+const ops = core.ops;
+import * as webidl from "internal:ext/webidl/00_webidl.js";
+import DOMException from "internal:ext/web/01_dom_exception.js";
+import { createFilteredInspectProxy } from "internal:ext/console/02_console.js";
+const primordials = globalThis.__bootstrap.primordials;
+const {
+ ArrayPrototypeFilter,
+ ArrayPrototypeIncludes,
+ ArrayPrototypeIndexOf,
+ ArrayPrototypeMap,
+ ArrayPrototypePush,
+ ArrayPrototypeSlice,
+ ArrayPrototypeSplice,
+ ArrayPrototypeUnshift,
+ Boolean,
+ DateNow,
+ Error,
+ FunctionPrototypeCall,
+ Map,
+ MapPrototypeGet,
+ MapPrototypeSet,
+ ObjectCreate,
+ ObjectDefineProperty,
+ ObjectGetOwnPropertyDescriptor,
+ ObjectPrototypeIsPrototypeOf,
+ ReflectDefineProperty,
+ ReflectHas,
+ SafeArrayIterator,
+ StringPrototypeStartsWith,
+ Symbol,
+ SymbolFor,
+ SymbolToStringTag,
+ TypeError,
+} = primordials;
+
+// This should be set via setGlobalThis this is required so that if even
+// user deletes globalThis it is still usable
+let globalThis_;
+
+function saveGlobalThisReference(val) {
+ globalThis_ = val;
+}
+
+// accessors for non runtime visible data
+
+function getDispatched(event) {
+ return Boolean(event[_dispatched]);
+}
+
+function getPath(event) {
+ return event[_path] ?? [];
+}
+
+function getStopImmediatePropagation(event) {
+ return Boolean(event[_stopImmediatePropagationFlag]);
+}
+
+function setCurrentTarget(
+ event,
+ value,
+) {
+ event[_attributes].currentTarget = value;
+}
+
+function setIsTrusted(event, value) {
+ event[_isTrusted] = value;
+}
+
+function setDispatched(event, value) {
+ event[_dispatched] = value;
+}
+
+function setEventPhase(event, value) {
+ event[_attributes].eventPhase = value;
+}
+
+function setInPassiveListener(event, value) {
+ event[_inPassiveListener] = value;
+}
+
+function setPath(event, value) {
+ event[_path] = value;
+}
+
+function setRelatedTarget(
+ event,
+ value,
+) {
+ event[_attributes].relatedTarget = value;
+}
+
+function setTarget(event, value) {
+ event[_attributes].target = value;
+}
+
+function setStopImmediatePropagation(
+ event,
+ value,
+) {
+ event[_stopImmediatePropagationFlag] = value;
+}
+
+// Type guards that widen the event type
+
+function hasRelatedTarget(
+ event,
+) {
+ return ReflectHas(event, "relatedTarget");
+}
+
+const isTrusted = ObjectGetOwnPropertyDescriptor({
+ get isTrusted() {
+ return this[_isTrusted];
+ },
+}, "isTrusted").get;
+
+const eventInitConverter = webidl.createDictionaryConverter("EventInit", [{
+ key: "bubbles",
+ defaultValue: false,
+ converter: webidl.converters.boolean,
+}, {
+ key: "cancelable",
+ defaultValue: false,
+ converter: webidl.converters.boolean,
+}, {
+ key: "composed",
+ defaultValue: false,
+ converter: webidl.converters.boolean,
+}]);
+
+const _attributes = Symbol("[[attributes]]");
+const _canceledFlag = Symbol("[[canceledFlag]]");
+const _stopPropagationFlag = Symbol("[[stopPropagationFlag]]");
+const _stopImmediatePropagationFlag = Symbol(
+ "[[stopImmediatePropagationFlag]]",
+);
+const _inPassiveListener = Symbol("[[inPassiveListener]]");
+const _dispatched = Symbol("[[dispatched]]");
+const _isTrusted = Symbol("[[isTrusted]]");
+const _path = Symbol("[[path]]");
+// internal.
+const _skipInternalInit = Symbol("[[skipSlowInit]]");
+
+class Event {
+ constructor(type, eventInitDict = {}) {
+ // TODO(lucacasonato): remove when this interface is spec aligned
+ this[SymbolToStringTag] = "Event";
+ this[_canceledFlag] = false;
+ this[_stopPropagationFlag] = false;
+ this[_stopImmediatePropagationFlag] = false;
+ this[_inPassiveListener] = false;
+ this[_dispatched] = false;
+ this[_isTrusted] = false;
+ this[_path] = [];
+
+ if (!eventInitDict[_skipInternalInit]) {
+ webidl.requiredArguments(arguments.length, 1, {
+ prefix: "Failed to construct 'Event'",
+ });
+ type = webidl.converters.DOMString(type, {
+ prefix: "Failed to construct 'Event'",
+ context: "Argument 1",
+ });
+ const eventInit = eventInitConverter(eventInitDict, {
+ prefix: "Failed to construct 'Event'",
+ context: "Argument 2",
+ });
+ this[_attributes] = {
+ type,
+ ...eventInit,
+ currentTarget: null,
+ eventPhase: Event.NONE,
+ target: null,
+ timeStamp: DateNow(),
+ };
+ // [LegacyUnforgeable]
+ ReflectDefineProperty(this, "isTrusted", {
+ enumerable: true,
+ get: isTrusted,
+ });
+ } else {
+ this[_attributes] = {
+ type,
+ data: eventInitDict.data ?? null,
+ bubbles: eventInitDict.bubbles ?? false,
+ cancelable: eventInitDict.cancelable ?? false,
+ composed: eventInitDict.composed ?? false,
+ currentTarget: null,
+ eventPhase: Event.NONE,
+ target: null,
+ timeStamp: DateNow(),
+ };
+ // TODO(@littledivy): Not spec compliant but performance is hurt badly
+ // for users of `_skipInternalInit`.
+ this.isTrusted = false;
+ }
}
- function setInPassiveListener(event, value) {
- event[_inPassiveListener] = value;
+ [SymbolFor("Deno.privateCustomInspect")](inspect) {
+ return inspect(createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(Event.prototype, this),
+ keys: EVENT_PROPS,
+ }));
}
- function setPath(event, value) {
- event[_path] = value;
+ get type() {
+ return this[_attributes].type;
}
- function setRelatedTarget(
- event,
- value,
- ) {
- event[_attributes].relatedTarget = value;
+ get target() {
+ return this[_attributes].target;
}
- function setTarget(event, value) {
- event[_attributes].target = value;
+ get srcElement() {
+ return null;
}
- function setStopImmediatePropagation(
- event,
- value,
- ) {
- event[_stopImmediatePropagationFlag] = value;
+ set srcElement(_) {
+ // this member is deprecated
}
- // Type guards that widen the event type
-
- function hasRelatedTarget(
- event,
- ) {
- return ReflectHas(event, "relatedTarget");
+ get currentTarget() {
+ return this[_attributes].currentTarget;
}
- const isTrusted = ObjectGetOwnPropertyDescriptor({
- get isTrusted() {
- return this[_isTrusted];
- },
- }, "isTrusted").get;
-
- const eventInitConverter = webidl.createDictionaryConverter("EventInit", [{
- key: "bubbles",
- defaultValue: false,
- converter: webidl.converters.boolean,
- }, {
- key: "cancelable",
- defaultValue: false,
- converter: webidl.converters.boolean,
- }, {
- key: "composed",
- defaultValue: false,
- converter: webidl.converters.boolean,
- }]);
-
- const _attributes = Symbol("[[attributes]]");
- const _canceledFlag = Symbol("[[canceledFlag]]");
- const _stopPropagationFlag = Symbol("[[stopPropagationFlag]]");
- const _stopImmediatePropagationFlag = Symbol(
- "[[stopImmediatePropagationFlag]]",
- );
- const _inPassiveListener = Symbol("[[inPassiveListener]]");
- const _dispatched = Symbol("[[dispatched]]");
- const _isTrusted = Symbol("[[isTrusted]]");
- const _path = Symbol("[[path]]");
- // internal.
- const _skipInternalInit = Symbol("[[skipSlowInit]]");
-
- class Event {
- constructor(type, eventInitDict = {}) {
- // TODO(lucacasonato): remove when this interface is spec aligned
- this[SymbolToStringTag] = "Event";
- this[_canceledFlag] = false;
- this[_stopPropagationFlag] = false;
- this[_stopImmediatePropagationFlag] = false;
- this[_inPassiveListener] = false;
- this[_dispatched] = false;
- this[_isTrusted] = false;
- this[_path] = [];
-
- if (!eventInitDict[_skipInternalInit]) {
- webidl.requiredArguments(arguments.length, 1, {
- prefix: "Failed to construct 'Event'",
- });
- type = webidl.converters.DOMString(type, {
- prefix: "Failed to construct 'Event'",
- context: "Argument 1",
- });
- const eventInit = eventInitConverter(eventInitDict, {
- prefix: "Failed to construct 'Event'",
- context: "Argument 2",
- });
- this[_attributes] = {
- type,
- ...eventInit,
- currentTarget: null,
- eventPhase: Event.NONE,
- target: null,
- timeStamp: DateNow(),
- };
- // [LegacyUnforgeable]
- ReflectDefineProperty(this, "isTrusted", {
- enumerable: true,
- get: isTrusted,
- });
- } else {
- this[_attributes] = {
- type,
- data: eventInitDict.data ?? null,
- bubbles: eventInitDict.bubbles ?? false,
- cancelable: eventInitDict.cancelable ?? false,
- composed: eventInitDict.composed ?? false,
- currentTarget: null,
- eventPhase: Event.NONE,
- target: null,
- timeStamp: DateNow(),
- };
- // TODO(@littledivy): Not spec compliant but performance is hurt badly
- // for users of `_skipInternalInit`.
- this.isTrusted = false;
- }
+ composedPath() {
+ const path = this[_path];
+ if (path.length === 0) {
+ return [];
}
- [SymbolFor("Deno.privateCustomInspect")](inspect) {
- return inspect(consoleInternal.createFilteredInspectProxy({
- object: this,
- evaluate: ObjectPrototypeIsPrototypeOf(Event.prototype, this),
- keys: EVENT_PROPS,
- }));
+ if (!this.currentTarget) {
+ throw new Error("assertion error");
}
+ const composedPath = [
+ {
+ item: this.currentTarget,
+ itemInShadowTree: false,
+ relatedTarget: null,
+ rootOfClosedTree: false,
+ slotInClosedTree: false,
+ target: null,
+ touchTargetList: [],
+ },
+ ];
- get type() {
- return this[_attributes].type;
- }
+ let currentTargetIndex = 0;
+ let currentTargetHiddenSubtreeLevel = 0;
- get target() {
- return this[_attributes].target;
- }
+ for (let index = path.length - 1; index >= 0; index--) {
+ const { item, rootOfClosedTree, slotInClosedTree } = path[index];
- get srcElement() {
- return null;
- }
+ if (rootOfClosedTree) {
+ currentTargetHiddenSubtreeLevel++;
+ }
- set srcElement(_) {
- // this member is deprecated
- }
+ if (item === this.currentTarget) {
+ currentTargetIndex = index;
+ break;
+ }
- get currentTarget() {
- return this[_attributes].currentTarget;
+ if (slotInClosedTree) {
+ currentTargetHiddenSubtreeLevel--;
+ }
}
- composedPath() {
- const path = this[_path];
- if (path.length === 0) {
- return [];
- }
+ let currentHiddenLevel = currentTargetHiddenSubtreeLevel;
+ let maxHiddenLevel = currentTargetHiddenSubtreeLevel;
+
+ for (let i = currentTargetIndex - 1; i >= 0; i--) {
+ const { item, rootOfClosedTree, slotInClosedTree } = path[i];
- if (!this.currentTarget) {
- throw new Error("assertion error");
+ if (rootOfClosedTree) {
+ currentHiddenLevel++;
}
- const composedPath = [
- {
- item: this.currentTarget,
+
+ if (currentHiddenLevel <= maxHiddenLevel) {
+ ArrayPrototypeUnshift(composedPath, {
+ item,
itemInShadowTree: false,
relatedTarget: null,
rootOfClosedTree: false,
slotInClosedTree: false,
target: null,
touchTargetList: [],
- },
- ];
-
- let currentTargetIndex = 0;
- let currentTargetHiddenSubtreeLevel = 0;
-
- for (let index = path.length - 1; index >= 0; index--) {
- const { item, rootOfClosedTree, slotInClosedTree } = path[index];
-
- if (rootOfClosedTree) {
- currentTargetHiddenSubtreeLevel++;
- }
-
- if (item === this.currentTarget) {
- currentTargetIndex = index;
- break;
- }
-
- if (slotInClosedTree) {
- currentTargetHiddenSubtreeLevel--;
- }
+ });
}
- let currentHiddenLevel = currentTargetHiddenSubtreeLevel;
- let maxHiddenLevel = currentTargetHiddenSubtreeLevel;
-
- for (let i = currentTargetIndex - 1; i >= 0; i--) {
- const { item, rootOfClosedTree, slotInClosedTree } = path[i];
-
- if (rootOfClosedTree) {
- currentHiddenLevel++;
- }
-
- if (currentHiddenLevel <= maxHiddenLevel) {
- ArrayPrototypeUnshift(composedPath, {
- item,
- itemInShadowTree: false,
- relatedTarget: null,
- rootOfClosedTree: false,
- slotInClosedTree: false,
- target: null,
- touchTargetList: [],
- });
- }
-
- if (slotInClosedTree) {
- currentHiddenLevel--;
+ if (slotInClosedTree) {
+ currentHiddenLevel--;
- if (currentHiddenLevel < maxHiddenLevel) {
- maxHiddenLevel = currentHiddenLevel;
- }
+ if (currentHiddenLevel < maxHiddenLevel) {
+ maxHiddenLevel = currentHiddenLevel;
}
}
+ }
- currentHiddenLevel = currentTargetHiddenSubtreeLevel;
- maxHiddenLevel = currentTargetHiddenSubtreeLevel;
+ currentHiddenLevel = currentTargetHiddenSubtreeLevel;
+ maxHiddenLevel = currentTargetHiddenSubtreeLevel;
- for (let index = currentTargetIndex + 1; index < path.length; index++) {
- const { item, rootOfClosedTree, slotInClosedTree } = path[index];
+ for (let index = currentTargetIndex + 1; index < path.length; index++) {
+ const { item, rootOfClosedTree, slotInClosedTree } = path[index];
- if (slotInClosedTree) {
- currentHiddenLevel++;
- }
+ if (slotInClosedTree) {
+ currentHiddenLevel++;
+ }
- if (currentHiddenLevel <= maxHiddenLevel) {
- ArrayPrototypePush(composedPath, {
- item,
- itemInShadowTree: false,
- relatedTarget: null,
- rootOfClosedTree: false,
- slotInClosedTree: false,
- target: null,
- touchTargetList: [],
- });
- }
+ if (currentHiddenLevel <= maxHiddenLevel) {
+ ArrayPrototypePush(composedPath, {
+ item,
+ itemInShadowTree: false,
+ relatedTarget: null,
+ rootOfClosedTree: false,
+ slotInClosedTree: false,
+ target: null,
+ touchTargetList: [],
+ });
+ }
- if (rootOfClosedTree) {
- currentHiddenLevel--;
+ if (rootOfClosedTree) {
+ currentHiddenLevel--;
- if (currentHiddenLevel < maxHiddenLevel) {
- maxHiddenLevel = currentHiddenLevel;
- }
+ if (currentHiddenLevel < maxHiddenLevel) {
+ maxHiddenLevel = currentHiddenLevel;
}
}
- return ArrayPrototypeMap(composedPath, (p) => p.item);
- }
-
- get NONE() {
- return Event.NONE;
- }
-
- get CAPTURING_PHASE() {
- return Event.CAPTURING_PHASE;
- }
-
- get AT_TARGET() {
- return Event.AT_TARGET;
- }
-
- get BUBBLING_PHASE() {
- return Event.BUBBLING_PHASE;
- }
-
- static get NONE() {
- return 0;
}
+ return ArrayPrototypeMap(composedPath, (p) => p.item);
+ }
- static get CAPTURING_PHASE() {
- return 1;
- }
+ get NONE() {
+ return Event.NONE;
+ }
- static get AT_TARGET() {
- return 2;
- }
+ get CAPTURING_PHASE() {
+ return Event.CAPTURING_PHASE;
+ }
- static get BUBBLING_PHASE() {
- return 3;
- }
+ get AT_TARGET() {
+ return Event.AT_TARGET;
+ }
- get eventPhase() {
- return this[_attributes].eventPhase;
- }
+ get BUBBLING_PHASE() {
+ return Event.BUBBLING_PHASE;
+ }
- stopPropagation() {
- this[_stopPropagationFlag] = true;
- }
+ static get NONE() {
+ return 0;
+ }
- get cancelBubble() {
- return this[_stopPropagationFlag];
- }
+ static get CAPTURING_PHASE() {
+ return 1;
+ }
- set cancelBubble(value) {
- this[_stopPropagationFlag] = webidl.converters.boolean(value);
- }
+ static get AT_TARGET() {
+ return 2;
+ }
- stopImmediatePropagation() {
- this[_stopPropagationFlag] = true;
- this[_stopImmediatePropagationFlag] = true;
- }
+ static get BUBBLING_PHASE() {
+ return 3;
+ }
- get bubbles() {
- return this[_attributes].bubbles;
- }
+ get eventPhase() {
+ return this[_attributes].eventPhase;
+ }
- get cancelable() {
- return this[_attributes].cancelable;
- }
+ stopPropagation() {
+ this[_stopPropagationFlag] = true;
+ }
- get returnValue() {
- return !this[_canceledFlag];
- }
+ get cancelBubble() {
+ return this[_stopPropagationFlag];
+ }
- set returnValue(value) {
- if (!webidl.converters.boolean(value)) {
- this[_canceledFlag] = true;
- }
- }
+ set cancelBubble(value) {
+ this[_stopPropagationFlag] = webidl.converters.boolean(value);
+ }
- preventDefault() {
- if (this[_attributes].cancelable && !this[_inPassiveListener]) {
- this[_canceledFlag] = true;
- }
- }
+ stopImmediatePropagation() {
+ this[_stopPropagationFlag] = true;
+ this[_stopImmediatePropagationFlag] = true;
+ }
- get defaultPrevented() {
- return this[_canceledFlag];
- }
+ get bubbles() {
+ return this[_attributes].bubbles;
+ }
- get composed() {
- return this[_attributes].composed;
- }
+ get cancelable() {
+ return this[_attributes].cancelable;
+ }
- get initialized() {
- return true;
- }
+ get returnValue() {
+ return !this[_canceledFlag];
+ }
- get timeStamp() {
- return this[_attributes].timeStamp;
+ set returnValue(value) {
+ if (!webidl.converters.boolean(value)) {
+ this[_canceledFlag] = true;
}
}
- function defineEnumerableProps(
- Ctor,
- props,
- ) {
- for (let i = 0; i < props.length; ++i) {
- const prop = props[i];
- ReflectDefineProperty(Ctor.prototype, prop, { enumerable: true });
+ preventDefault() {
+ if (this[_attributes].cancelable && !this[_inPassiveListener]) {
+ this[_canceledFlag] = true;
}
}
- const EVENT_PROPS = [
- "bubbles",
- "cancelable",
- "composed",
- "currentTarget",
- "defaultPrevented",
- "eventPhase",
- "srcElement",
- "target",
- "returnValue",
- "timeStamp",
- "type",
- ];
-
- defineEnumerableProps(Event, EVENT_PROPS);
-
- // This is currently the only node type we are using, so instead of implementing
- // the whole of the Node interface at the moment, this just gives us the one
- // value to power the standards based logic
- const DOCUMENT_FRAGMENT_NODE = 11;
-
- // DOM Logic Helper functions and type guards
-
- /** Get the parent node, for event targets that have a parent.
- *
- * Ref: https://dom.spec.whatwg.org/#get-the-parent */
- function getParent(eventTarget) {
- return isNode(eventTarget) ? eventTarget.parentNode : null;
+ get defaultPrevented() {
+ return this[_canceledFlag];
}
- function getRoot(eventTarget) {
- return isNode(eventTarget)
- ? eventTarget.getRootNode({ composed: true })
- : null;
+ get composed() {
+ return this[_attributes].composed;
}
- function isNode(
- eventTarget,
- ) {
- return Boolean(eventTarget && ReflectHas(eventTarget, "nodeType"));
+ get initialized() {
+ return true;
}
- // https://dom.spec.whatwg.org/#concept-shadow-including-inclusive-ancestor
- function isShadowInclusiveAncestor(
- ancestor,
- node,
- ) {
- while (isNode(node)) {
- if (node === ancestor) {
- return true;
- }
-
- if (isShadowRoot(node)) {
- node = node && getHost(node);
- } else {
- node = getParent(node);
- }
- }
-
- return false;
+ get timeStamp() {
+ return this[_attributes].timeStamp;
}
-
- function isShadowRoot(nodeImpl) {
- return Boolean(
- nodeImpl &&
- isNode(nodeImpl) &&
- nodeImpl.nodeType === DOCUMENT_FRAGMENT_NODE &&
- getHost(nodeImpl) != null,
- );
+}
+
+function defineEnumerableProps(
+ Ctor,
+ props,
+) {
+ for (let i = 0; i < props.length; ++i) {
+ const prop = props[i];
+ ReflectDefineProperty(Ctor.prototype, prop, { enumerable: true });
}
+}
+
+const EVENT_PROPS = [
+ "bubbles",
+ "cancelable",
+ "composed",
+ "currentTarget",
+ "defaultPrevented",
+ "eventPhase",
+ "srcElement",
+ "target",
+ "returnValue",
+ "timeStamp",
+ "type",
+];
+
+defineEnumerableProps(Event, EVENT_PROPS);
+
+// This is currently the only node type we are using, so instead of implementing
+// the whole of the Node interface at the moment, this just gives us the one
+// value to power the standards based logic
+const DOCUMENT_FRAGMENT_NODE = 11;
+
+// DOM Logic Helper functions and type guards
+
+/** Get the parent node, for event targets that have a parent.
+ *
+ * Ref: https://dom.spec.whatwg.org/#get-the-parent */
+function getParent(eventTarget) {
+ return isNode(eventTarget) ? eventTarget.parentNode : null;
+}
+
+function getRoot(eventTarget) {
+ return isNode(eventTarget)
+ ? eventTarget.getRootNode({ composed: true })
+ : null;
+}
+
+function isNode(
+ eventTarget,
+) {
+ return Boolean(eventTarget && ReflectHas(eventTarget, "nodeType"));
+}
+
+// https://dom.spec.whatwg.org/#concept-shadow-including-inclusive-ancestor
+function isShadowInclusiveAncestor(
+ ancestor,
+ node,
+) {
+ while (isNode(node)) {
+ if (node === ancestor) {
+ return true;
+ }
- function isSlotable(
- nodeImpl,
- ) {
- return Boolean(isNode(nodeImpl) && ReflectHas(nodeImpl, "assignedSlot"));
+ if (isShadowRoot(node)) {
+ node = node && getHost(node);
+ } else {
+ node = getParent(node);
+ }
}
- // DOM Logic functions
+ return false;
+}
- /** Append a path item to an event's path.
- *
- * Ref: https://dom.spec.whatwg.org/#concept-event-path-append
- */
- function appendToEventPath(
- eventImpl,
- target,
- targetOverride,
+function isShadowRoot(nodeImpl) {
+ return Boolean(
+ nodeImpl &&
+ isNode(nodeImpl) &&
+ nodeImpl.nodeType === DOCUMENT_FRAGMENT_NODE &&
+ getHost(nodeImpl) != null,
+ );
+}
+
+function isSlotable(
+ nodeImpl,
+) {
+ return Boolean(isNode(nodeImpl) && ReflectHas(nodeImpl, "assignedSlot"));
+}
+
+// DOM Logic functions
+
+/** Append a path item to an event's path.
+ *
+ * Ref: https://dom.spec.whatwg.org/#concept-event-path-append
+ */
+function appendToEventPath(
+ eventImpl,
+ target,
+ targetOverride,
+ relatedTarget,
+ touchTargets,
+ slotInClosedTree,
+) {
+ const itemInShadowTree = isNode(target) && isShadowRoot(getRoot(target));
+ const rootOfClosedTree = isShadowRoot(target) &&
+ getMode(target) === "closed";
+
+ ArrayPrototypePush(getPath(eventImpl), {
+ item: target,
+ itemInShadowTree,
+ target: targetOverride,
relatedTarget,
- touchTargets,
+ touchTargetList: touchTargets,
+ rootOfClosedTree,
slotInClosedTree,
- ) {
- const itemInShadowTree = isNode(target) && isShadowRoot(getRoot(target));
- const rootOfClosedTree = isShadowRoot(target) &&
- getMode(target) === "closed";
-
- ArrayPrototypePush(getPath(eventImpl), {
- item: target,
- itemInShadowTree,
- target: targetOverride,
+ });
+}
+
+function dispatch(
+ targetImpl,
+ eventImpl,
+ targetOverride,
+) {
+ let clearTargets = false;
+ let activationTarget = null;
+
+ setDispatched(eventImpl, true);
+
+ targetOverride = targetOverride ?? targetImpl;
+ const eventRelatedTarget = hasRelatedTarget(eventImpl)
+ ? eventImpl.relatedTarget
+ : null;
+ let relatedTarget = retarget(eventRelatedTarget, targetImpl);
+
+ if (targetImpl !== relatedTarget || targetImpl === eventRelatedTarget) {
+ const touchTargets = [];
+
+ appendToEventPath(
+ eventImpl,
+ targetImpl,
+ targetOverride,
relatedTarget,
- touchTargetList: touchTargets,
- rootOfClosedTree,
- slotInClosedTree,
- });
- }
+ touchTargets,
+ false,
+ );
- function dispatch(
- targetImpl,
- eventImpl,
- targetOverride,
- ) {
- let clearTargets = false;
- let activationTarget = null;
+ const isActivationEvent = eventImpl.type === "click";
- setDispatched(eventImpl, true);
+ if (isActivationEvent && getHasActivationBehavior(targetImpl)) {
+ activationTarget = targetImpl;
+ }
- targetOverride = targetOverride ?? targetImpl;
- const eventRelatedTarget = hasRelatedTarget(eventImpl)
- ? eventImpl.relatedTarget
+ let slotInClosedTree = false;
+ let slotable = isSlotable(targetImpl) && getAssignedSlot(targetImpl)
+ ? targetImpl
: null;
- let relatedTarget = retarget(eventRelatedTarget, targetImpl);
-
- if (targetImpl !== relatedTarget || targetImpl === eventRelatedTarget) {
- const touchTargets = [];
-
- appendToEventPath(
- eventImpl,
- targetImpl,
- targetOverride,
- relatedTarget,
- touchTargets,
- false,
- );
-
- const isActivationEvent = eventImpl.type === "click";
+ let parent = getParent(targetImpl);
- if (isActivationEvent && getHasActivationBehavior(targetImpl)) {
- activationTarget = targetImpl;
- }
-
- let slotInClosedTree = false;
- let slotable = isSlotable(targetImpl) && getAssignedSlot(targetImpl)
- ? targetImpl
- : null;
- let parent = getParent(targetImpl);
-
- // Populate event path
- // https://dom.spec.whatwg.org/#event-path
- while (parent !== null) {
- if (slotable !== null) {
- slotable = null;
-
- const parentRoot = getRoot(parent);
- if (
- isShadowRoot(parentRoot) &&
- parentRoot &&
- getMode(parentRoot) === "closed"
- ) {
- slotInClosedTree = true;
- }
- }
-
- relatedTarget = retarget(eventRelatedTarget, parent);
+ // Populate event path
+ // https://dom.spec.whatwg.org/#event-path
+ while (parent !== null) {
+ if (slotable !== null) {
+ slotable = null;
+ const parentRoot = getRoot(parent);
if (
- isNode(parent) &&
- isShadowInclusiveAncestor(getRoot(targetImpl), parent)
+ isShadowRoot(parentRoot) &&
+ parentRoot &&
+ getMode(parentRoot) === "closed"
) {
- appendToEventPath(
- eventImpl,
- parent,
- null,
- relatedTarget,
- touchTargets,
- slotInClosedTree,
- );
- } else if (parent === relatedTarget) {
- parent = null;
- } else {
- targetImpl = parent;
-
- if (
- isActivationEvent &&
- activationTarget === null &&
- getHasActivationBehavior(targetImpl)
- ) {
- activationTarget = targetImpl;
- }
-
- appendToEventPath(
- eventImpl,
- parent,
- targetImpl,
- relatedTarget,
- touchTargets,
- slotInClosedTree,
- );
- }
-
- if (parent !== null) {
- parent = getParent(parent);
- }
-
- slotInClosedTree = false;
- }
-
- let clearTargetsTupleIndex = -1;
- const path = getPath(eventImpl);
- for (
- let i = path.length - 1;
- i >= 0 && clearTargetsTupleIndex === -1;
- i--
- ) {
- if (path[i].target !== null) {
- clearTargetsTupleIndex = i;
- }
- }
- const clearTargetsTuple = path[clearTargetsTupleIndex];
-
- clearTargets = (isNode(clearTargetsTuple.target) &&
- isShadowRoot(getRoot(clearTargetsTuple.target))) ||
- (isNode(clearTargetsTuple.relatedTarget) &&
- isShadowRoot(getRoot(clearTargetsTuple.relatedTarget)));
-
- setEventPhase(eventImpl, Event.CAPTURING_PHASE);
-
- for (let i = path.length - 1; i >= 0; --i) {
- const tuple = path[i];
-
- if (tuple.target === null) {
- invokeEventListeners(tuple, eventImpl);
+ slotInClosedTree = true;
}
}
- for (let i = 0; i < path.length; i++) {
- const tuple = path[i];
+ relatedTarget = retarget(eventRelatedTarget, parent);
- if (tuple.target !== null) {
- setEventPhase(eventImpl, Event.AT_TARGET);
- } else {
- setEventPhase(eventImpl, Event.BUBBLING_PHASE);
- }
+ if (
+ isNode(parent) &&
+ isShadowInclusiveAncestor(getRoot(targetImpl), parent)
+ ) {
+ appendToEventPath(
+ eventImpl,
+ parent,
+ null,
+ relatedTarget,
+ touchTargets,
+ slotInClosedTree,
+ );
+ } else if (parent === relatedTarget) {
+ parent = null;
+ } else {
+ targetImpl = parent;
if (
- (eventImpl.eventPhase === Event.BUBBLING_PHASE &&
- eventImpl.bubbles) ||
- eventImpl.eventPhase === Event.AT_TARGET
+ isActivationEvent &&
+ activationTarget === null &&
+ getHasActivationBehavior(targetImpl)
) {
- invokeEventListeners(tuple, eventImpl);
+ activationTarget = targetImpl;
}
+
+ appendToEventPath(
+ eventImpl,
+ parent,
+ targetImpl,
+ relatedTarget,
+ touchTargets,
+ slotInClosedTree,
+ );
}
- }
- setEventPhase(eventImpl, Event.NONE);
- setCurrentTarget(eventImpl, null);
- setPath(eventImpl, []);
- setDispatched(eventImpl, false);
- eventImpl.cancelBubble = false;
- setStopImmediatePropagation(eventImpl, false);
+ if (parent !== null) {
+ parent = getParent(parent);
+ }
- if (clearTargets) {
- setTarget(eventImpl, null);
- setRelatedTarget(eventImpl, null);
+ slotInClosedTree = false;
}
- // TODO(bartlomieju): invoke activation targets if HTML nodes will be implemented
- // if (activationTarget !== null) {
- // if (!eventImpl.defaultPrevented) {
- // activationTarget._activationBehavior();
- // }
- // }
+ let clearTargetsTupleIndex = -1;
+ const path = getPath(eventImpl);
+ for (
+ let i = path.length - 1;
+ i >= 0 && clearTargetsTupleIndex === -1;
+ i--
+ ) {
+ if (path[i].target !== null) {
+ clearTargetsTupleIndex = i;
+ }
+ }
+ const clearTargetsTuple = path[clearTargetsTupleIndex];
- return !eventImpl.defaultPrevented;
- }
+ clearTargets = (isNode(clearTargetsTuple.target) &&
+ isShadowRoot(getRoot(clearTargetsTuple.target))) ||
+ (isNode(clearTargetsTuple.relatedTarget) &&
+ isShadowRoot(getRoot(clearTargetsTuple.relatedTarget)));
- /** Inner invoking of the event listeners where the resolved listeners are
- * called.
- *
- * Ref: https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke */
- function innerInvokeEventListeners(
- eventImpl,
- targetListeners,
- ) {
- let found = false;
+ setEventPhase(eventImpl, Event.CAPTURING_PHASE);
- const { type } = eventImpl;
+ for (let i = path.length - 1; i >= 0; --i) {
+ const tuple = path[i];
- if (!targetListeners || !targetListeners[type]) {
- return found;
+ if (tuple.target === null) {
+ invokeEventListeners(tuple, eventImpl);
+ }
}
- // Copy event listeners before iterating since the list can be modified during the iteration.
- const handlers = ArrayPrototypeSlice(targetListeners[type]);
+ for (let i = 0; i < path.length; i++) {
+ const tuple = path[i];
- for (let i = 0; i < handlers.length; i++) {
- const listener = handlers[i];
-
- let capture, once, passive;
- if (typeof listener.options === "boolean") {
- capture = listener.options;
- once = false;
- passive = false;
+ if (tuple.target !== null) {
+ setEventPhase(eventImpl, Event.AT_TARGET);
} else {
- capture = listener.options.capture;
- once = listener.options.once;
- passive = listener.options.passive;
- }
-
- // Check if the event listener has been removed since the listeners has been cloned.
- if (!ArrayPrototypeIncludes(targetListeners[type], listener)) {
- continue;
+ setEventPhase(eventImpl, Event.BUBBLING_PHASE);
}
- found = true;
-
if (
- (eventImpl.eventPhase === Event.CAPTURING_PHASE && !capture) ||
- (eventImpl.eventPhase === Event.BUBBLING_PHASE && capture)
+ (eventImpl.eventPhase === Event.BUBBLING_PHASE &&
+ eventImpl.bubbles) ||
+ eventImpl.eventPhase === Event.AT_TARGET
) {
- continue;
- }
-
- if (once) {
- ArrayPrototypeSplice(
- targetListeners[type],
- ArrayPrototypeIndexOf(targetListeners[type], listener),
- 1,
- );
- }
-
- if (passive) {
- setInPassiveListener(eventImpl, true);
- }
-
- if (typeof listener.callback === "object") {
- if (typeof listener.callback.handleEvent === "function") {
- listener.callback.handleEvent(eventImpl);
- }
- } else {
- FunctionPrototypeCall(
- listener.callback,
- eventImpl.currentTarget,
- eventImpl,
- );
+ invokeEventListeners(tuple, eventImpl);
}
+ }
+ }
- setInPassiveListener(eventImpl, false);
+ setEventPhase(eventImpl, Event.NONE);
+ setCurrentTarget(eventImpl, null);
+ setPath(eventImpl, []);
+ setDispatched(eventImpl, false);
+ eventImpl.cancelBubble = false;
+ setStopImmediatePropagation(eventImpl, false);
- if (getStopImmediatePropagation(eventImpl)) {
- return found;
- }
- }
+ if (clearTargets) {
+ setTarget(eventImpl, null);
+ setRelatedTarget(eventImpl, null);
+ }
+ // TODO(bartlomieju): invoke activation targets if HTML nodes will be implemented
+ // if (activationTarget !== null) {
+ // if (!eventImpl.defaultPrevented) {
+ // activationTarget._activationBehavior();
+ // }
+ // }
+
+ return !eventImpl.defaultPrevented;
+}
+
+/** Inner invoking of the event listeners where the resolved listeners are
+ * called.
+ *
+ * Ref: https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke */
+function innerInvokeEventListeners(
+ eventImpl,
+ targetListeners,
+) {
+ let found = false;
+
+ const { type } = eventImpl;
+
+ if (!targetListeners || !targetListeners[type]) {
return found;
}
- /** Invokes the listeners on a given event path with the supplied event.
- *
- * Ref: https://dom.spec.whatwg.org/#concept-event-listener-invoke */
- function invokeEventListeners(tuple, eventImpl) {
- const path = getPath(eventImpl);
- const tupleIndex = ArrayPrototypeIndexOf(path, tuple);
- for (let i = tupleIndex; i >= 0; i--) {
- const t = path[i];
- if (t.target) {
- setTarget(eventImpl, t.target);
- break;
- }
- }
+ // Copy event listeners before iterating since the list can be modified during the iteration.
+ const handlers = ArrayPrototypeSlice(targetListeners[type]);
- setRelatedTarget(eventImpl, tuple.relatedTarget);
+ for (let i = 0; i < handlers.length; i++) {
+ const listener = handlers[i];
- if (eventImpl.cancelBubble) {
- return;
+ let capture, once, passive;
+ if (typeof listener.options === "boolean") {
+ capture = listener.options;
+ once = false;
+ passive = false;
+ } else {
+ capture = listener.options.capture;
+ once = listener.options.once;
+ passive = listener.options.passive;
}
- setCurrentTarget(eventImpl, tuple.item);
-
- try {
- innerInvokeEventListeners(eventImpl, getListeners(tuple.item));
- } catch (error) {
- reportException(error);
+ // Check if the event listener has been removed since the listeners has been cloned.
+ if (!ArrayPrototypeIncludes(targetListeners[type], listener)) {
+ continue;
}
- }
- function normalizeEventHandlerOptions(
- options,
- ) {
- if (typeof options === "boolean" || typeof options === "undefined") {
- return {
- capture: Boolean(options),
- };
- } else {
- return options;
- }
- }
+ found = true;
- /** Retarget the target following the spec logic.
- *
- * Ref: https://dom.spec.whatwg.org/#retarget */
- function retarget(a, b) {
- while (true) {
- if (!isNode(a)) {
- return a;
- }
+ if (
+ (eventImpl.eventPhase === Event.CAPTURING_PHASE && !capture) ||
+ (eventImpl.eventPhase === Event.BUBBLING_PHASE && capture)
+ ) {
+ continue;
+ }
- const aRoot = a.getRootNode();
+ if (once) {
+ ArrayPrototypeSplice(
+ targetListeners[type],
+ ArrayPrototypeIndexOf(targetListeners[type], listener),
+ 1,
+ );
+ }
- if (aRoot) {
- if (
- !isShadowRoot(aRoot) ||
- (isNode(b) && isShadowInclusiveAncestor(aRoot, b))
- ) {
- return a;
- }
+ if (passive) {
+ setInPassiveListener(eventImpl, true);
+ }
- a = getHost(aRoot);
+ if (typeof listener.callback === "object") {
+ if (typeof listener.callback.handleEvent === "function") {
+ listener.callback.handleEvent(eventImpl);
}
+ } else {
+ FunctionPrototypeCall(
+ listener.callback,
+ eventImpl.currentTarget,
+ eventImpl,
+ );
}
- }
- // Accessors for non-public data
+ setInPassiveListener(eventImpl, false);
- const eventTargetData = Symbol();
-
- function setEventTargetData(target) {
- target[eventTargetData] = getDefaultTargetData();
- }
-
- function getAssignedSlot(target) {
- return Boolean(target?.[eventTargetData]?.assignedSlot);
+ if (getStopImmediatePropagation(eventImpl)) {
+ return found;
+ }
}
- function getHasActivationBehavior(target) {
- return Boolean(target?.[eventTargetData]?.hasActivationBehavior);
+ return found;
+}
+
+/** Invokes the listeners on a given event path with the supplied event.
+ *
+ * Ref: https://dom.spec.whatwg.org/#concept-event-listener-invoke */
+function invokeEventListeners(tuple, eventImpl) {
+ const path = getPath(eventImpl);
+ const tupleIndex = ArrayPrototypeIndexOf(path, tuple);
+ for (let i = tupleIndex; i >= 0; i--) {
+ const t = path[i];
+ if (t.target) {
+ setTarget(eventImpl, t.target);
+ break;
+ }
}
- function getHost(target) {
- return target?.[eventTargetData]?.host ?? null;
- }
+ setRelatedTarget(eventImpl, tuple.relatedTarget);
- function getListeners(target) {
- return target?.[eventTargetData]?.listeners ?? {};
+ if (eventImpl.cancelBubble) {
+ return;
}
- function getMode(target) {
- return target?.[eventTargetData]?.mode ?? null;
- }
+ setCurrentTarget(eventImpl, tuple.item);
- function listenerCount(target, type) {
- return getListeners(target)?.[type]?.length ?? 0;
+ try {
+ innerInvokeEventListeners(eventImpl, getListeners(tuple.item));
+ } catch (error) {
+ reportException(error);
}
+}
- function getDefaultTargetData() {
+function normalizeEventHandlerOptions(
+ options,
+) {
+ if (typeof options === "boolean" || typeof options === "undefined") {
return {
- assignedSlot: false,
- hasActivationBehavior: false,
- host: null,
- listeners: ObjectCreate(null),
- mode: "",
+ capture: Boolean(options),
};
+ } else {
+ return options;
}
+}
- // This is lazy loaded because there is a circular dependency with AbortSignal.
- let addEventListenerOptionsConverter;
-
- function lazyAddEventListenerOptionsConverter() {
- addEventListenerOptionsConverter ??= webidl.createDictionaryConverter(
- "AddEventListenerOptions",
- [
- {
- key: "capture",
- defaultValue: false,
- converter: webidl.converters.boolean,
- },
- {
- key: "passive",
- defaultValue: false,
- converter: webidl.converters.boolean,
- },
- {
- key: "once",
- defaultValue: false,
- converter: webidl.converters.boolean,
- },
- {
- key: "signal",
- converter: webidl.converters.AbortSignal,
- },
- ],
- );
- }
-
- webidl.converters.AddEventListenerOptions = (V, opts) => {
- if (webidl.type(V) !== "Object" || V === null) {
- V = { capture: Boolean(V) };
- }
-
- lazyAddEventListenerOptionsConverter();
- return addEventListenerOptionsConverter(V, opts);
- };
-
- class EventTarget {
- constructor() {
- this[eventTargetData] = getDefaultTargetData();
- this[webidl.brand] = webidl.brand;
+/** Retarget the target following the spec logic.
+ *
+ * Ref: https://dom.spec.whatwg.org/#retarget */
+function retarget(a, b) {
+ while (true) {
+ if (!isNode(a)) {
+ return a;
}
- addEventListener(
- type,
- callback,
- options,
- ) {
- const self = this ?? globalThis;
- webidl.assertBranded(self, EventTargetPrototype);
- const prefix = "Failed to execute 'addEventListener' on 'EventTarget'";
+ const aRoot = a.getRootNode();
- webidl.requiredArguments(arguments.length, 2, {
- prefix,
- });
+ if (aRoot) {
+ if (
+ !isShadowRoot(aRoot) ||
+ (isNode(b) && isShadowInclusiveAncestor(aRoot, b))
+ ) {
+ return a;
+ }
- options = webidl.converters.AddEventListenerOptions(options, {
- prefix,
- context: "Argument 3",
- });
+ a = getHost(aRoot);
+ }
+ }
+}
- if (callback === null) {
- return;
- }
+// Accessors for non-public data
- const { listeners } = self[eventTargetData];
+const eventTargetData = Symbol();
- if (!(ReflectHas(listeners, type))) {
- listeners[type] = [];
- }
+function setEventTargetData(target) {
+ target[eventTargetData] = getDefaultTargetData();
+}
- const listenerList = listeners[type];
- for (let i = 0; i < listenerList.length; ++i) {
- const listener = listenerList[i];
- if (
- ((typeof listener.options === "boolean" &&
- listener.options === options.capture) ||
- (typeof listener.options === "object" &&
- listener.options.capture === options.capture)) &&
- listener.callback === callback
- ) {
- return;
- }
- }
- if (options?.signal) {
- const signal = options?.signal;
- if (signal.aborted) {
- // If signal is not null and its aborted flag is set, then return.
- return;
- } else {
- // If listener’s signal is not null, then add the following abort
- // abort steps to it: Remove an event listener.
- signal.addEventListener("abort", () => {
- self.removeEventListener(type, callback, options);
- });
- }
- }
+function getAssignedSlot(target) {
+ return Boolean(target?.[eventTargetData]?.assignedSlot);
+}
- ArrayPrototypePush(listeners[type], { callback, options });
- }
+function getHasActivationBehavior(target) {
+ return Boolean(target?.[eventTargetData]?.hasActivationBehavior);
+}
- removeEventListener(
- type,
- callback,
- options,
- ) {
- const self = this ?? globalThis;
- webidl.assertBranded(self, EventTargetPrototype);
- webidl.requiredArguments(arguments.length, 2, {
- prefix: "Failed to execute 'removeEventListener' on 'EventTarget'",
- });
+function getHost(target) {
+ return target?.[eventTargetData]?.host ?? null;
+}
- const { listeners } = self[eventTargetData];
- if (callback !== null && ReflectHas(listeners, type)) {
- listeners[type] = ArrayPrototypeFilter(
- listeners[type],
- (listener) => listener.callback !== callback,
- );
- } else if (callback === null || !listeners[type]) {
- return;
- }
+function getListeners(target) {
+ return target?.[eventTargetData]?.listeners ?? {};
+}
- options = normalizeEventHandlerOptions(options);
+function getMode(target) {
+ return target?.[eventTargetData]?.mode ?? null;
+}
- for (let i = 0; i < listeners[type].length; ++i) {
- const listener = listeners[type][i];
- if (
- ((typeof listener.options === "boolean" &&
- listener.options === options.capture) ||
- (typeof listener.options === "object" &&
- listener.options.capture === options.capture)) &&
- listener.callback === callback
- ) {
- ArrayPrototypeSplice(listeners[type], i, 1);
- break;
- }
- }
- }
+function listenerCount(target, type) {
+ return getListeners(target)?.[type]?.length ?? 0;
+}
- dispatchEvent(event) {
- // If `this` is not present, then fallback to global scope. We don't use
- // `globalThis` directly here, because it could be deleted by user.
- // Instead use saved reference to global scope when the script was
- // executed.
- const self = this ?? window;
- webidl.assertBranded(self, EventTargetPrototype);
- webidl.requiredArguments(arguments.length, 1, {
- prefix: "Failed to execute 'dispatchEvent' on 'EventTarget'",
- });
+function getDefaultTargetData() {
+ return {
+ assignedSlot: false,
+ hasActivationBehavior: false,
+ host: null,
+ listeners: ObjectCreate(null),
+ mode: "",
+ };
+}
- const { listeners } = self[eventTargetData];
- if (!ReflectHas(listeners, event.type)) {
- setTarget(event, this);
- return true;
- }
+// This is lazy loaded because there is a circular dependency with AbortSignal.
+let addEventListenerOptionsConverter;
- if (getDispatched(event)) {
- throw new DOMException("Invalid event state.", "InvalidStateError");
- }
+function lazyAddEventListenerOptionsConverter() {
+ addEventListenerOptionsConverter ??= webidl.createDictionaryConverter(
+ "AddEventListenerOptions",
+ [
+ {
+ key: "capture",
+ defaultValue: false,
+ converter: webidl.converters.boolean,
+ },
+ {
+ key: "passive",
+ defaultValue: false,
+ converter: webidl.converters.boolean,
+ },
+ {
+ key: "once",
+ defaultValue: false,
+ converter: webidl.converters.boolean,
+ },
+ {
+ key: "signal",
+ converter: webidl.converters.AbortSignal,
+ },
+ ],
+ );
+}
- if (event.eventPhase !== Event.NONE) {
- throw new DOMException("Invalid event state.", "InvalidStateError");
- }
+webidl.converters.AddEventListenerOptions = (V, opts) => {
+ if (webidl.type(V) !== "Object" || V === null) {
+ V = { capture: Boolean(V) };
+ }
- return dispatch(self, event);
- }
+ lazyAddEventListenerOptionsConverter();
+ return addEventListenerOptionsConverter(V, opts);
+};
- getParent(_event) {
- return null;
- }
+class EventTarget {
+ constructor() {
+ this[eventTargetData] = getDefaultTargetData();
+ this[webidl.brand] = webidl.brand;
}
- webidl.configurePrototype(EventTarget);
- const EventTargetPrototype = EventTarget.prototype;
+ addEventListener(
+ type,
+ callback,
+ options,
+ ) {
+ const self = this ?? globalThis_;
+ webidl.assertBranded(self, EventTargetPrototype);
+ const prefix = "Failed to execute 'addEventListener' on 'EventTarget'";
- defineEnumerableProps(EventTarget, [
- "addEventListener",
- "removeEventListener",
- "dispatchEvent",
- ]);
+ webidl.requiredArguments(arguments.length, 2, {
+ prefix,
+ });
- class ErrorEvent extends Event {
- #message = "";
- #filename = "";
- #lineno = "";
- #colno = "";
- #error = "";
+ options = webidl.converters.AddEventListenerOptions(options, {
+ prefix,
+ context: "Argument 3",
+ });
- get message() {
- return this.#message;
- }
- get filename() {
- return this.#filename;
+ if (callback === null) {
+ return;
}
- get lineno() {
- return this.#lineno;
+
+ const { listeners } = self[eventTargetData];
+
+ if (!(ReflectHas(listeners, type))) {
+ listeners[type] = [];
}
- get colno() {
- return this.#colno;
+
+ const listenerList = listeners[type];
+ for (let i = 0; i < listenerList.length; ++i) {
+ const listener = listenerList[i];
+ if (
+ ((typeof listener.options === "boolean" &&
+ listener.options === options.capture) ||
+ (typeof listener.options === "object" &&
+ listener.options.capture === options.capture)) &&
+ listener.callback === callback
+ ) {
+ return;
+ }
}
- get error() {
- return this.#error;
+ if (options?.signal) {
+ const signal = options?.signal;
+ if (signal.aborted) {
+ // If signal is not null and its aborted flag is set, then return.
+ return;
+ } else {
+ // If listener’s signal is not null, then add the following abort
+ // abort steps to it: Remove an event listener.
+ signal.addEventListener("abort", () => {
+ self.removeEventListener(type, callback, options);
+ });
+ }
}
- constructor(
- type,
- {
- bubbles,
- cancelable,
- composed,
- message = "",
- filename = "",
- lineno = 0,
- colno = 0,
- error,
- } = {},
- ) {
- super(type, {
- bubbles: bubbles,
- cancelable: cancelable,
- composed: composed,
- });
+ ArrayPrototypePush(listeners[type], { callback, options });
+ }
- this.#message = message;
- this.#filename = filename;
- this.#lineno = lineno;
- this.#colno = colno;
- this.#error = error;
- }
+ removeEventListener(
+ type,
+ callback,
+ options,
+ ) {
+ const self = this ?? globalThis_;
+ webidl.assertBranded(self, EventTargetPrototype);
+ webidl.requiredArguments(arguments.length, 2, {
+ prefix: "Failed to execute 'removeEventListener' on 'EventTarget'",
+ });
- [SymbolFor("Deno.privateCustomInspect")](inspect) {
- return inspect(consoleInternal.createFilteredInspectProxy({
- object: this,
- evaluate: ObjectPrototypeIsPrototypeOf(ErrorEvent.prototype, this),
- keys: [
- ...new SafeArrayIterator(EVENT_PROPS),
- "message",
- "filename",
- "lineno",
- "colno",
- "error",
- ],
- }));
+ const { listeners } = self[eventTargetData];
+ if (callback !== null && ReflectHas(listeners, type)) {
+ listeners[type] = ArrayPrototypeFilter(
+ listeners[type],
+ (listener) => listener.callback !== callback,
+ );
+ } else if (callback === null || !listeners[type]) {
+ return;
}
- // TODO(lucacasonato): remove when this interface is spec aligned
- [SymbolToStringTag] = "ErrorEvent";
- }
+ options = normalizeEventHandlerOptions(options);
- defineEnumerableProps(ErrorEvent, [
- "message",
- "filename",
- "lineno",
- "colno",
- "error",
- ]);
+ for (let i = 0; i < listeners[type].length; ++i) {
+ const listener = listeners[type][i];
+ if (
+ ((typeof listener.options === "boolean" &&
+ listener.options === options.capture) ||
+ (typeof listener.options === "object" &&
+ listener.options.capture === options.capture)) &&
+ listener.callback === callback
+ ) {
+ ArrayPrototypeSplice(listeners[type], i, 1);
+ break;
+ }
+ }
+ }
- class CloseEvent extends Event {
- #wasClean = "";
- #code = "";
- #reason = "";
+ dispatchEvent(event) {
+ // If `this` is not present, then fallback to global scope. We don't use
+ // `globalThis` directly here, because it could be deleted by user.
+ // Instead use saved reference to global scope when the script was
+ // executed.
+ const self = this ?? globalThis_;
+ webidl.assertBranded(self, EventTargetPrototype);
+ webidl.requiredArguments(arguments.length, 1, {
+ prefix: "Failed to execute 'dispatchEvent' on 'EventTarget'",
+ });
- get wasClean() {
- return this.#wasClean;
+ const { listeners } = self[eventTargetData];
+ if (!ReflectHas(listeners, event.type)) {
+ setTarget(event, this);
+ return true;
}
- get code() {
- return this.#code;
+
+ if (getDispatched(event)) {
+ throw new DOMException("Invalid event state.", "InvalidStateError");
}
- get reason() {
- return this.#reason;
+
+ if (event.eventPhase !== Event.NONE) {
+ throw new DOMException("Invalid event state.", "InvalidStateError");
}
- constructor(type, {
+ return dispatch(self, event);
+ }
+
+ getParent(_event) {
+ return null;
+ }
+}
+
+webidl.configurePrototype(EventTarget);
+const EventTargetPrototype = EventTarget.prototype;
+
+defineEnumerableProps(EventTarget, [
+ "addEventListener",
+ "removeEventListener",
+ "dispatchEvent",
+]);
+
+class ErrorEvent extends Event {
+ #message = "";
+ #filename = "";
+ #lineno = "";
+ #colno = "";
+ #error = "";
+
+ get message() {
+ return this.#message;
+ }
+ get filename() {
+ return this.#filename;
+ }
+ get lineno() {
+ return this.#lineno;
+ }
+ get colno() {
+ return this.#colno;
+ }
+ get error() {
+ return this.#error;
+ }
+
+ constructor(
+ type,
+ {
bubbles,
cancelable,
composed,
- wasClean = false,
- code = 0,
- reason = "",
- } = {}) {
- super(type, {
- bubbles: bubbles,
- cancelable: cancelable,
- composed: composed,
- });
+ message = "",
+ filename = "",
+ lineno = 0,
+ colno = 0,
+ error,
+ } = {},
+ ) {
+ super(type, {
+ bubbles: bubbles,
+ cancelable: cancelable,
+ composed: composed,
+ });
- this.#wasClean = wasClean;
- this.#code = code;
- this.#reason = reason;
- }
+ this.#message = message;
+ this.#filename = filename;
+ this.#lineno = lineno;
+ this.#colno = colno;
+ this.#error = error;
+ }
- [SymbolFor("Deno.privateCustomInspect")](inspect) {
- return inspect(consoleInternal.createFilteredInspectProxy({
- object: this,
- evaluate: ObjectPrototypeIsPrototypeOf(CloseEvent.prototype, this),
- keys: [
- ...new SafeArrayIterator(EVENT_PROPS),
- "wasClean",
- "code",
- "reason",
- ],
- }));
- }
+ [SymbolFor("Deno.privateCustomInspect")](inspect) {
+ return inspect(createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(ErrorEvent.prototype, this),
+ keys: [
+ ...new SafeArrayIterator(EVENT_PROPS),
+ "message",
+ "filename",
+ "lineno",
+ "colno",
+ "error",
+ ],
+ }));
}
- class MessageEvent extends Event {
- get source() {
- return null;
- }
+ // TODO(lucacasonato): remove when this interface is spec aligned
+ [SymbolToStringTag] = "ErrorEvent";
+}
+
+defineEnumerableProps(ErrorEvent, [
+ "message",
+ "filename",
+ "lineno",
+ "colno",
+ "error",
+]);
+
+class CloseEvent extends Event {
+ #wasClean = "";
+ #code = "";
+ #reason = "";
+
+ get wasClean() {
+ return this.#wasClean;
+ }
+ get code() {
+ return this.#code;
+ }
+ get reason() {
+ return this.#reason;
+ }
- constructor(type, eventInitDict) {
- super(type, {
- bubbles: eventInitDict?.bubbles ?? false,
- cancelable: eventInitDict?.cancelable ?? false,
- composed: eventInitDict?.composed ?? false,
- [_skipInternalInit]: eventInitDict?.[_skipInternalInit],
- });
+ constructor(type, {
+ bubbles,
+ cancelable,
+ composed,
+ wasClean = false,
+ code = 0,
+ reason = "",
+ } = {}) {
+ super(type, {
+ bubbles: bubbles,
+ cancelable: cancelable,
+ composed: composed,
+ });
- this.data = eventInitDict?.data ?? null;
- this.ports = eventInitDict?.ports ?? [];
- this.origin = eventInitDict?.origin ?? "";
- this.lastEventId = eventInitDict?.lastEventId ?? "";
- }
+ this.#wasClean = wasClean;
+ this.#code = code;
+ this.#reason = reason;
+ }
- [SymbolFor("Deno.privateCustomInspect")](inspect) {
- return inspect(consoleInternal.createFilteredInspectProxy({
- object: this,
- evaluate: ObjectPrototypeIsPrototypeOf(MessageEvent.prototype, this),
- keys: [
- ...new SafeArrayIterator(EVENT_PROPS),
- "data",
- "origin",
- "lastEventId",
- ],
- }));
- }
+ [SymbolFor("Deno.privateCustomInspect")](inspect) {
+ return inspect(createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(CloseEvent.prototype, this),
+ keys: [
+ ...new SafeArrayIterator(EVENT_PROPS),
+ "wasClean",
+ "code",
+ "reason",
+ ],
+ }));
+ }
+}
- // TODO(lucacasonato): remove when this interface is spec aligned
- [SymbolToStringTag] = "CloseEvent";
+class MessageEvent extends Event {
+ get source() {
+ return null;
}
- class CustomEvent extends Event {
- #detail = null;
+ constructor(type, eventInitDict) {
+ super(type, {
+ bubbles: eventInitDict?.bubbles ?? false,
+ cancelable: eventInitDict?.cancelable ?? false,
+ composed: eventInitDict?.composed ?? false,
+ [_skipInternalInit]: eventInitDict?.[_skipInternalInit],
+ });
- constructor(type, eventInitDict = {}) {
- super(type, eventInitDict);
- webidl.requiredArguments(arguments.length, 1, {
- prefix: "Failed to construct 'CustomEvent'",
- });
- const { detail } = eventInitDict;
- this.#detail = detail;
- }
+ this.data = eventInitDict?.data ?? null;
+ this.ports = eventInitDict?.ports ?? [];
+ this.origin = eventInitDict?.origin ?? "";
+ this.lastEventId = eventInitDict?.lastEventId ?? "";
+ }
- get detail() {
- return this.#detail;
- }
+ [SymbolFor("Deno.privateCustomInspect")](inspect) {
+ return inspect(createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(MessageEvent.prototype, this),
+ keys: [
+ ...new SafeArrayIterator(EVENT_PROPS),
+ "data",
+ "origin",
+ "lastEventId",
+ ],
+ }));
+ }
- [SymbolFor("Deno.privateCustomInspect")](inspect) {
- return inspect(consoleInternal.createFilteredInspectProxy({
- object: this,
- evaluate: ObjectPrototypeIsPrototypeOf(CustomEvent.prototype, this),
- keys: [
- ...new SafeArrayIterator(EVENT_PROPS),
- "detail",
- ],
- }));
- }
+ // TODO(lucacasonato): remove when this interface is spec aligned
+ [SymbolToStringTag] = "CloseEvent";
+}
- // TODO(lucacasonato): remove when this interface is spec aligned
- [SymbolToStringTag] = "CustomEvent";
+class CustomEvent extends Event {
+ #detail = null;
+
+ constructor(type, eventInitDict = {}) {
+ super(type, eventInitDict);
+ webidl.requiredArguments(arguments.length, 1, {
+ prefix: "Failed to construct 'CustomEvent'",
+ });
+ const { detail } = eventInitDict;
+ this.#detail = detail;
}
- ReflectDefineProperty(CustomEvent.prototype, "detail", {
- enumerable: true,
- });
+ get detail() {
+ return this.#detail;
+ }
- // ProgressEvent could also be used in other DOM progress event emits.
- // Current use is for FileReader.
- class ProgressEvent extends Event {
- constructor(type, eventInitDict = {}) {
- super(type, eventInitDict);
+ [SymbolFor("Deno.privateCustomInspect")](inspect) {
+ return inspect(createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(CustomEvent.prototype, this),
+ keys: [
+ ...new SafeArrayIterator(EVENT_PROPS),
+ "detail",
+ ],
+ }));
+ }
- this.lengthComputable = eventInitDict?.lengthComputable ?? false;
- this.loaded = eventInitDict?.loaded ?? 0;
- this.total = eventInitDict?.total ?? 0;
- }
+ // TODO(lucacasonato): remove when this interface is spec aligned
+ [SymbolToStringTag] = "CustomEvent";
+}
- [SymbolFor("Deno.privateCustomInspect")](inspect) {
- return inspect(consoleInternal.createFilteredInspectProxy({
- object: this,
- evaluate: ObjectPrototypeIsPrototypeOf(ProgressEvent.prototype, this),
- keys: [
- ...new SafeArrayIterator(EVENT_PROPS),
- "lengthComputable",
- "loaded",
- "total",
- ],
- }));
- }
+ReflectDefineProperty(CustomEvent.prototype, "detail", {
+ enumerable: true,
+});
- // TODO(lucacasonato): remove when this interface is spec aligned
- [SymbolToStringTag] = "ProgressEvent";
+// ProgressEvent could also be used in other DOM progress event emits.
+// Current use is for FileReader.
+class ProgressEvent extends Event {
+ constructor(type, eventInitDict = {}) {
+ super(type, eventInitDict);
+
+ this.lengthComputable = eventInitDict?.lengthComputable ?? false;
+ this.loaded = eventInitDict?.loaded ?? 0;
+ this.total = eventInitDict?.total ?? 0;
}
- class PromiseRejectionEvent extends Event {
- #promise = null;
- #reason = null;
+ [SymbolFor("Deno.privateCustomInspect")](inspect) {
+ return inspect(createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(ProgressEvent.prototype, this),
+ keys: [
+ ...new SafeArrayIterator(EVENT_PROPS),
+ "lengthComputable",
+ "loaded",
+ "total",
+ ],
+ }));
+ }
- get promise() {
- return this.#promise;
- }
- get reason() {
- return this.#reason;
- }
+ // TODO(lucacasonato): remove when this interface is spec aligned
+ [SymbolToStringTag] = "ProgressEvent";
+}
- constructor(
- type,
- {
- bubbles,
- cancelable,
- composed,
- promise,
- reason,
- } = {},
- ) {
- super(type, {
- bubbles: bubbles,
- cancelable: cancelable,
- composed: composed,
- });
+class PromiseRejectionEvent extends Event {
+ #promise = null;
+ #reason = null;
- this.#promise = promise;
- this.#reason = reason;
- }
+ get promise() {
+ return this.#promise;
+ }
+ get reason() {
+ return this.#reason;
+ }
- [SymbolFor("Deno.privateCustomInspect")](inspect) {
- return inspect(consoleInternal.createFilteredInspectProxy({
- object: this,
- evaluate: ObjectPrototypeIsPrototypeOf(
- PromiseRejectionEvent.prototype,
- this,
- ),
- keys: [
- ...new SafeArrayIterator(EVENT_PROPS),
- "promise",
- "reason",
- ],
- }));
- }
+ constructor(
+ type,
+ {
+ bubbles,
+ cancelable,
+ composed,
+ promise,
+ reason,
+ } = {},
+ ) {
+ super(type, {
+ bubbles: bubbles,
+ cancelable: cancelable,
+ composed: composed,
+ });
- // TODO(lucacasonato): remove when this interface is spec aligned
- [SymbolToStringTag] = "PromiseRejectionEvent";
+ this.#promise = promise;
+ this.#reason = reason;
}
- defineEnumerableProps(PromiseRejectionEvent, [
- "promise",
- "reason",
- ]);
+ [SymbolFor("Deno.privateCustomInspect")](inspect) {
+ return inspect(createFilteredInspectProxy({
+ object: this,
+ evaluate: ObjectPrototypeIsPrototypeOf(
+ PromiseRejectionEvent.prototype,
+ this,
+ ),
+ keys: [
+ ...new SafeArrayIterator(EVENT_PROPS),
+ "promise",
+ "reason",
+ ],
+ }));
+ }
- const _eventHandlers = Symbol("eventHandlers");
+ // TODO(lucacasonato): remove when this interface is spec aligned
+ [SymbolToStringTag] = "PromiseRejectionEvent";
+}
- function makeWrappedHandler(handler, isSpecialErrorEventHandler) {
- function wrappedHandler(evt) {
- if (typeof wrappedHandler.handler !== "function") {
- return;
- }
+defineEnumerableProps(PromiseRejectionEvent, [
+ "promise",
+ "reason",
+]);
- if (
- isSpecialErrorEventHandler &&
- ObjectPrototypeIsPrototypeOf(ErrorEvent.prototype, evt) &&
- 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;
- }
+const _eventHandlers = Symbol("eventHandlers");
- return FunctionPrototypeCall(wrappedHandler.handler, this, evt);
+function makeWrappedHandler(handler, isSpecialErrorEventHandler) {
+ function wrappedHandler(evt) {
+ if (typeof wrappedHandler.handler !== "function") {
+ return;
}
- wrappedHandler.handler = handler;
- return wrappedHandler;
- }
-
- // `init` is an optional function that will be called the first time that the
- // event handler property is set. It will be called with the object on which
- // 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}`, {
- get() {
- if (!this[_eventHandlers]) {
- return null;
- }
- return MapPrototypeGet(this[_eventHandlers], name)?.handler ?? null;
- },
- 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 (
+ isSpecialErrorEventHandler &&
+ ObjectPrototypeIsPrototypeOf(ErrorEvent.prototype, evt) &&
+ 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;
+ }
- if (!this[_eventHandlers]) {
- this[_eventHandlers] = new Map();
- }
- let handlerWrapper = MapPrototypeGet(this[_eventHandlers], name);
- if (handlerWrapper) {
- handlerWrapper.handler = value;
- } else if (value !== null) {
- handlerWrapper = makeWrappedHandler(
- value,
- isSpecialErrorEventHandler,
- );
- this.addEventListener(name, handlerWrapper);
- init?.(this);
- }
- MapPrototypeSet(this[_eventHandlers], name, handlerWrapper);
- },
- configurable: true,
- enumerable: true,
- });
+ return FunctionPrototypeCall(wrappedHandler.handler, this, evt);
}
+ wrappedHandler.handler = handler;
+ return wrappedHandler;
+}
+
+// `init` is an optional function that will be called the first time that the
+// event handler property is set. It will be called with the object on which
+// 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}`, {
+ get() {
+ if (!this[_eventHandlers]) {
+ return null;
+ }
- let reportExceptionStackedCalls = 0;
-
- // https://html.spec.whatwg.org/#report-the-exception
- function reportException(error) {
- reportExceptionStackedCalls++;
- const jsError = core.destructureError(error);
- const message = jsError.exceptionMessage;
- let filename = "";
- let lineno = 0;
- let colno = 0;
- if (jsError.frames.length > 0) {
- filename = jsError.frames[0].fileName;
- lineno = jsError.frames[0].lineNumber;
- colno = jsError.frames[0].columnNumber;
- } else {
- const jsError = core.destructureError(new Error());
- const frames = jsError.frames;
- for (let i = 0; i < frames.length; ++i) {
- const frame = frames[i];
- if (
- typeof frame.fileName == "string" &&
- !StringPrototypeStartsWith(frame.fileName, "internal:")
- ) {
- filename = frame.fileName;
- lineno = frame.lineNumber;
- colno = frame.columnNumber;
- break;
- }
+ return MapPrototypeGet(this[_eventHandlers], name)?.handler ?? null;
+ },
+ 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;
}
- }
- const event = new ErrorEvent("error", {
- cancelable: true,
- message,
- filename,
- lineno,
- colno,
- error,
- });
- // Avoid recursing `reportException()` via error handlers more than once.
- if (reportExceptionStackedCalls > 1 || window.dispatchEvent(event)) {
- ops.op_dispatch_exception(error);
- }
- reportExceptionStackedCalls--;
- }
- function checkThis(thisArg) {
- if (thisArg !== null && thisArg !== undefined && thisArg !== globalThis) {
- throw new TypeError("Illegal invocation");
+ if (!this[_eventHandlers]) {
+ this[_eventHandlers] = new Map();
+ }
+ let handlerWrapper = MapPrototypeGet(this[_eventHandlers], name);
+ if (handlerWrapper) {
+ handlerWrapper.handler = value;
+ } else if (value !== null) {
+ handlerWrapper = makeWrappedHandler(
+ value,
+ isSpecialErrorEventHandler,
+ );
+ this.addEventListener(name, handlerWrapper);
+ init?.(this);
+ }
+ MapPrototypeSet(this[_eventHandlers], name, handlerWrapper);
+ },
+ configurable: true,
+ enumerable: true,
+ });
+}
+
+let reportExceptionStackedCalls = 0;
+
+// https://html.spec.whatwg.org/#report-the-exception
+function reportException(error) {
+ reportExceptionStackedCalls++;
+ const jsError = core.destructureError(error);
+ const message = jsError.exceptionMessage;
+ let filename = "";
+ let lineno = 0;
+ let colno = 0;
+ if (jsError.frames.length > 0) {
+ filename = jsError.frames[0].fileName;
+ lineno = jsError.frames[0].lineNumber;
+ colno = jsError.frames[0].columnNumber;
+ } else {
+ const jsError = core.destructureError(new Error());
+ const frames = jsError.frames;
+ for (let i = 0; i < frames.length; ++i) {
+ const frame = frames[i];
+ if (
+ typeof frame.fileName == "string" &&
+ !StringPrototypeStartsWith(frame.fileName, "internal:")
+ ) {
+ filename = frame.fileName;
+ lineno = frame.lineNumber;
+ colno = frame.columnNumber;
+ break;
+ }
}
}
-
- // https://html.spec.whatwg.org/#dom-reporterror
- function reportError(error) {
- checkThis(this);
- const prefix = "Failed to call 'reportError'";
- webidl.requiredArguments(arguments.length, 1, { prefix });
- reportException(error);
+ const event = new ErrorEvent("error", {
+ cancelable: true,
+ message,
+ filename,
+ lineno,
+ colno,
+ error,
+ });
+ // Avoid recursing `reportException()` via error handlers more than once.
+ if (reportExceptionStackedCalls > 1 || globalThis_.dispatchEvent(event)) {
+ ops.op_dispatch_exception(error);
}
+ reportExceptionStackedCalls--;
+}
- window.__bootstrap.eventTarget = {
- EventTarget,
- setEventTargetData,
- listenerCount,
- };
- window.__bootstrap.event = {
- reportException,
- setIsTrusted,
- setTarget,
- defineEventHandler,
- _skipInternalInit,
- Event,
- ErrorEvent,
- CloseEvent,
- MessageEvent,
- CustomEvent,
- ProgressEvent,
- PromiseRejectionEvent,
- reportError,
- };
-})(this);
+function checkThis(thisArg) {
+ if (thisArg !== null && thisArg !== undefined && thisArg !== globalThis_) {
+ throw new TypeError("Illegal invocation");
+ }
+}
+
+// https://html.spec.whatwg.org/#dom-reporterror
+function reportError(error) {
+ checkThis(this);
+ const prefix = "Failed to call 'reportError'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ reportException(error);
+}
+
+export {
+ _skipInternalInit,
+ CloseEvent,
+ CustomEvent,
+ defineEventHandler,
+ ErrorEvent,
+ Event,
+ EventTarget,
+ listenerCount,
+ MessageEvent,
+ ProgressEvent,
+ PromiseRejectionEvent,
+ reportError,
+ reportException,
+ saveGlobalThisReference,
+ setEventTargetData,
+ setIsTrusted,
+ setTarget,
+};