buninu-browser 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/audio/playback-buffer.js +27 -0
- package/lib/audio/pulse-client.js +18 -8
- package/lib/cdp-server.js +53 -14
- package/lib/happy-dom/document-base.js +33 -0
- package/lib/happy-dom/parser.js +20 -6
- package/lib/happy-dom/selector-matcher.js +2 -2
- package/lib/happy-dom/selector-pseudo-adapter.js +33 -0
- package/lib/happy-dom/svg-attribute-adapter.js +50 -0
- package/lib/headless-shell.js +12 -6
- package/lib/input/hit-test.js +67 -19
- package/lib/layout/text-layout.js +289 -131
- package/lib/media/byte-pipe.js +2 -2
- package/lib/media/position-audio.js +10 -1
- package/lib/media/source-snapshots.js +126 -0
- package/lib/media/video-keyframe.js +42 -0
- package/lib/media/webm-stream.js +106 -0
- package/lib/network/network-service.js +268 -120
- package/lib/paint/display-list.js +22 -27
- package/lib/paint/frame-cache.js +25 -0
- package/lib/paint/frame.js +42 -13
- package/lib/paint/paragraph-text.js +24 -0
- package/lib/paint/raster-readback.js +41 -0
- package/lib/paint/rounded-rect.js +82 -0
- package/lib/paint/shared-frame-pool.js +105 -0
- package/lib/render-tree/index.js +40 -6
- package/lib/render-tree/svg.js +15 -7
- package/lib/renderer/bindings.js +244 -82
- package/lib/renderer/broadcast-channel.js +76 -0
- package/lib/renderer/canvas-2d.js +257 -14
- package/lib/renderer/canvas-text.js +120 -0
- package/lib/renderer/compositor.js +27 -10
- package/lib/renderer/custom-element-reactions.js +42 -0
- package/lib/renderer/font-loading.js +227 -0
- package/lib/renderer/history.js +42 -0
- package/lib/renderer/host.js +36 -5
- package/lib/renderer/image-loading.js +27 -0
- package/lib/renderer/image-resources.js +65 -0
- package/lib/renderer/interfaces.js +28 -0
- package/lib/renderer/keyboard-input.js +12 -0
- package/lib/renderer/layout-observers.js +236 -0
- package/lib/renderer/main.js +33 -5
- package/lib/renderer/media-playback-events.js +13 -0
- package/lib/renderer/media-source.js +126 -55
- package/lib/renderer/page-realm.js +278 -147
- package/lib/renderer/page-renderer.js +840 -301
- package/lib/renderer/piano-input.js +22 -0
- package/lib/renderer/pointer-move.js +29 -0
- package/lib/renderer/prepared-script-cache.js +50 -0
- package/lib/renderer/rendered-text.js +108 -0
- package/lib/renderer/rewrite-worker.js +4 -3
- package/lib/renderer/script-rewrite.js +13 -2
- package/lib/renderer/scroll-into-view.js +28 -0
- package/lib/renderer/slot-assignment.js +60 -0
- package/lib/renderer/video-presentation.js +145 -0
- package/lib/renderer/workers.js +2 -2
- package/lib/style/computed-style.js +632 -108
- package/lib/style/selector-traits.js +49 -5
- package/package.json +1 -1
- package/test/broadcast-channel.test.js +26 -0
- package/test/canvas-2d.test.js +83 -2
- package/test/canvas-paint.test.js +9 -0
- package/test/canvas-text.test.js +41 -0
- package/test/cdp-stream.test.js +56 -0
- package/test/compositor.test.js +38 -0
- package/test/computed-style-binding.test.js +151 -0
- package/test/computed-style.test.js +279 -1
- package/test/display-list.test.js +26 -0
- package/test/document-base.test.js +87 -0
- package/test/dom-platform.test.js +461 -0
- package/test/download.test.js +8 -0
- package/test/fetch-lifecycle.test.js +161 -0
- package/test/fixtures/canvas-paint.js +117 -0
- package/test/fixtures/canvas-text.js +42 -0
- package/test/fixtures/image-lifetime.js +90 -0
- package/test/fixtures/media-seek-snapshot.js +77 -0
- package/test/fixtures/mse-audio-chirp.webm +0 -0
- package/test/fixtures/mse-switch-large.mp4 +0 -0
- package/test/fixtures/mse-switch-small.mp4 +0 -0
- package/test/fixtures/renderer-memory.js +110 -0
- package/test/fixtures/summary-style.js +28 -0
- package/test/font-loading.test.js +97 -0
- package/test/grid.test.js +58 -0
- package/test/headless-shell.test.js +22 -0
- package/test/hit-test.test.js +21 -1
- package/test/image-lifetime.test.js +12 -0
- package/test/image-resources.test.js +94 -0
- package/test/local-media.test.js +84 -0
- package/test/media-playback-events.test.js +22 -0
- package/test/media-seek-snapshot.test.js +11 -0
- package/test/media-source-config.test.js +98 -0
- package/test/media-source-webm-accounting.test.js +98 -0
- package/test/media-source-webm.test.js +108 -0
- package/test/navigation.test.js +1 -0
- package/test/network-service.test.js +388 -0
- package/test/page-scripts.test.js +160 -2
- package/test/paint-frame.test.js +103 -0
- package/test/paint-paragraph-cache.test.js +87 -0
- package/test/piano-input.test.js +44 -0
- package/test/playback-buffer.test.js +31 -0
- package/test/position-audio.test.js +24 -0
- package/test/positioning.test.js +22 -0
- package/test/prepared-script-cache.test.js +45 -0
- package/test/pulse-client.test.js +27 -5
- package/test/raster-readback.test.js +40 -0
- package/test/render-tree.test.js +28 -0
- package/test/renderer-memory.test.js +11 -0
- package/test/selector-traits.test.js +24 -0
- package/test/shared-frame-pool.test.js +63 -0
- package/test/style-sheets.test.js +51 -0
- package/test/svg.test.js +29 -0
- package/test/text-layout.test.js +168 -0
- package/test/video-keyframe.test.js +61 -0
- package/test/video-presentation.test.js +136 -0
- package/tools/browser-lab.js +78 -0
- package/tools/browser-soak.js +75 -0
- package/tools/network-reference.js +49 -0
- package/tools/process-memory.js +38 -0
- package/tools/script-benchmark.js +25 -0
package/lib/renderer/bindings.js
CHANGED
|
@@ -10,12 +10,21 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Coverage is the commonly used subset of DOM, not the full WebIDL surface.
|
|
12
12
|
*/
|
|
13
|
+
import { renderedText, setRenderedText } from "./rendered-text.js";
|
|
14
|
+
import { assignedSlot, slotAssignedNodes, assignSlotNodes } from "./slot-assignment.js";
|
|
15
|
+
import { scrollIntoViewOptions, scrollAlignmentDelta } from "./scroll-into-view.js";
|
|
16
|
+
import { documentBaseURL } from "../happy-dom/document-base.js";
|
|
17
|
+
import { resetCanvas } from "./canvas-2d.js";
|
|
13
18
|
|
|
14
19
|
const CONSTRUCT = Symbol("binding construction key");
|
|
20
|
+
const isCanvasSizeAttribute = (node, name, namespace = null) => !namespace
|
|
21
|
+
&& node.namespaceURI === "http://www.w3.org/1999/xhtml" && node.tagName === "CANVAS"
|
|
22
|
+
&& (name === "width" || name === "height");
|
|
15
23
|
const REFLECTED_PROPERTIES = [
|
|
16
24
|
"value", "checked", "disabled", "type", "name", "href", "src", "alt", "title",
|
|
17
25
|
"placeholder", "selected", "readOnly", "required", "min", "max", "step", "rel", "target",
|
|
18
26
|
"htmlFor", "lang", "dir", "tabIndex", "hidden", "defaultValue", "action", "method",
|
|
27
|
+
"controls", "autoplay", "loop", "playsInline", "preload", "poster",
|
|
19
28
|
];
|
|
20
29
|
|
|
21
30
|
/** Per-document state shared by that document's wrappers. */
|
|
@@ -41,6 +50,9 @@ export class BindingRealm {
|
|
|
41
50
|
textPrototype = null;
|
|
42
51
|
characterDataPrototype = null;
|
|
43
52
|
nodePrototype = null;
|
|
53
|
+
datasetPrototype = null;
|
|
54
|
+
wrapValidity = null;
|
|
55
|
+
documentPrototype = null;
|
|
44
56
|
|
|
45
57
|
/**
|
|
46
58
|
* @param {object} window the Happy DOM window of the document
|
|
@@ -87,6 +99,7 @@ export class BindingRealm {
|
|
|
87
99
|
if (Binding === TextBinding && this.textPrototype) Object.setPrototypeOf(wrapper, this.textPrototype);
|
|
88
100
|
if (Binding === CharacterDataBinding && this.characterDataPrototype) Object.setPrototypeOf(wrapper, this.characterDataPrototype);
|
|
89
101
|
if (Binding === NodeBinding && this.nodePrototype) Object.setPrototypeOf(wrapper, this.nodePrototype);
|
|
102
|
+
if (Binding === DocumentBinding && this.documentPrototype) Object.setPrototypeOf(wrapper, this.documentPrototype);
|
|
90
103
|
this.#wrappers.set(node, wrapper);
|
|
91
104
|
}
|
|
92
105
|
return wrapper;
|
|
@@ -209,7 +222,7 @@ export class BindingRealm {
|
|
|
209
222
|
if (!wrapper) {
|
|
210
223
|
wrapper = new EventBinding(CONSTRUCT, event, this, detail);
|
|
211
224
|
if (this.eventPrototype) {
|
|
212
|
-
const prototype = this.eventPrototypes?.get(event.constructor?.name) ?? this.eventPrototype;
|
|
225
|
+
const prototype = this.eventPrototypes?.get(this.#messages.has(event) ? "MessageEvent" : event.constructor?.name) ?? this.eventPrototype;
|
|
213
226
|
Object.setPrototypeOf(wrapper, prototype);
|
|
214
227
|
// isTrusted is [LegacyUnforgeable]: an own accessor of every event.
|
|
215
228
|
const isTrusted = Object.getOwnPropertyDescriptor(EventBinding.prototype, "isTrusted");
|
|
@@ -347,15 +360,7 @@ class NodeBinding {
|
|
|
347
360
|
|
|
348
361
|
/** The document base URL (HTML §2.4.1): the first <base href>, else the document's URL. */
|
|
349
362
|
get baseURI() {
|
|
350
|
-
|
|
351
|
-
const url = document?.URL ?? "about:blank";
|
|
352
|
-
const href = document?.querySelector?.("base[href]")?.getAttribute("href");
|
|
353
|
-
if (href == null) return url;
|
|
354
|
-
try {
|
|
355
|
-
return new URL(href, url).href;
|
|
356
|
-
} catch {
|
|
357
|
-
return url;
|
|
358
|
-
}
|
|
363
|
+
return documentBaseURL(this.#node);
|
|
359
364
|
}
|
|
360
365
|
|
|
361
366
|
get nodeValue() {
|
|
@@ -553,7 +558,9 @@ class CharacterDataBinding extends NodeBinding {
|
|
|
553
558
|
}
|
|
554
559
|
}
|
|
555
560
|
|
|
556
|
-
class TextBinding extends CharacterDataBinding {
|
|
561
|
+
class TextBinding extends CharacterDataBinding {
|
|
562
|
+
get assignedSlot() { return realmOf(this).wrap(assignedSlot(nodeOf(this))); }
|
|
563
|
+
}
|
|
557
564
|
|
|
558
565
|
/** Methods shared by elements, documents and fragments (ParentNode). */
|
|
559
566
|
class ParentBinding extends NodeBinding {
|
|
@@ -634,6 +641,9 @@ class ShadowRootBinding extends FragmentBinding {
|
|
|
634
641
|
return nodeOf(this).mode;
|
|
635
642
|
}
|
|
636
643
|
|
|
644
|
+
get delegatesFocus() { return nodeOf(this).delegatesFocus; }
|
|
645
|
+
get slotAssignment() { return nodeOf(this).slotAssignment; }
|
|
646
|
+
|
|
637
647
|
get innerHTML() {
|
|
638
648
|
return nodeOf(this).innerHTML;
|
|
639
649
|
}
|
|
@@ -649,20 +659,49 @@ class ElementBinding extends ParentBinding {
|
|
|
649
659
|
#classList = null;
|
|
650
660
|
#dataset = null;
|
|
651
661
|
#attributes = null;
|
|
662
|
+
#validity = null;
|
|
663
|
+
#clickInProgress = false;
|
|
652
664
|
|
|
653
665
|
get tagName() {
|
|
654
666
|
return nodeOf(this).tagName;
|
|
655
667
|
}
|
|
656
668
|
|
|
657
669
|
attachShadow(init) {
|
|
658
|
-
const mode = init?.mode
|
|
659
|
-
|
|
670
|
+
const mode = String(init?.mode);
|
|
671
|
+
if (mode !== "open" && mode !== "closed") throw new TypeError("Shadow root mode must be open or closed");
|
|
672
|
+
const slotAssignment = String(init?.slotAssignment ?? "named");
|
|
673
|
+
if (slotAssignment !== "named" && slotAssignment !== "manual") throw new TypeError("Invalid slot assignment mode");
|
|
674
|
+
// Happy DOM spells this internal option delegateFocus (without the s).
|
|
675
|
+
return realmOf(this).wrap(nodeOf(this).attachShadow({ mode, slotAssignment, delegateFocus: Boolean(init?.delegatesFocus) }));
|
|
660
676
|
}
|
|
661
677
|
|
|
662
678
|
get shadowRoot() {
|
|
663
679
|
return realmOf(this).wrap(nodeOf(this).shadowRoot);
|
|
664
680
|
}
|
|
665
681
|
|
|
682
|
+
get assignedSlot() { return realmOf(this).wrap(assignedSlot(nodeOf(this))); }
|
|
683
|
+
|
|
684
|
+
get slot() { return nodeOf(this).getAttribute("slot") ?? ""; }
|
|
685
|
+
set slot(value) { this.setAttribute("slot", String(value)); }
|
|
686
|
+
|
|
687
|
+
assignedNodes(options = {}) {
|
|
688
|
+
const realm = realmOf(this);
|
|
689
|
+
return slotAssignedNodes(nodeOf(this), Boolean(options?.flatten)).map(node => realm.wrap(node));
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
assignedElements(options = {}) {
|
|
693
|
+
const realm = realmOf(this);
|
|
694
|
+
return slotAssignedNodes(nodeOf(this), Boolean(options?.flatten)).filter(node => node.nodeType === 1).map(node => realm.wrap(node));
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
assign(...nodes) {
|
|
698
|
+
const realm = realmOf(this);
|
|
699
|
+
assignSlotNodes(nodeOf(this), nodes.map(node => nodeOf(node)), slot => {
|
|
700
|
+
queueMicrotask(() => slot.dispatchEvent(new realm.window.Event("slotchange", { bubbles: true })));
|
|
701
|
+
});
|
|
702
|
+
touch(this);
|
|
703
|
+
}
|
|
704
|
+
|
|
666
705
|
// HTMLIFrameElement: its document's window and, when same-origin, its
|
|
667
706
|
// document (see messaging.js).
|
|
668
707
|
get contentWindow() {
|
|
@@ -746,6 +785,9 @@ class ElementBinding extends ParentBinding {
|
|
|
746
785
|
return nodeOf(this).localName;
|
|
747
786
|
}
|
|
748
787
|
|
|
788
|
+
get namespaceURI() { return nodeOf(this).namespaceURI; }
|
|
789
|
+
get prefix() { return nodeOf(this).prefix; }
|
|
790
|
+
|
|
749
791
|
get id() {
|
|
750
792
|
return nodeOf(this).id;
|
|
751
793
|
}
|
|
@@ -782,6 +824,11 @@ class ElementBinding extends ParentBinding {
|
|
|
782
824
|
return this.#style;
|
|
783
825
|
}
|
|
784
826
|
|
|
827
|
+
// ElementCSSInlineStyle is [PutForwards=cssText], not assignment-readonly.
|
|
828
|
+
set style(value) {
|
|
829
|
+
this.style.cssText = value;
|
|
830
|
+
}
|
|
831
|
+
|
|
785
832
|
get dataset() {
|
|
786
833
|
this.#dataset ??= datasetBinding(nodeOf(this), realmOf(this));
|
|
787
834
|
return this.#dataset;
|
|
@@ -833,18 +880,23 @@ class ElementBinding extends ParentBinding {
|
|
|
833
880
|
const attribute = element.getAttributeNode(String(name));
|
|
834
881
|
if (!attribute) return null;
|
|
835
882
|
const ownerElement = this;
|
|
883
|
+
const setValue = value => {
|
|
884
|
+
attribute.value = String(value);
|
|
885
|
+
if (attribute.ownerElement === element && isCanvasSizeAttribute(element, attribute.localName, attribute.namespaceURI)) resetCanvas(element);
|
|
886
|
+
touch(ownerElement);
|
|
887
|
+
};
|
|
836
888
|
return {
|
|
837
889
|
get name() { return attribute.name; },
|
|
838
890
|
get localName() { return attribute.localName; },
|
|
839
891
|
get namespaceURI() { return attribute.namespaceURI; },
|
|
840
892
|
get prefix() { return attribute.prefix; },
|
|
841
893
|
get value() { return attribute.value; },
|
|
842
|
-
set value(value) {
|
|
894
|
+
set value(value) { setValue(value); },
|
|
843
895
|
get nodeName() { return attribute.name; },
|
|
844
896
|
get nodeValue() { return attribute.value; },
|
|
845
|
-
set nodeValue(value) {
|
|
897
|
+
set nodeValue(value) { setValue(value ?? ""); },
|
|
846
898
|
get textContent() { return attribute.value; },
|
|
847
|
-
set textContent(value) {
|
|
899
|
+
set textContent(value) { setValue(value ?? ""); },
|
|
848
900
|
ownerElement,
|
|
849
901
|
specified: true,
|
|
850
902
|
nodeType: 2,
|
|
@@ -855,8 +907,10 @@ class ElementBinding extends ParentBinding {
|
|
|
855
907
|
const node = nodeOf(this);
|
|
856
908
|
const attribute = String(name);
|
|
857
909
|
const next = String(value);
|
|
858
|
-
|
|
910
|
+
const resetsCanvas = isCanvasSizeAttribute(node, attribute.toLowerCase());
|
|
911
|
+
if (node.getAttribute(attribute) !== next || resetsCanvas) {
|
|
859
912
|
node.setAttribute(attribute, next);
|
|
913
|
+
if (resetsCanvas) resetCanvas(node);
|
|
860
914
|
touch(this);
|
|
861
915
|
}
|
|
862
916
|
}
|
|
@@ -866,6 +920,7 @@ class ElementBinding extends ParentBinding {
|
|
|
866
920
|
const attribute = String(name);
|
|
867
921
|
if (node.hasAttribute(attribute)) {
|
|
868
922
|
node.removeAttribute(attribute);
|
|
923
|
+
if (isCanvasSizeAttribute(node, attribute.toLowerCase())) resetCanvas(node);
|
|
869
924
|
touch(this);
|
|
870
925
|
}
|
|
871
926
|
}
|
|
@@ -888,17 +943,25 @@ class ElementBinding extends ParentBinding {
|
|
|
888
943
|
}
|
|
889
944
|
|
|
890
945
|
setAttributeNS(namespace, qualifiedName, value) {
|
|
891
|
-
nodeOf(this)
|
|
946
|
+
const node = nodeOf(this), ns = namespace === undefined ? null : namespace && String(namespace), name = String(qualifiedName);
|
|
947
|
+
node.setAttributeNS(ns, name, String(value));
|
|
948
|
+
if (isCanvasSizeAttribute(node, name, ns)) resetCanvas(node);
|
|
892
949
|
touch(this);
|
|
893
950
|
}
|
|
894
951
|
|
|
895
952
|
removeAttributeNS(namespace, localName) {
|
|
896
|
-
nodeOf(this)
|
|
953
|
+
const node = nodeOf(this), ns = namespace === undefined ? null : namespace && String(namespace), name = String(localName);
|
|
954
|
+
const existed = node.hasAttributeNS(ns, name);
|
|
955
|
+
node.removeAttributeNS(ns, name);
|
|
956
|
+
if (existed && isCanvasSizeAttribute(node, name, ns)) resetCanvas(node);
|
|
897
957
|
touch(this);
|
|
898
958
|
}
|
|
899
959
|
|
|
900
960
|
toggleAttribute(name, force = undefined) {
|
|
901
|
-
const
|
|
961
|
+
const node = nodeOf(this), attribute = String(name);
|
|
962
|
+
const existed = node.hasAttribute(attribute);
|
|
963
|
+
const result = node.toggleAttribute(attribute, force === undefined ? undefined : Boolean(force));
|
|
964
|
+
if (existed !== result && isCanvasSizeAttribute(node, attribute.toLowerCase())) resetCanvas(node);
|
|
902
965
|
touch(this);
|
|
903
966
|
return result;
|
|
904
967
|
}
|
|
@@ -922,11 +985,13 @@ class ElementBinding extends ParentBinding {
|
|
|
922
985
|
}
|
|
923
986
|
|
|
924
987
|
get innerText() {
|
|
925
|
-
|
|
988
|
+
const node = nodeOf(this);
|
|
989
|
+
const realm = realmOf(this);
|
|
990
|
+
return node.ownerDocument !== realm.window.document ? node.textContent : renderedText(node, realm.hooks.computedStyle);
|
|
926
991
|
}
|
|
927
992
|
|
|
928
993
|
set innerText(value) {
|
|
929
|
-
nodeOf(this)
|
|
994
|
+
setRenderedText(nodeOf(this), value);
|
|
930
995
|
touch(this);
|
|
931
996
|
}
|
|
932
997
|
|
|
@@ -992,11 +1057,18 @@ class ElementBinding extends ParentBinding {
|
|
|
992
1057
|
click() {
|
|
993
1058
|
const realm = realmOf(this);
|
|
994
1059
|
const element = nodeOf(this);
|
|
995
|
-
|
|
996
|
-
element.
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1060
|
+
if (this.#clickInProgress || element.matches(":disabled")) return;
|
|
1061
|
+
const anchor = element.closest?.("a[href]");
|
|
1062
|
+
const Event = realm.window.PointerEvent ?? realm.window.MouseEvent;
|
|
1063
|
+
const event = new Event("click", { bubbles: true, cancelable: true, composed: true });
|
|
1064
|
+
const activationState = realm.hooks.activationState?.(element);
|
|
1065
|
+
this.#clickInProgress = true;
|
|
1066
|
+
try {
|
|
1067
|
+
element.dispatchEvent(event);
|
|
1068
|
+
if (!event.defaultPrevented && anchor?.hasAttribute("href")) realm.hooks.activateLink?.(anchor);
|
|
1069
|
+
} finally {
|
|
1070
|
+
this.#clickInProgress = false;
|
|
1071
|
+
realm.hooks.activationChanged?.(activationState);
|
|
1000
1072
|
}
|
|
1001
1073
|
}
|
|
1002
1074
|
|
|
@@ -1010,21 +1082,42 @@ class ElementBinding extends ParentBinding {
|
|
|
1010
1082
|
const form = nodeOf(this);
|
|
1011
1083
|
if (form.tagName !== "FORM") return;
|
|
1012
1084
|
const realSubmitter = submitter == null ? null : nodeOf(submitter);
|
|
1085
|
+
if (realSubmitter && !((realSubmitter.tagName === "BUTTON" && realSubmitter.type === "submit")
|
|
1086
|
+
|| (realSubmitter.tagName === "INPUT" && ["submit", "image"].includes(realSubmitter.type)))) {
|
|
1087
|
+
throw new TypeError("The submitter must be a submit button");
|
|
1088
|
+
}
|
|
1013
1089
|
if (realSubmitter && realSubmitter.form !== form) throw new DOMException("Submitter is not owned by this form", "NotFoundError");
|
|
1014
|
-
if (!form.checkValidity()) return;
|
|
1090
|
+
if (!form.noValidate && !realSubmitter?.formNoValidate && !form.checkValidity()) return;
|
|
1015
1091
|
const event = new realm.window.SubmitEvent("submit", {
|
|
1016
|
-
bubbles: true, cancelable: true, submitter: realSubmitter
|
|
1092
|
+
bubbles: true, cancelable: true, submitter: realSubmitter,
|
|
1017
1093
|
});
|
|
1018
1094
|
form.dispatchEvent(event);
|
|
1019
1095
|
if (!event.defaultPrevented) realm.hooks.submitForm?.(form, realSubmitter);
|
|
1020
1096
|
}
|
|
1021
1097
|
|
|
1098
|
+
get validity() { return this.#validity ??= realmOf(this).wrapValidity(nodeOf(this).validity); }
|
|
1099
|
+
|
|
1100
|
+
setCustomValidity(message) {
|
|
1101
|
+
if (!arguments.length) throw new TypeError("setCustomValidity requires a message");
|
|
1102
|
+
nodeOf(this).setCustomValidity(String(message));
|
|
1103
|
+
touch(this, [":state"]);
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
checkValidity() { return nodeOf(this).checkValidity(); }
|
|
1107
|
+
reportValidity() { return nodeOf(this).reportValidity(); }
|
|
1108
|
+
|
|
1022
1109
|
focus() {
|
|
1023
|
-
nodeOf(this)
|
|
1110
|
+
const node = nodeOf(this);
|
|
1111
|
+
const previous = node.ownerDocument.activeElement;
|
|
1112
|
+
node.focus();
|
|
1113
|
+
if (node.ownerDocument.activeElement !== previous) touch(this, [":focus", ":focus-visible"]);
|
|
1024
1114
|
}
|
|
1025
1115
|
|
|
1026
1116
|
blur() {
|
|
1027
|
-
nodeOf(this)
|
|
1117
|
+
const node = nodeOf(this);
|
|
1118
|
+
const previous = node.ownerDocument.activeElement;
|
|
1119
|
+
node.blur();
|
|
1120
|
+
if (node.ownerDocument.activeElement !== previous) touch(this, [":focus", ":focus-visible"]);
|
|
1028
1121
|
}
|
|
1029
1122
|
|
|
1030
1123
|
getBoundingClientRect() {
|
|
@@ -1033,7 +1126,10 @@ class ElementBinding extends ParentBinding {
|
|
|
1033
1126
|
}
|
|
1034
1127
|
|
|
1035
1128
|
getClientRects() {
|
|
1036
|
-
|
|
1129
|
+
const realm = realmOf(this), node = nodeOf(this);
|
|
1130
|
+
if (!node.isConnected || realm.hooks.computedStyle?.(node)?.display === "contents") return [];
|
|
1131
|
+
const rect = realm.hooks.boundsOf(node);
|
|
1132
|
+
return rect ? [domRect(rect)] : [];
|
|
1037
1133
|
}
|
|
1038
1134
|
|
|
1039
1135
|
get offsetWidth() {
|
|
@@ -1211,15 +1307,17 @@ class ElementBinding extends ParentBinding {
|
|
|
1211
1307
|
return Math.max(this.offsetHeight, root ? realmOf(this).hooks.viewport().height : 0);
|
|
1212
1308
|
}
|
|
1213
1309
|
|
|
1214
|
-
scrollIntoView() {
|
|
1310
|
+
scrollIntoView(options = undefined) {
|
|
1215
1311
|
const realm = realmOf(this);
|
|
1312
|
+
const normalized = scrollIntoViewOptions(options);
|
|
1216
1313
|
if (realm.hooks.scrollIntoView) {
|
|
1217
|
-
realm.hooks.scrollIntoView(nodeOf(this));
|
|
1314
|
+
realm.hooks.scrollIntoView(nodeOf(this), normalized);
|
|
1218
1315
|
return;
|
|
1219
1316
|
}
|
|
1220
1317
|
const rect = this.getBoundingClientRect();
|
|
1221
|
-
const { scrollX, scrollY } = realm.hooks.viewport();
|
|
1222
|
-
realm.hooks.scrollTo(scrollX
|
|
1318
|
+
const { scrollX, scrollY, width, height } = realm.hooks.viewport();
|
|
1319
|
+
realm.hooks.scrollTo(scrollX + scrollAlignmentDelta(rect.left, rect.width, width, normalized.inline),
|
|
1320
|
+
scrollY + scrollAlignmentDelta(rect.top, rect.height, height, normalized.block));
|
|
1223
1321
|
}
|
|
1224
1322
|
}
|
|
1225
1323
|
|
|
@@ -1266,7 +1364,7 @@ export const EVENT_HANDLER_NAMES = Object.freeze([
|
|
|
1266
1364
|
"click", "dblclick", "mousedown", "mouseup", "mousemove", "mouseover", "mouseout", "mouseenter", "mouseleave",
|
|
1267
1365
|
"keydown", "keyup", "keypress", "beforeinput", "input", "change", "submit", "reset", "focus", "blur", "load", "error",
|
|
1268
1366
|
"abort", "scroll", "resize", "wheel", "contextmenu", "touchstart", "touchend", "touchmove",
|
|
1269
|
-
"pointerdown", "pointerup", "pointermove", "animationend", "transitionend", "readystatechange",
|
|
1367
|
+
"pointerdown", "pointerup", "pointermove", "animationend", "transitionend", "readystatechange", "slotchange", "toggle", "invalid",
|
|
1270
1368
|
]);
|
|
1271
1369
|
|
|
1272
1370
|
/**
|
|
@@ -1310,7 +1408,16 @@ for (const property of REFLECTED_PROPERTIES) {
|
|
|
1310
1408
|
configurable: true,
|
|
1311
1409
|
enumerable: true,
|
|
1312
1410
|
get() {
|
|
1313
|
-
const
|
|
1411
|
+
const node = nodeOf(this);
|
|
1412
|
+
const value = node[property];
|
|
1413
|
+
if (typeof value === "string" && ["src", "href", "action", "poster"].includes(property)) {
|
|
1414
|
+
const attribute = node.getAttribute(property);
|
|
1415
|
+
if (property === "action" && !attribute) return node.ownerDocument.URL;
|
|
1416
|
+
if (attribute !== null) {
|
|
1417
|
+
try { return new URL(attribute, node.tagName === "BASE" ? node.ownerDocument.URL : documentBaseURL(node)).href; }
|
|
1418
|
+
catch { return attribute; }
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1314
1421
|
return value === null || typeof value !== "object" ? value : String(value);
|
|
1315
1422
|
},
|
|
1316
1423
|
set(value) {
|
|
@@ -1358,12 +1465,32 @@ class DocumentBinding extends ParentBinding {
|
|
|
1358
1465
|
const realm = realmOf(this);
|
|
1359
1466
|
const node = nodeOf(this);
|
|
1360
1467
|
this.#implementation ??= Object.freeze({
|
|
1361
|
-
createHTMLDocument: (title = undefined) =>
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1468
|
+
createHTMLDocument: (title = undefined) => {
|
|
1469
|
+
const created = node.implementation.createHTMLDocument();
|
|
1470
|
+
const html = created.createElement("html");
|
|
1471
|
+
const head = created.createElement("head");
|
|
1472
|
+
const body = created.createElement("body");
|
|
1473
|
+
created.replaceChildren(created.implementation.createDocumentType("html", "", ""), html);
|
|
1474
|
+
html.append(head, body);
|
|
1475
|
+
if (title !== undefined) {
|
|
1476
|
+
const titleElement = created.createElement("title");
|
|
1477
|
+
titleElement.textContent = String(title);
|
|
1478
|
+
head.appendChild(titleElement);
|
|
1479
|
+
}
|
|
1480
|
+
return realm.wrap(created);
|
|
1481
|
+
},
|
|
1482
|
+
createDocument: (namespace, qualifiedName, doctype = null) => {
|
|
1483
|
+
const created = new realm.window.XMLDocument();
|
|
1484
|
+
const uri = namespace === null || namespace === undefined ? null : String(namespace);
|
|
1485
|
+
const name = qualifiedName === null || qualifiedName === undefined ? "" : String(qualifiedName);
|
|
1486
|
+
if (doctype !== null) created.appendChild(requireNode(doctype));
|
|
1487
|
+
if (name) created.appendChild(created.createElementNS(uri, name));
|
|
1488
|
+
Object.defineProperty(created, "contentType", {
|
|
1489
|
+
value: uri === "http://www.w3.org/1999/xhtml" ? "application/xhtml+xml"
|
|
1490
|
+
: uri === "http://www.w3.org/2000/svg" ? "image/svg+xml" : "application/xml", configurable: true,
|
|
1491
|
+
});
|
|
1492
|
+
return realm.wrap(created);
|
|
1493
|
+
},
|
|
1367
1494
|
createDocumentType: (qualifiedName, publicId, systemId) => realm.wrap(node.implementation.createDocumentType(
|
|
1368
1495
|
String(qualifiedName), String(publicId), String(systemId))),
|
|
1369
1496
|
hasFeature: () => true,
|
|
@@ -1375,13 +1502,16 @@ class DocumentBinding extends ParentBinding {
|
|
|
1375
1502
|
return realmOf(this).wrap(nodeOf(this).documentElement);
|
|
1376
1503
|
}
|
|
1377
1504
|
|
|
1505
|
+
get doctype() { return realmOf(this).wrap(nodeOf(this).doctype); }
|
|
1506
|
+
|
|
1378
1507
|
get head() {
|
|
1379
1508
|
return realmOf(this).wrap(nodeOf(this).head);
|
|
1380
1509
|
}
|
|
1381
1510
|
|
|
1382
1511
|
get body() {
|
|
1383
1512
|
const realm = realmOf(this);
|
|
1384
|
-
|
|
1513
|
+
const node = nodeOf(this);
|
|
1514
|
+
return realm.wrap(node === realm.window.document && realm.hooks.body ? realm.hooks.body() : node.body);
|
|
1385
1515
|
}
|
|
1386
1516
|
|
|
1387
1517
|
get title() {
|
|
@@ -1393,7 +1523,8 @@ class DocumentBinding extends ParentBinding {
|
|
|
1393
1523
|
}
|
|
1394
1524
|
|
|
1395
1525
|
get readyState() {
|
|
1396
|
-
|
|
1526
|
+
const realm = realmOf(this);
|
|
1527
|
+
return nodeOf(this) === realm.window.document ? realm.hooks.readyState?.() ?? "complete" : "complete";
|
|
1397
1528
|
}
|
|
1398
1529
|
|
|
1399
1530
|
// Page Visibility: the renderer only runs the page its client is showing.
|
|
@@ -1413,8 +1544,11 @@ class DocumentBinding extends ParentBinding {
|
|
|
1413
1544
|
return nodeOf(this).URL;
|
|
1414
1545
|
}
|
|
1415
1546
|
|
|
1547
|
+
get referrer() { return nodeOf(this).referrer ?? ""; }
|
|
1548
|
+
|
|
1416
1549
|
get location() {
|
|
1417
|
-
|
|
1550
|
+
const realm = realmOf(this);
|
|
1551
|
+
return nodeOf(this) === realm.window.document ? realm.hooks.pageGlobal?.()?.location ?? null : null;
|
|
1418
1552
|
}
|
|
1419
1553
|
|
|
1420
1554
|
get domain() {
|
|
@@ -1425,17 +1559,21 @@ class DocumentBinding extends ParentBinding {
|
|
|
1425
1559
|
return "UTF-8";
|
|
1426
1560
|
}
|
|
1427
1561
|
|
|
1562
|
+
get contentType() { return nodeOf(this).contentType; }
|
|
1563
|
+
|
|
1428
1564
|
get compatMode() {
|
|
1429
1565
|
return "CSS1Compat";
|
|
1430
1566
|
}
|
|
1431
1567
|
|
|
1432
1568
|
/** Non-HttpOnly cookies from the controller's jar; writes go back to the jar. */
|
|
1433
1569
|
get cookie() {
|
|
1434
|
-
|
|
1570
|
+
const realm = realmOf(this);
|
|
1571
|
+
return nodeOf(this) === realm.window.document ? realm.hooks.documentCookie?.() ?? "" : "";
|
|
1435
1572
|
}
|
|
1436
1573
|
|
|
1437
1574
|
set cookie(value) {
|
|
1438
|
-
realmOf(this)
|
|
1575
|
+
const realm = realmOf(this);
|
|
1576
|
+
if (nodeOf(this) === realm.window.document) realm.hooks.setDocumentCookie?.(String(value));
|
|
1439
1577
|
}
|
|
1440
1578
|
|
|
1441
1579
|
get activeElement() {
|
|
@@ -1463,7 +1601,7 @@ class DocumentBinding extends ParentBinding {
|
|
|
1463
1601
|
const sheets = [...nodeOf(this).querySelectorAll("style, link[rel~='stylesheet']")].map((element) => {
|
|
1464
1602
|
const href = element.tagName === "LINK" ? element.getAttribute("href") : null;
|
|
1465
1603
|
let resolved = null;
|
|
1466
|
-
if (href !== null) try { resolved = new URL(href, nodeOf(this).URL).href; } catch {}
|
|
1604
|
+
if (href !== null) try { resolved = new URL(href, element.baseURI || nodeOf(this).URL).href; } catch {}
|
|
1467
1605
|
return Object.freeze({
|
|
1468
1606
|
type: "text/css",
|
|
1469
1607
|
href: resolved,
|
|
@@ -1486,11 +1624,13 @@ class DocumentBinding extends ParentBinding {
|
|
|
1486
1624
|
}
|
|
1487
1625
|
|
|
1488
1626
|
get currentScript() {
|
|
1489
|
-
|
|
1627
|
+
const realm = realmOf(this);
|
|
1628
|
+
return nodeOf(this) === realm.window.document ? realm.wrap(realm.hooks.currentScript?.() ?? null) : null;
|
|
1490
1629
|
}
|
|
1491
1630
|
|
|
1492
1631
|
get defaultView() {
|
|
1493
|
-
|
|
1632
|
+
const realm = realmOf(this);
|
|
1633
|
+
return nodeOf(this) === realm.window.document ? realm.hooks.pageGlobal?.() ?? null : null;
|
|
1494
1634
|
}
|
|
1495
1635
|
|
|
1496
1636
|
get scripts() {
|
|
@@ -1709,6 +1849,8 @@ class EventBinding {
|
|
|
1709
1849
|
return this.#realm.wrap(this.#event.relatedTarget ?? null) ?? null;
|
|
1710
1850
|
}
|
|
1711
1851
|
|
|
1852
|
+
get submitter() { return this.#realm.wrap(this.#event.submitter ?? null); }
|
|
1853
|
+
|
|
1712
1854
|
getModifierState(key) {
|
|
1713
1855
|
const name = String(key);
|
|
1714
1856
|
return name === "Alt" ? this.altKey : name === "Control" ? this.ctrlKey : name === "Shift" ? this.shiftKey
|
|
@@ -1725,12 +1867,6 @@ class EventBinding {
|
|
|
1725
1867
|
this.initUIEvent(type, bubbles, cancelable, view, detail);
|
|
1726
1868
|
}
|
|
1727
1869
|
|
|
1728
|
-
// A plain Event has no detail of its own: assigning one (Polymer's fire()
|
|
1729
|
-
// does) makes an own property, as in browsers, instead of failing on the getter.
|
|
1730
|
-
set detail(value) {
|
|
1731
|
-
Object.defineProperty(this, "detail", { value, writable: true, configurable: true, enumerable: true });
|
|
1732
|
-
}
|
|
1733
|
-
|
|
1734
1870
|
/** Legacy initialisation of an event from document.createEvent() (DOM §2.5); not once dispatched. */
|
|
1735
1871
|
initEvent(type, bubbles = false, cancelable = false) {
|
|
1736
1872
|
if (this.#event.eventPhase !== 0) return;
|
|
@@ -1767,6 +1903,9 @@ class EventBinding {
|
|
|
1767
1903
|
return this.#event.code;
|
|
1768
1904
|
}
|
|
1769
1905
|
|
|
1906
|
+
get keyCode() { return this.#event.keyCode ?? 0; }
|
|
1907
|
+
get charCode() { return this.#event.charCode ?? 0; }
|
|
1908
|
+
|
|
1770
1909
|
get clientX() {
|
|
1771
1910
|
return this.#event.clientX;
|
|
1772
1911
|
}
|
|
@@ -2278,34 +2417,45 @@ function attributesBinding(owner) {
|
|
|
2278
2417
|
/** element.dataset: data-* attributes as camelCase string properties. */
|
|
2279
2418
|
function datasetBinding(node, realm) {
|
|
2280
2419
|
const attribute = (property) => `data-${property.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}`;
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2420
|
+
const supported = (property) => typeof property === "string" && !/-[a-z]/.test(property)
|
|
2421
|
+
&& node.hasAttribute(attribute(property));
|
|
2422
|
+
const set = (target, property, value) => {
|
|
2423
|
+
if (typeof property !== "string") return Reflect.set(target, property, value);
|
|
2424
|
+
if (/-[a-z]/.test(property)) throw new DOMException("The property name must not contain a hyphen followed by a lowercase letter.", "SyntaxError");
|
|
2425
|
+
const name = attribute(property);
|
|
2426
|
+
const next = String(value);
|
|
2427
|
+
if (node.getAttribute(name) !== next) {
|
|
2428
|
+
node.setAttribute(name, next);
|
|
2289
2429
|
realm.hooks.touch(node);
|
|
2290
|
-
|
|
2430
|
+
}
|
|
2431
|
+
return true;
|
|
2432
|
+
};
|
|
2433
|
+
return new Proxy(Object.create(realm.datasetPrototype), {
|
|
2434
|
+
get(target, property, receiver) {
|
|
2435
|
+
return supported(property) ? node.getAttribute(attribute(property)) : Reflect.get(target, property, receiver);
|
|
2291
2436
|
},
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2437
|
+
set,
|
|
2438
|
+
has: (target, property) => supported(property) || Reflect.has(target, property),
|
|
2439
|
+
deleteProperty(target, property) {
|
|
2440
|
+
if (!supported(property)) return Reflect.deleteProperty(target, property);
|
|
2295
2441
|
node.removeAttribute(attribute(property));
|
|
2296
2442
|
realm.hooks.touch(node);
|
|
2297
2443
|
return true;
|
|
2298
2444
|
},
|
|
2299
|
-
ownKeys: () => node.getAttributeNames()
|
|
2445
|
+
ownKeys: (target) => [...node.getAttributeNames()
|
|
2300
2446
|
.filter((name) => name.startsWith("data-"))
|
|
2301
|
-
.map((name) => name.slice(5).replace(/-([a-z])/g, (_, letter) => letter.toUpperCase())),
|
|
2302
|
-
getOwnPropertyDescriptor(
|
|
2303
|
-
|
|
2304
|
-
|
|
2447
|
+
.map((name) => name.slice(5).replace(/-([a-z])/g, (_, letter) => letter.toUpperCase())), ...Reflect.ownKeys(target)],
|
|
2448
|
+
getOwnPropertyDescriptor(target, property) {
|
|
2449
|
+
return supported(property)
|
|
2450
|
+
? { value: node.getAttribute(attribute(property)), writable: true, enumerable: true, configurable: true }
|
|
2451
|
+
: Reflect.getOwnPropertyDescriptor(target, property);
|
|
2305
2452
|
},
|
|
2306
|
-
defineProperty
|
|
2307
|
-
|
|
2308
|
-
|
|
2453
|
+
defineProperty(target, property, descriptor) {
|
|
2454
|
+
if (typeof property !== "string") return Reflect.defineProperty(target, property, descriptor);
|
|
2455
|
+
if (!("value" in descriptor) || descriptor.configurable === false) return false;
|
|
2456
|
+
return set(target, property, descriptor.value);
|
|
2457
|
+
},
|
|
2458
|
+
preventExtensions: () => false,
|
|
2309
2459
|
});
|
|
2310
2460
|
}
|
|
2311
2461
|
|
|
@@ -2515,12 +2665,13 @@ const EVENT_INTERFACES = {
|
|
|
2515
2665
|
MouseEvent: ["UIEvent", MOUSE_MEMBERS],
|
|
2516
2666
|
PointerEvent: ["MouseEvent", ["pointerType"]],
|
|
2517
2667
|
WheelEvent: ["MouseEvent", ["deltaX", "deltaY", "deltaMode"]],
|
|
2518
|
-
KeyboardEvent: ["UIEvent", ["key", "code", "repeat", ...MODIFIERS]],
|
|
2668
|
+
KeyboardEvent: ["UIEvent", ["key", "code", "keyCode", "charCode", "repeat", ...MODIFIERS]],
|
|
2519
2669
|
FocusEvent: ["UIEvent", ["relatedTarget"]],
|
|
2520
2670
|
InputEvent: ["UIEvent", ["data", "inputType"]],
|
|
2521
2671
|
MessageEvent: ["Event", ["data", "origin", "lastEventId", "source", "ports"]],
|
|
2522
2672
|
PopStateEvent: ["Event", ["state"]],
|
|
2523
2673
|
HashChangeEvent: ["Event", ["oldURL", "newURL"]],
|
|
2674
|
+
SubmitEvent: ["Event", ["submitter"]],
|
|
2524
2675
|
};
|
|
2525
2676
|
/** document.createEvent() interface names (DOM §4.5): legacy aliases included. */
|
|
2526
2677
|
const CREATE_EVENT_TYPES = new Map([["event", "Event"], ["events", "Event"], ["htmlevents", "Event"],
|
|
@@ -2531,7 +2682,7 @@ const CREATE_EVENT_TYPES = new Map([["event", "Event"], ["events", "Event"], ["h
|
|
|
2531
2682
|
const UI_EVENT_TYPES = ["UIEvent", "MouseEvent", "PointerEvent", "WheelEvent", "KeyboardEvent", "FocusEvent", "InputEvent"];
|
|
2532
2683
|
const UI_EVENT_FIELDS = [
|
|
2533
2684
|
...["detail", "clientX", "clientY", "screenX", "screenY", "button", "buttons", "deltaX", "deltaY", "deltaZ", "deltaMode",
|
|
2534
|
-
"pointerId", "width", "height", "pressure", "location"].map((key) => [key, Number]),
|
|
2685
|
+
"pointerId", "width", "height", "pressure", "location", "keyCode", "which", "charCode"].map((key) => [key, Number]),
|
|
2535
2686
|
...["key", "code", "data", "inputType", "pointerType"].map((key) => [key, String]),
|
|
2536
2687
|
...["ctrlKey", "altKey", "shiftKey", "metaKey", "repeat", "isComposing", "isPrimary"].map((key) => [key, Boolean]),
|
|
2537
2688
|
];
|
|
@@ -2553,7 +2704,10 @@ export function eventConstructors(realm) {
|
|
|
2553
2704
|
// detail stays in the binding: page values never enter Happy DOM.
|
|
2554
2705
|
return realm.wrapEvent(new window.CustomEvent(String(type), init(options)), options?.detail);
|
|
2555
2706
|
}
|
|
2556
|
-
|
|
2707
|
+
// Members moved to UI event subclasses must not remain inherited through
|
|
2708
|
+
// the frozen engine binding. Plain Events allow expandos such as React's
|
|
2709
|
+
// afterblur.relatedTarget, and Event.prototype directly inherits Object.
|
|
2710
|
+
realm.eventPrototype = mutableBindingPrototype(EventBinding.prototype, Object.prototype);
|
|
2557
2711
|
Object.defineProperty(realm.eventPrototype, "constructor", { value: Event, writable: true, configurable: true });
|
|
2558
2712
|
Event.prototype = realm.eventPrototype;
|
|
2559
2713
|
for (const [name, value] of Object.entries(EVENT_PHASES)) {
|
|
@@ -2611,6 +2765,14 @@ export function eventConstructors(realm) {
|
|
|
2611
2765
|
}
|
|
2612
2766
|
MessageEvent.prototype = realm.eventPrototypes.get("MessageEvent");
|
|
2613
2767
|
Object.defineProperty(MessageEvent.prototype, "constructor", { value: MessageEvent, writable: true, configurable: true });
|
|
2768
|
+
function SubmitEvent(type, options = undefined) {
|
|
2769
|
+
if (!new.target) throw new TypeError("Constructor SubmitEvent requires 'new'");
|
|
2770
|
+
const submitter = options?.submitter == null ? null : nodeOf(options.submitter);
|
|
2771
|
+
if (submitter && submitter.nodeType !== 1) throw new TypeError("The submitter must be an element");
|
|
2772
|
+
return realm.wrapEvent(new window.SubmitEvent(String(type), { ...init(options), submitter }));
|
|
2773
|
+
}
|
|
2774
|
+
SubmitEvent.prototype = realm.eventPrototypes.get("SubmitEvent");
|
|
2775
|
+
Object.defineProperty(SubmitEvent.prototype, "constructor", { value: SubmitEvent, writable: true, configurable: true });
|
|
2614
2776
|
// PromiseRejectionEvent (HTML §8.1.5.7): promise and reason stay page values in the binding.
|
|
2615
2777
|
const rejections = new WeakMap();
|
|
2616
2778
|
function PromiseRejectionEvent(type, options) {
|
|
@@ -2634,7 +2796,7 @@ export function eventConstructors(realm) {
|
|
|
2634
2796
|
enumerable: true, configurable: true },
|
|
2635
2797
|
[Symbol.toStringTag]: { value: "PromiseRejectionEvent", configurable: true },
|
|
2636
2798
|
});
|
|
2637
|
-
return { Event, CustomEvent, MessageEvent, PromiseRejectionEvent, ...uiEvents };
|
|
2799
|
+
return { Event, CustomEvent, MessageEvent, SubmitEvent, PromiseRejectionEvent, ...uiEvents };
|
|
2638
2800
|
}
|
|
2639
2801
|
|
|
2640
2802
|
function domRect({ left, top, width, height }) {
|