2021-01-11 12:13:41 -05:00
|
|
|
|
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
|
2020-07-19 13:49:44 -04:00
|
|
|
|
|
|
|
|
|
// This module follows most of the WHATWG Living Standard for the DOM logic.
|
|
|
|
|
// 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.
|
2021-02-04 17:18:32 -05:00
|
|
|
|
"use strict";
|
2020-07-19 13:49:44 -04:00
|
|
|
|
|
|
|
|
|
((window) => {
|
2021-02-13 09:58:12 -05:00
|
|
|
|
const webidl = window.__bootstrap.webidl;
|
2021-07-03 15:32:28 -04:00
|
|
|
|
const { DOMException } = window.__bootstrap.domException;
|
2020-07-19 13:49:44 -04:00
|
|
|
|
|
|
|
|
|
// accessors for non runtime visible data
|
|
|
|
|
|
|
|
|
|
function getDispatched(event) {
|
2021-02-13 09:58:12 -05:00
|
|
|
|
return Boolean(event[_dispatched]);
|
2020-07-19 13:49:44 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getPath(event) {
|
2021-02-13 09:58:12 -05:00
|
|
|
|
return event[_path] ?? [];
|
2020-07-19 13:49:44 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getStopImmediatePropagation(event) {
|
2021-02-13 09:58:12 -05:00
|
|
|
|
return Boolean(event[_stopImmediatePropagationFlag]);
|
2020-07-19 13:49:44 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function setCurrentTarget(
|
|
|
|
|
event,
|
|
|
|
|
value,
|
|
|
|
|
) {
|
2021-02-13 09:58:12 -05:00
|
|
|
|
event[_attributes].currentTarget = value;
|
2020-07-19 13:49:44 -04:00
|
|
|
|
}
|
|
|
|
|
|
2021-01-05 16:43:25 -05:00
|
|
|
|
function setIsTrusted(event, value) {
|
2021-02-13 09:58:12 -05:00
|
|
|
|
event[_isTrusted] = value;
|
2021-01-05 16:43:25 -05:00
|
|
|
|
}
|
|
|
|
|
|
2020-07-19 13:49:44 -04:00
|
|
|
|
function setDispatched(event, value) {
|
2021-02-13 09:58:12 -05:00
|
|
|
|
event[_dispatched] = value;
|
2020-07-19 13:49:44 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function setEventPhase(event, value) {
|
2021-02-13 09:58:12 -05:00
|
|
|
|
event[_attributes].eventPhase = value;
|
2020-07-19 13:49:44 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function setInPassiveListener(event, value) {
|
2021-02-13 09:58:12 -05:00
|
|
|
|
event[_inPassiveListener] = value;
|
2020-07-19 13:49:44 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function setPath(event, value) {
|
2021-02-13 09:58:12 -05:00
|
|
|
|
event[_path] = value;
|
2020-07-19 13:49:44 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function setRelatedTarget(
|
|
|
|
|
event,
|
|
|
|
|
value,
|
|
|
|
|
) {
|
2021-02-13 09:58:12 -05:00
|
|
|
|
event[_attributes].relatedTarget = value;
|
2020-07-19 13:49:44 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function setTarget(event, value) {
|
2021-02-13 09:58:12 -05:00
|
|
|
|
event[_attributes].target = value;
|
2020-07-19 13:49:44 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function setStopImmediatePropagation(
|
|
|
|
|
event,
|
|
|
|
|
value,
|
|
|
|
|
) {
|
2021-02-13 09:58:12 -05:00
|
|
|
|
event[_stopImmediatePropagationFlag] = value;
|
2020-07-19 13:49:44 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Type guards that widen the event type
|
|
|
|
|
|
|
|
|
|
function hasRelatedTarget(
|
|
|
|
|
event,
|
|
|
|
|
) {
|
|
|
|
|
return "relatedTarget" in event;
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-10 15:26:58 -05:00
|
|
|
|
const isTrusted = Object.getOwnPropertyDescriptor({
|
|
|
|
|
get isTrusted() {
|
2021-02-13 09:58:12 -05:00
|
|
|
|
return this[_isTrusted];
|
2020-11-10 15:26:58 -05:00
|
|
|
|
},
|
|
|
|
|
}, "isTrusted").get;
|
2020-07-19 13:49:44 -04:00
|
|
|
|
|
2021-02-13 09:58:12 -05:00
|
|
|
|
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]]");
|
|
|
|
|
|
2020-07-19 13:49:44 -04:00
|
|
|
|
class Event {
|
2021-02-13 09:58:12 -05:00
|
|
|
|
[_attributes] = {};
|
|
|
|
|
[_canceledFlag] = false;
|
|
|
|
|
[_stopPropagationFlag] = false;
|
|
|
|
|
[_stopImmediatePropagationFlag] = false;
|
|
|
|
|
[_inPassiveListener] = false;
|
|
|
|
|
[_dispatched] = false;
|
|
|
|
|
[_isTrusted] = false;
|
|
|
|
|
[_path] = [];
|
2020-07-19 13:49:44 -04:00
|
|
|
|
|
|
|
|
|
constructor(type, eventInitDict = {}) {
|
2021-02-13 09:58:12 -05:00
|
|
|
|
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] = {
|
2020-07-19 13:49:44 -04:00
|
|
|
|
type,
|
2021-02-13 09:58:12 -05:00
|
|
|
|
...eventInit,
|
2020-07-19 13:49:44 -04:00
|
|
|
|
currentTarget: null,
|
|
|
|
|
eventPhase: Event.NONE,
|
|
|
|
|
target: null,
|
|
|
|
|
timeStamp: Date.now(),
|
|
|
|
|
};
|
|
|
|
|
Reflect.defineProperty(this, "isTrusted", {
|
|
|
|
|
enumerable: true,
|
|
|
|
|
get: isTrusted,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-25 03:19:18 -04:00
|
|
|
|
[Symbol.for("Deno.privateCustomInspect")](inspect) {
|
2021-06-24 09:43:41 -04:00
|
|
|
|
return inspect(buildFilteredPropertyInspectObject(this, EVENT_PROPS));
|
2020-10-26 18:22:03 -04:00
|
|
|
|
}
|
|
|
|
|
|
2021-02-13 09:58:12 -05:00
|
|
|
|
get type() {
|
|
|
|
|
return this[_attributes].type;
|
2020-07-19 13:49:44 -04:00
|
|
|
|
}
|
2021-06-24 14:21:13 -04:00
|
|
|
|
|
2021-02-13 09:58:12 -05:00
|
|
|
|
get target() {
|
|
|
|
|
return this[_attributes].target;
|
2020-07-19 13:49:44 -04:00
|
|
|
|
}
|
2021-06-24 14:21:13 -04:00
|
|
|
|
|
2021-02-13 09:58:12 -05:00
|
|
|
|
get srcElement() {
|
|
|
|
|
return null;
|
2020-07-19 13:49:44 -04:00
|
|
|
|
}
|
2021-06-24 14:21:13 -04:00
|
|
|
|
|
2021-02-13 09:58:12 -05:00
|
|
|
|
set srcElement(_) {
|
2021-06-24 14:21:13 -04:00
|
|
|
|
// this member is deprecated
|
2020-07-19 13:49:44 -04:00
|
|
|
|
}
|
2021-06-24 14:21:13 -04:00
|
|
|
|
|
2021-02-13 09:58:12 -05:00
|
|
|
|
get currentTarget() {
|
|
|
|
|
return this[_attributes].currentTarget;
|
2020-07-19 13:49:44 -04:00
|
|
|
|
}
|
2021-06-24 14:21:13 -04:00
|
|
|
|
|
2020-07-19 13:49:44 -04:00
|
|
|
|
composedPath() {
|
2021-02-13 09:58:12 -05:00
|
|
|
|
const path = this[_path];
|
2020-07-19 13:49:44 -04:00
|
|
|
|
if (path.length === 0) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!this.currentTarget) {
|
|
|
|
|
throw new Error("assertion error");
|
|
|
|
|
}
|
|
|
|
|
const composedPath = [
|
|
|
|
|
{
|
|
|
|
|
item: this.currentTarget,
|
|
|
|
|
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) {
|
|
|
|
|
composedPath.unshift({
|
|
|
|
|
item,
|
|
|
|
|
itemInShadowTree: false,
|
|
|
|
|
relatedTarget: null,
|
|
|
|
|
rootOfClosedTree: false,
|
|
|
|
|
slotInClosedTree: false,
|
|
|
|
|
target: null,
|
|
|
|
|
touchTargetList: [],
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (slotInClosedTree) {
|
|
|
|
|
currentHiddenLevel--;
|
|
|
|
|
|
|
|
|
|
if (currentHiddenLevel < maxHiddenLevel) {
|
|
|
|
|
maxHiddenLevel = currentHiddenLevel;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
currentHiddenLevel = currentTargetHiddenSubtreeLevel;
|
|
|
|
|
maxHiddenLevel = currentTargetHiddenSubtreeLevel;
|
|
|
|
|
|
|
|
|
|
for (let index = currentTargetIndex + 1; index < path.length; index++) {
|
|
|
|
|
const { item, rootOfClosedTree, slotInClosedTree } = path[index];
|
|
|
|
|
|
|
|
|
|
if (slotInClosedTree) {
|
|
|
|
|
currentHiddenLevel++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (currentHiddenLevel <= maxHiddenLevel) {
|
|
|
|
|
composedPath.push({
|
|
|
|
|
item,
|
|
|
|
|
itemInShadowTree: false,
|
|
|
|
|
relatedTarget: null,
|
|
|
|
|
rootOfClosedTree: false,
|
|
|
|
|
slotInClosedTree: false,
|
|
|
|
|
target: null,
|
|
|
|
|
touchTargetList: [],
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (rootOfClosedTree) {
|
|
|
|
|
currentHiddenLevel--;
|
|
|
|
|
|
|
|
|
|
if (currentHiddenLevel < maxHiddenLevel) {
|
|
|
|
|
maxHiddenLevel = currentHiddenLevel;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return composedPath.map((p) => p.item);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get NONE() {
|
|
|
|
|
return Event.NONE;
|
|
|
|
|
}
|
2021-06-24 14:21:13 -04:00
|
|
|
|
|
2020-07-19 13:49:44 -04:00
|
|
|
|
get CAPTURING_PHASE() {
|
|
|
|
|
return Event.CAPTURING_PHASE;
|
|
|
|
|
}
|
2021-06-24 14:21:13 -04:00
|
|
|
|
|
2020-07-19 13:49:44 -04:00
|
|
|
|
get AT_TARGET() {
|
|
|
|
|
return Event.AT_TARGET;
|
|
|
|
|
}
|
2021-06-24 14:21:13 -04:00
|
|
|
|
|
2020-07-19 13:49:44 -04:00
|
|
|
|
get BUBBLING_PHASE() {
|
|
|
|
|
return Event.BUBBLING_PHASE;
|
|
|
|
|
}
|
2021-06-24 14:21:13 -04:00
|
|
|
|
|
2020-07-19 13:49:44 -04:00
|
|
|
|
static get NONE() {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2021-06-24 14:21:13 -04:00
|
|
|
|
|
2020-07-19 13:49:44 -04:00
|
|
|
|
static get CAPTURING_PHASE() {
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2021-06-24 14:21:13 -04:00
|
|
|
|
|
2020-07-19 13:49:44 -04:00
|
|
|
|
static get AT_TARGET() {
|
|
|
|
|
return 2;
|
|
|
|
|
}
|
2021-06-24 14:21:13 -04:00
|
|
|
|
|
2020-07-19 13:49:44 -04:00
|
|
|
|
static get BUBBLING_PHASE() {
|
|
|
|
|
return 3;
|
|
|
|
|
}
|
2021-06-24 14:21:13 -04:00
|
|
|
|
|
2021-02-13 09:58:12 -05:00
|
|
|
|
get eventPhase() {
|
|
|
|
|
return this[_attributes].eventPhase;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stopPropagation() {
|
|
|
|
|
this[_stopPropagationFlag] = true;
|
|
|
|
|
}
|
2021-06-24 14:21:13 -04:00
|
|
|
|
|
2021-02-13 09:58:12 -05:00
|
|
|
|
get cancelBubble() {
|
|
|
|
|
return this[_stopPropagationFlag];
|
|
|
|
|
}
|
2021-06-24 14:21:13 -04:00
|
|
|
|
|
2021-02-13 09:58:12 -05:00
|
|
|
|
set cancelBubble(value) {
|
|
|
|
|
this[_stopPropagationFlag] = webidl.converters.boolean(value);
|
|
|
|
|
}
|
2021-06-24 14:21:13 -04:00
|
|
|
|
|
2021-02-13 09:58:12 -05:00
|
|
|
|
stopImmediatePropagation() {
|
|
|
|
|
this[_stopPropagationFlag] = true;
|
|
|
|
|
this[_stopImmediatePropagationFlag] = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get bubbles() {
|
|
|
|
|
return this[_attributes].bubbles;
|
|
|
|
|
}
|
2021-06-24 14:21:13 -04:00
|
|
|
|
|
2021-02-13 09:58:12 -05:00
|
|
|
|
get cancelable() {
|
|
|
|
|
return this[_attributes].cancelable;
|
|
|
|
|
}
|
2021-06-24 14:21:13 -04:00
|
|
|
|
|
2021-02-13 09:58:12 -05:00
|
|
|
|
get returnValue() {
|
|
|
|
|
return !this[_canceledFlag];
|
|
|
|
|
}
|
2021-06-24 14:21:13 -04:00
|
|
|
|
|
2021-02-13 09:58:12 -05:00
|
|
|
|
set returnValue(value) {
|
|
|
|
|
if (!webidl.converters.boolean(value)) {
|
|
|
|
|
this[_canceledFlag] = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-06-24 14:21:13 -04:00
|
|
|
|
|
2021-02-13 09:58:12 -05:00
|
|
|
|
preventDefault() {
|
|
|
|
|
if (this[_attributes].cancelable && !this[_inPassiveListener]) {
|
|
|
|
|
this[_canceledFlag] = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-06-24 14:21:13 -04:00
|
|
|
|
|
2021-02-13 09:58:12 -05:00
|
|
|
|
get defaultPrevented() {
|
|
|
|
|
return this[_canceledFlag];
|
|
|
|
|
}
|
2021-06-24 14:21:13 -04:00
|
|
|
|
|
2021-02-13 09:58:12 -05:00
|
|
|
|
get composed() {
|
|
|
|
|
return this[_attributes].composed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get initialized() {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get timeStamp() {
|
|
|
|
|
return this[_attributes].timeStamp;
|
|
|
|
|
}
|
2020-07-19 13:49:44 -04:00
|
|
|
|
}
|
|
|
|
|
|
2021-06-24 09:43:41 -04:00
|
|
|
|
function buildFilteredPropertyInspectObject(object, keys) {
|
|
|
|
|
// forward the subset of properties from `object` without evaluating
|
|
|
|
|
// as evaluation could lead to an error, which is better handled
|
|
|
|
|
// in the inspect code
|
|
|
|
|
return new Proxy({}, {
|
|
|
|
|
get(_target, key) {
|
|
|
|
|
if (key === Symbol.toStringTag) {
|
|
|
|
|
return object.constructor?.name;
|
|
|
|
|
} else if (keys.includes(key)) {
|
|
|
|
|
return Reflect.get(object, key);
|
|
|
|
|
} else {
|
|
|
|
|
return undefined;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
getOwnPropertyDescriptor(_target, key) {
|
|
|
|
|
if (!keys.includes(key)) {
|
|
|
|
|
return undefined;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Reflect.getOwnPropertyDescriptor(object, key) ??
|
|
|
|
|
(object.prototype &&
|
|
|
|
|
Reflect.getOwnPropertyDescriptor(object.prototype, key)) ??
|
|
|
|
|
{
|
|
|
|
|
configurable: true,
|
|
|
|
|
enumerable: true,
|
|
|
|
|
value: object[key],
|
|
|
|
|
};
|
|
|
|
|
},
|
|
|
|
|
has(_target, key) {
|
|
|
|
|
return keys.includes(key);
|
|
|
|
|
},
|
|
|
|
|
ownKeys() {
|
|
|
|
|
return keys;
|
|
|
|
|
},
|
|
|
|
|
});
|
2020-10-26 18:22:03 -04:00
|
|
|
|
}
|
|
|
|
|
|
2020-07-19 13:49:44 -04:00
|
|
|
|
function defineEnumerableProps(
|
|
|
|
|
Ctor,
|
|
|
|
|
props,
|
|
|
|
|
) {
|
|
|
|
|
for (const prop of props) {
|
|
|
|
|
Reflect.defineProperty(Ctor.prototype, prop, { enumerable: true });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-26 18:22:03 -04:00
|
|
|
|
const EVENT_PROPS = [
|
2020-07-19 13:49:44 -04:00
|
|
|
|
"bubbles",
|
|
|
|
|
"cancelable",
|
|
|
|
|
"composed",
|
|
|
|
|
"currentTarget",
|
|
|
|
|
"defaultPrevented",
|
|
|
|
|
"eventPhase",
|
2021-02-13 09:58:12 -05:00
|
|
|
|
"srcElement",
|
2020-07-19 13:49:44 -04:00
|
|
|
|
"target",
|
2021-02-13 09:58:12 -05:00
|
|
|
|
"returnValue",
|
2020-07-19 13:49:44 -04:00
|
|
|
|
"timeStamp",
|
|
|
|
|
"type",
|
2020-10-26 18:22:03 -04:00
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
defineEnumerableProps(Event, EVENT_PROPS);
|
2020-07-19 13:49:44 -04:00
|
|
|
|
|
|
|
|
|
// 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 && "nodeType" in eventTarget);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isShadowRoot(nodeImpl) {
|
|
|
|
|
return Boolean(
|
|
|
|
|
nodeImpl &&
|
|
|
|
|
isNode(nodeImpl) &&
|
|
|
|
|
nodeImpl.nodeType === DOCUMENT_FRAGMENT_NODE &&
|
|
|
|
|
getHost(nodeImpl) != null,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isSlotable(
|
|
|
|
|
nodeImpl,
|
|
|
|
|
) {
|
|
|
|
|
return Boolean(isNode(nodeImpl) && "assignedSlot" in nodeImpl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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";
|
|
|
|
|
|
|
|
|
|
getPath(eventImpl).push({
|
|
|
|
|
item: target,
|
|
|
|
|
itemInShadowTree,
|
|
|
|
|
target: targetOverride,
|
|
|
|
|
relatedTarget,
|
|
|
|
|
touchTargetList: touchTargets,
|
|
|
|
|
rootOfClosedTree,
|
|
|
|
|
slotInClosedTree,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
touchTargets,
|
|
|
|
|
false,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const isActivationEvent = eventImpl.type === "click";
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
isNode(parent) &&
|
|
|
|
|
isShadowInclusiveAncestor(getRoot(targetImpl), parent)
|
|
|
|
|
) {
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < path.length; i++) {
|
|
|
|
|
const tuple = path[i];
|
|
|
|
|
|
|
|
|
|
if (tuple.target !== null) {
|
|
|
|
|
setEventPhase(eventImpl, Event.AT_TARGET);
|
|
|
|
|
} else {
|
|
|
|
|
setEventPhase(eventImpl, Event.BUBBLING_PHASE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
(eventImpl.eventPhase === Event.BUBBLING_PHASE &&
|
|
|
|
|
eventImpl.bubbles) ||
|
|
|
|
|
eventImpl.eventPhase === Event.AT_TARGET
|
|
|
|
|
) {
|
|
|
|
|
invokeEventListeners(tuple, eventImpl);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setEventPhase(eventImpl, Event.NONE);
|
|
|
|
|
setCurrentTarget(eventImpl, null);
|
|
|
|
|
setPath(eventImpl, []);
|
|
|
|
|
setDispatched(eventImpl, false);
|
|
|
|
|
eventImpl.cancelBubble = false;
|
|
|
|
|
setStopImmediatePropagation(eventImpl, false);
|
|
|
|
|
|
|
|
|
|
if (clearTargets) {
|
|
|
|
|
setTarget(eventImpl, null);
|
|
|
|
|
setRelatedTarget(eventImpl, null);
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-16 18:32:59 -05:00
|
|
|
|
// TODO(bartlomieju): invoke activation targets if HTML nodes will be implemented
|
2020-07-19 13:49:44 -04:00
|
|
|
|
// 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Copy event listeners before iterating since the list can be modified during the iteration.
|
|
|
|
|
const handlers = targetListeners[type].slice();
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < handlers.length; i++) {
|
|
|
|
|
const listener = handlers[i];
|
|
|
|
|
|
2021-05-18 11:24:01 -04:00
|
|
|
|
let capture, once, passive;
|
2020-07-19 13:49:44 -04:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if the event listener has been removed since the listeners has been cloned.
|
|
|
|
|
if (!targetListeners[type].includes(listener)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
found = true;
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
(eventImpl.eventPhase === Event.CAPTURING_PHASE && !capture) ||
|
|
|
|
|
(eventImpl.eventPhase === Event.BUBBLING_PHASE && capture)
|
|
|
|
|
) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (once) {
|
|
|
|
|
targetListeners[type].splice(
|
|
|
|
|
targetListeners[type].indexOf(listener),
|
|
|
|
|
1,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (passive) {
|
|
|
|
|
setInPassiveListener(eventImpl, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (typeof listener.callback === "object") {
|
|
|
|
|
if (typeof listener.callback.handleEvent === "function") {
|
|
|
|
|
listener.callback.handleEvent(eventImpl);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
listener.callback.call(eventImpl.currentTarget, eventImpl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setInPassiveListener(eventImpl, false);
|
|
|
|
|
|
|
|
|
|
if (getStopImmediatePropagation(eventImpl)) {
|
|
|
|
|
return found;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 = path.indexOf(tuple);
|
|
|
|
|
for (let i = tupleIndex; i >= 0; i--) {
|
|
|
|
|
const t = path[i];
|
|
|
|
|
if (t.target) {
|
|
|
|
|
setTarget(eventImpl, t.target);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setRelatedTarget(eventImpl, tuple.relatedTarget);
|
|
|
|
|
|
|
|
|
|
if (eventImpl.cancelBubble) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setCurrentTarget(eventImpl, tuple.item);
|
|
|
|
|
|
|
|
|
|
innerInvokeEventListeners(eventImpl, getListeners(tuple.item));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function normalizeAddEventHandlerOptions(
|
|
|
|
|
options,
|
|
|
|
|
) {
|
|
|
|
|
if (typeof options === "boolean" || typeof options === "undefined") {
|
|
|
|
|
return {
|
|
|
|
|
capture: Boolean(options),
|
|
|
|
|
once: false,
|
|
|
|
|
passive: false,
|
|
|
|
|
};
|
|
|
|
|
} else {
|
|
|
|
|
return options;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function normalizeEventHandlerOptions(
|
|
|
|
|
options,
|
|
|
|
|
) {
|
|
|
|
|
if (typeof options === "boolean" || typeof options === "undefined") {
|
|
|
|
|
return {
|
|
|
|
|
capture: Boolean(options),
|
|
|
|
|
};
|
|
|
|
|
} else {
|
|
|
|
|
return options;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const aRoot = a.getRootNode();
|
|
|
|
|
|
|
|
|
|
if (aRoot) {
|
|
|
|
|
if (
|
|
|
|
|
!isShadowRoot(aRoot) ||
|
|
|
|
|
(isNode(b) && isShadowInclusiveAncestor(aRoot, b))
|
|
|
|
|
) {
|
|
|
|
|
return a;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
a = getHost(aRoot);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Accessors for non-public data
|
|
|
|
|
|
|
|
|
|
const eventTargetData = new WeakMap();
|
|
|
|
|
|
|
|
|
|
function setEventTargetData(value) {
|
|
|
|
|
eventTargetData.set(value, getDefaultTargetData());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getAssignedSlot(target) {
|
|
|
|
|
return Boolean(eventTargetData.get(target)?.assignedSlot);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getHasActivationBehavior(target) {
|
|
|
|
|
return Boolean(
|
|
|
|
|
eventTargetData.get(target)?.hasActivationBehavior,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getHost(target) {
|
|
|
|
|
return eventTargetData.get(target)?.host ?? null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getListeners(target) {
|
|
|
|
|
return eventTargetData.get(target)?.listeners ?? {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getMode(target) {
|
|
|
|
|
return eventTargetData.get(target)?.mode ?? null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getDefaultTargetData() {
|
|
|
|
|
return {
|
|
|
|
|
assignedSlot: false,
|
|
|
|
|
hasActivationBehavior: false,
|
|
|
|
|
host: null,
|
|
|
|
|
listeners: Object.create(null),
|
|
|
|
|
mode: "",
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class EventTarget {
|
|
|
|
|
constructor() {
|
|
|
|
|
eventTargetData.set(this, getDefaultTargetData());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addEventListener(
|
|
|
|
|
type,
|
|
|
|
|
callback,
|
|
|
|
|
options,
|
|
|
|
|
) {
|
2021-02-13 09:58:12 -05:00
|
|
|
|
webidl.requiredArguments(arguments.length, 2, {
|
|
|
|
|
prefix: "Failed to execute 'addEventListener' on 'EventTarget'",
|
|
|
|
|
});
|
2020-07-19 13:49:44 -04:00
|
|
|
|
if (callback === null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
options = normalizeAddEventHandlerOptions(options);
|
|
|
|
|
const { listeners } = eventTargetData.get(this ?? globalThis);
|
|
|
|
|
|
|
|
|
|
if (!(type in listeners)) {
|
|
|
|
|
listeners[type] = [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const listener of listeners[type]) {
|
|
|
|
|
if (
|
|
|
|
|
((typeof listener.options === "boolean" &&
|
|
|
|
|
listener.options === options.capture) ||
|
|
|
|
|
(typeof listener.options === "object" &&
|
|
|
|
|
listener.options.capture === options.capture)) &&
|
|
|
|
|
listener.callback === callback
|
|
|
|
|
) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-12-04 12:47:08 -05:00
|
|
|
|
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", () => {
|
|
|
|
|
this.removeEventListener(type, callback, options);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-07-19 13:49:44 -04:00
|
|
|
|
listeners[type].push({ callback, options });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
removeEventListener(
|
|
|
|
|
type,
|
|
|
|
|
callback,
|
|
|
|
|
options,
|
|
|
|
|
) {
|
2021-02-13 09:58:12 -05:00
|
|
|
|
webidl.requiredArguments(arguments.length, 2, {
|
|
|
|
|
prefix: "Failed to execute 'removeEventListener' on 'EventTarget'",
|
|
|
|
|
});
|
2020-07-19 13:49:44 -04:00
|
|
|
|
|
|
|
|
|
const listeners = eventTargetData.get(this ?? globalThis).listeners;
|
|
|
|
|
if (callback !== null && type in listeners) {
|
|
|
|
|
listeners[type] = listeners[type].filter(
|
|
|
|
|
(listener) => listener.callback !== callback,
|
|
|
|
|
);
|
|
|
|
|
} else if (callback === null || !listeners[type]) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
options = normalizeEventHandlerOptions(options);
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
) {
|
|
|
|
|
listeners[type].splice(i, 1);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dispatchEvent(event) {
|
2021-02-13 09:58:12 -05:00
|
|
|
|
webidl.requiredArguments(arguments.length, 1, {
|
|
|
|
|
prefix: "Failed to execute 'dispatchEvent' on 'EventTarget'",
|
|
|
|
|
});
|
2020-07-19 13:49:44 -04:00
|
|
|
|
const self = this ?? globalThis;
|
|
|
|
|
|
|
|
|
|
const listeners = eventTargetData.get(self).listeners;
|
|
|
|
|
if (!(event.type in listeners)) {
|
2020-11-02 12:42:22 -05:00
|
|
|
|
setTarget(event, this);
|
2020-07-19 13:49:44 -04:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (getDispatched(event)) {
|
|
|
|
|
throw new DOMException("Invalid event state.", "InvalidStateError");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (event.eventPhase !== Event.NONE) {
|
|
|
|
|
throw new DOMException("Invalid event state.", "InvalidStateError");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return dispatch(self, event);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get [Symbol.toStringTag]() {
|
|
|
|
|
return "EventTarget";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getParent(_event) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
message = "",
|
|
|
|
|
filename = "",
|
|
|
|
|
lineno = 0,
|
|
|
|
|
colno = 0,
|
|
|
|
|
error = null,
|
|
|
|
|
} = {},
|
|
|
|
|
) {
|
|
|
|
|
super(type, {
|
|
|
|
|
bubbles: bubbles,
|
|
|
|
|
cancelable: cancelable,
|
|
|
|
|
composed: composed,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.#message = message;
|
|
|
|
|
this.#filename = filename;
|
|
|
|
|
this.#lineno = lineno;
|
|
|
|
|
this.#colno = colno;
|
|
|
|
|
this.#error = error;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get [Symbol.toStringTag]() {
|
|
|
|
|
return "ErrorEvent";
|
|
|
|
|
}
|
2020-10-26 18:22:03 -04:00
|
|
|
|
|
2021-06-25 03:19:18 -04:00
|
|
|
|
[Symbol.for("Deno.privateCustomInspect")](inspect) {
|
2021-06-24 09:43:41 -04:00
|
|
|
|
return inspect(buildFilteredPropertyInspectObject(this, [
|
2020-10-26 18:22:03 -04:00
|
|
|
|
...EVENT_PROPS,
|
|
|
|
|
"message",
|
|
|
|
|
"filename",
|
|
|
|
|
"lineno",
|
|
|
|
|
"colno",
|
|
|
|
|
"error",
|
2021-06-24 09:43:41 -04:00
|
|
|
|
]));
|
2020-10-26 18:22:03 -04:00
|
|
|
|
}
|
2020-07-19 13:49:44 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
defineEnumerableProps(ErrorEvent, [
|
|
|
|
|
"message",
|
|
|
|
|
"filename",
|
|
|
|
|
"lineno",
|
|
|
|
|
"colno",
|
|
|
|
|
"error",
|
|
|
|
|
]);
|
|
|
|
|
|
2020-09-05 10:39:25 -04:00
|
|
|
|
class CloseEvent extends Event {
|
|
|
|
|
#wasClean = "";
|
|
|
|
|
#code = "";
|
|
|
|
|
#reason = "";
|
|
|
|
|
|
|
|
|
|
get wasClean() {
|
|
|
|
|
return this.#wasClean;
|
|
|
|
|
}
|
|
|
|
|
get code() {
|
|
|
|
|
return this.#code;
|
|
|
|
|
}
|
|
|
|
|
get reason() {
|
|
|
|
|
return this.#reason;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
constructor(type, {
|
|
|
|
|
bubbles,
|
|
|
|
|
cancelable,
|
|
|
|
|
composed,
|
|
|
|
|
wasClean = false,
|
|
|
|
|
code = 0,
|
|
|
|
|
reason = "",
|
|
|
|
|
} = {}) {
|
|
|
|
|
super(type, {
|
|
|
|
|
bubbles: bubbles,
|
|
|
|
|
cancelable: cancelable,
|
|
|
|
|
composed: composed,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.#wasClean = wasClean;
|
|
|
|
|
this.#code = code;
|
|
|
|
|
this.#reason = reason;
|
|
|
|
|
}
|
2020-10-26 18:22:03 -04:00
|
|
|
|
|
2021-06-25 03:19:18 -04:00
|
|
|
|
[Symbol.for("Deno.privateCustomInspect")](inspect) {
|
2021-06-24 09:43:41 -04:00
|
|
|
|
return inspect(buildFilteredPropertyInspectObject(this, [
|
2020-10-26 18:22:03 -04:00
|
|
|
|
...EVENT_PROPS,
|
|
|
|
|
"wasClean",
|
|
|
|
|
"code",
|
|
|
|
|
"reason",
|
2021-06-24 09:43:41 -04:00
|
|
|
|
]));
|
2020-10-26 18:22:03 -04:00
|
|
|
|
}
|
2020-09-05 10:39:25 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class MessageEvent extends Event {
|
2021-05-22 12:08:24 -04:00
|
|
|
|
get source() {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-05 10:39:25 -04:00
|
|
|
|
constructor(type, eventInitDict) {
|
|
|
|
|
super(type, {
|
|
|
|
|
bubbles: eventInitDict?.bubbles ?? false,
|
|
|
|
|
cancelable: eventInitDict?.cancelable ?? false,
|
|
|
|
|
composed: eventInitDict?.composed ?? false,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.data = eventInitDict?.data ?? null;
|
2021-06-21 13:53:52 -04:00
|
|
|
|
this.ports = eventInitDict?.ports ?? [];
|
2020-09-05 10:39:25 -04:00
|
|
|
|
this.origin = eventInitDict?.origin ?? "";
|
|
|
|
|
this.lastEventId = eventInitDict?.lastEventId ?? "";
|
|
|
|
|
}
|
2020-10-26 18:22:03 -04:00
|
|
|
|
|
2021-06-25 03:19:18 -04:00
|
|
|
|
[Symbol.for("Deno.privateCustomInspect")](inspect) {
|
2021-06-24 09:43:41 -04:00
|
|
|
|
return inspect(buildFilteredPropertyInspectObject(this, [
|
2020-10-26 18:22:03 -04:00
|
|
|
|
...EVENT_PROPS,
|
|
|
|
|
"data",
|
|
|
|
|
"origin",
|
|
|
|
|
"lastEventId",
|
2021-06-24 09:43:41 -04:00
|
|
|
|
]));
|
2020-10-26 18:22:03 -04:00
|
|
|
|
}
|
2020-09-05 10:39:25 -04:00
|
|
|
|
}
|
|
|
|
|
|
2020-07-19 13:49:44 -04:00
|
|
|
|
class CustomEvent extends Event {
|
|
|
|
|
#detail = null;
|
|
|
|
|
|
|
|
|
|
constructor(type, eventInitDict = {}) {
|
|
|
|
|
super(type, eventInitDict);
|
2021-02-13 09:58:12 -05:00
|
|
|
|
webidl.requiredArguments(arguments.length, 1, {
|
|
|
|
|
prefix: "Failed to construct 'CustomEvent'",
|
|
|
|
|
});
|
2020-07-19 13:49:44 -04:00
|
|
|
|
const { detail } = eventInitDict;
|
|
|
|
|
this.#detail = detail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get detail() {
|
|
|
|
|
return this.#detail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get [Symbol.toStringTag]() {
|
|
|
|
|
return "CustomEvent";
|
|
|
|
|
}
|
2020-10-26 18:22:03 -04:00
|
|
|
|
|
2021-06-25 03:19:18 -04:00
|
|
|
|
[Symbol.for("Deno.privateCustomInspect")](inspect) {
|
2021-06-24 09:43:41 -04:00
|
|
|
|
return inspect(buildFilteredPropertyInspectObject(this, [
|
2020-10-26 18:22:03 -04:00
|
|
|
|
...EVENT_PROPS,
|
|
|
|
|
"detail",
|
2021-06-24 09:43:41 -04:00
|
|
|
|
]));
|
2020-10-26 18:22:03 -04:00
|
|
|
|
}
|
2020-07-19 13:49:44 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Reflect.defineProperty(CustomEvent.prototype, "detail", {
|
|
|
|
|
enumerable: true,
|
|
|
|
|
});
|
|
|
|
|
|
2020-09-18 10:01:50 -04:00
|
|
|
|
// 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;
|
|
|
|
|
}
|
2020-10-26 18:22:03 -04:00
|
|
|
|
|
2021-06-25 03:19:18 -04:00
|
|
|
|
[Symbol.for("Deno.privateCustomInspect")](inspect) {
|
2021-06-24 09:43:41 -04:00
|
|
|
|
return inspect(buildFilteredPropertyInspectObject(this, [
|
2020-10-26 18:22:03 -04:00
|
|
|
|
...EVENT_PROPS,
|
|
|
|
|
"lengthComputable",
|
|
|
|
|
"loaded",
|
|
|
|
|
"total",
|
2021-06-24 09:43:41 -04:00
|
|
|
|
]));
|
2020-10-26 18:22:03 -04:00
|
|
|
|
}
|
2020-09-18 10:01:50 -04:00
|
|
|
|
}
|
|
|
|
|
|
2021-06-21 13:53:52 -04:00
|
|
|
|
const _eventHandlers = Symbol("eventHandlers");
|
|
|
|
|
|
|
|
|
|
function makeWrappedHandler(handler) {
|
|
|
|
|
function wrappedHandler(...args) {
|
|
|
|
|
if (typeof wrappedHandler.handler !== "function") {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
return wrappedHandler.handler.call(this, ...args);
|
|
|
|
|
}
|
|
|
|
|
wrappedHandler.handler = handler;
|
|
|
|
|
return wrappedHandler;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO(benjamingr) reuse this here and websocket where possible
|
|
|
|
|
function defineEventHandler(emitter, name, init) {
|
|
|
|
|
// HTML specification section 8.1.5.1
|
|
|
|
|
Object.defineProperty(emitter, `on${name}`, {
|
|
|
|
|
get() {
|
|
|
|
|
return this[_eventHandlers]?.get(name)?.handler;
|
|
|
|
|
},
|
|
|
|
|
set(value) {
|
|
|
|
|
if (!this[_eventHandlers]) {
|
|
|
|
|
this[_eventHandlers] = new Map();
|
|
|
|
|
}
|
|
|
|
|
let handlerWrapper = this[_eventHandlers]?.get(name);
|
|
|
|
|
if (handlerWrapper) {
|
|
|
|
|
console.log("foo");
|
|
|
|
|
handlerWrapper.handler = value;
|
|
|
|
|
} else {
|
|
|
|
|
handlerWrapper = makeWrappedHandler(value);
|
|
|
|
|
this.addEventListener(name, handlerWrapper);
|
|
|
|
|
init?.(this);
|
|
|
|
|
}
|
|
|
|
|
this[_eventHandlers].set(name, handlerWrapper);
|
|
|
|
|
},
|
|
|
|
|
configurable: true,
|
|
|
|
|
enumerable: true,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-19 13:49:44 -04:00
|
|
|
|
window.Event = Event;
|
|
|
|
|
window.EventTarget = EventTarget;
|
|
|
|
|
window.ErrorEvent = ErrorEvent;
|
2020-09-05 10:39:25 -04:00
|
|
|
|
window.CloseEvent = CloseEvent;
|
|
|
|
|
window.MessageEvent = MessageEvent;
|
2020-07-19 13:49:44 -04:00
|
|
|
|
window.CustomEvent = CustomEvent;
|
2020-09-18 10:01:50 -04:00
|
|
|
|
window.ProgressEvent = ProgressEvent;
|
2020-08-07 10:55:02 -04:00
|
|
|
|
window.dispatchEvent = EventTarget.prototype.dispatchEvent;
|
|
|
|
|
window.addEventListener = EventTarget.prototype.addEventListener;
|
|
|
|
|
window.removeEventListener = EventTarget.prototype.removeEventListener;
|
2020-07-19 13:49:44 -04:00
|
|
|
|
window.__bootstrap.eventTarget = {
|
2021-03-01 05:31:13 -05:00
|
|
|
|
EventTarget,
|
2020-07-19 13:49:44 -04:00
|
|
|
|
setEventTargetData,
|
|
|
|
|
};
|
2021-01-05 16:43:25 -05:00
|
|
|
|
window.__bootstrap.event = {
|
|
|
|
|
setIsTrusted,
|
2021-05-22 12:08:24 -04:00
|
|
|
|
setTarget,
|
2021-06-21 13:53:52 -04:00
|
|
|
|
defineEventHandler,
|
2021-01-05 16:43:25 -05:00
|
|
|
|
};
|
2020-07-19 13:49:44 -04:00
|
|
|
|
})(this);
|