From 552c235e2091c1c48e70cecb2cabb25ab10a6b25 Mon Sep 17 00:00:00 2001 From: Khafra <42794878+KhafraDev@users.noreply.github.com> Date: Fri, 7 Oct 2022 13:10:26 -0400 Subject: [PATCH] feat(WPTs): add in idlharness.any.js test (#1689) this was painful. --- lib/fetch/body.js | 24 - lib/fetch/headers.js | 3 +- lib/fetch/request.js | 48 +- lib/fetch/response.js | 26 +- test/wpt/runner/runner/runner.mjs | 7 +- test/wpt/runner/runner/util.mjs | 6 + test/wpt/runner/runner/worker.mjs | 5 + test/wpt/server/server.mjs | 4 + test/wpt/status/fetch.status.json | 5 + test/wpt/tests/fetch/api/idlharness.any.js | 21 + test/wpt/tests/interfaces/dom.idl | 645 +++ test/wpt/tests/interfaces/fetch.idl | 114 + test/wpt/tests/interfaces/html.idl | 2691 ++++++++++++ test/wpt/tests/interfaces/referrer-policy.idl | 16 + test/wpt/tests/resources/WebIDLParser.js | 1 + test/wpt/tests/resources/idlharness.js | 3526 +++++++++++++++ .../tests/resources/webidl2/lib/webidl2.js | 3824 +++++++++++++++++ 17 files changed, 10928 insertions(+), 38 deletions(-) create mode 100644 test/wpt/tests/fetch/api/idlharness.any.js create mode 100644 test/wpt/tests/interfaces/dom.idl create mode 100644 test/wpt/tests/interfaces/fetch.idl create mode 100644 test/wpt/tests/interfaces/html.idl create mode 100644 test/wpt/tests/interfaces/referrer-policy.idl create mode 120000 test/wpt/tests/resources/WebIDLParser.js create mode 100644 test/wpt/tests/resources/idlharness.js create mode 100644 test/wpt/tests/resources/webidl2/lib/webidl2.js diff --git a/lib/fetch/body.js b/lib/fetch/body.js index ccb5b19b655..0891fa87282 100644 --- a/lib/fetch/body.js +++ b/lib/fetch/body.js @@ -530,32 +530,8 @@ function bodyMixinMethods (instance) { return methods } -const properties = { - body: { - enumerable: true, - get () { - if (!this || !this[kState]) { - throw new TypeError('Illegal invocation') - } - - return this[kState].body ? this[kState].body.stream : null - } - }, - bodyUsed: { - enumerable: true, - get () { - if (!this || !this[kState]) { - throw new TypeError('Illegal invocation') - } - - return !!this[kState].body && util.isDisturbed(this[kState].body.stream) - } - } -} - function mixinBody (prototype) { Object.assign(prototype.prototype, bodyMixinMethods(prototype)) - Object.defineProperties(prototype.prototype, properties) } module.exports = { diff --git a/lib/fetch/headers.js b/lib/fetch/headers.js index 78f2990774f..30a2e33e79f 100644 --- a/lib/fetch/headers.js +++ b/lib/fetch/headers.js @@ -478,7 +478,8 @@ Object.defineProperties(Headers.prototype, { keys: kEnumerableProperty, values: kEnumerableProperty, entries: kEnumerableProperty, - forEach: kEnumerableProperty + forEach: kEnumerableProperty, + [Symbol.iterator]: { enumerable: false } }) webidl.converters.HeadersInit = function (V) { diff --git a/lib/fetch/request.js b/lib/fetch/request.js index c25062a41cb..02e4ae46899 100644 --- a/lib/fetch/request.js +++ b/lib/fetch/request.js @@ -711,6 +711,30 @@ class Request { return this[kSignal] } + get body () { + if (!this || !this[kState]) { + throw new TypeError('Illegal invocation') + } + + return this[kState].body ? this[kState].body.stream : null + } + + get bodyUsed () { + if (!this || !this[kState]) { + throw new TypeError('Illegal invocation') + } + + return !!this[kState].body && util.isDisturbed(this[kState].body.stream) + } + + get duplex () { + if (!(this instanceof Request)) { + throw new TypeError('Illegal invocation') + } + + return 'half' + } + // Returns a clone of request. clone () { if (!(this instanceof Request)) { @@ -828,16 +852,20 @@ Object.defineProperties(Request.prototype, { redirect: kEnumerableProperty, clone: kEnumerableProperty, signal: kEnumerableProperty, - duplex: { - ...kEnumerableProperty, - get () { - // The duplex getter steps are to return "half". - return 'half' - }, - set () { - - } - } + duplex: kEnumerableProperty, + destination: kEnumerableProperty, + body: kEnumerableProperty, + bodyUsed: kEnumerableProperty, + isHistoryNavigation: kEnumerableProperty, + isReloadNavigation: kEnumerableProperty, + keepalive: kEnumerableProperty, + integrity: kEnumerableProperty, + cache: kEnumerableProperty, + credentials: kEnumerableProperty, + attribute: kEnumerableProperty, + referrerPolicy: kEnumerableProperty, + referrer: kEnumerableProperty, + mode: kEnumerableProperty }) webidl.converters.Request = webidl.interfaceConverter( diff --git a/lib/fetch/response.js b/lib/fetch/response.js index 198beb9c226..85bd6af7a8b 100644 --- a/lib/fetch/response.js +++ b/lib/fetch/response.js @@ -259,6 +259,22 @@ class Response { return this[kHeaders] } + get body () { + if (!this || !this[kState]) { + throw new TypeError('Illegal invocation') + } + + return this[kState].body ? this[kState].body.stream : null + } + + get bodyUsed () { + if (!this || !this[kState]) { + throw new TypeError('Illegal invocation') + } + + return !!this[kState].body && util.isDisturbed(this[kState].body.stream) + } + // Returns a clone of response. clone () { if (!(this instanceof Response)) { @@ -299,7 +315,15 @@ Object.defineProperties(Response.prototype, { redirected: kEnumerableProperty, statusText: kEnumerableProperty, headers: kEnumerableProperty, - clone: kEnumerableProperty + clone: kEnumerableProperty, + body: kEnumerableProperty, + bodyUsed: kEnumerableProperty +}) + +Object.defineProperties(Response, { + json: kEnumerableProperty, + redirect: kEnumerableProperty, + error: kEnumerableProperty }) // https://fetch.spec.whatwg.org/#concept-response-clone diff --git a/test/wpt/runner/runner/runner.mjs b/test/wpt/runner/runner/runner.mjs index 4881320235c..8ed5921b978 100644 --- a/test/wpt/runner/runner/runner.mjs +++ b/test/wpt/runner/runner/runner.mjs @@ -3,7 +3,7 @@ import { readdirSync, readFileSync, statSync } from 'node:fs' import { basename, isAbsolute, join, resolve } from 'node:path' import { fileURLToPath } from 'node:url' import { Worker } from 'node:worker_threads' -import { parseMeta } from './util.mjs' +import { parseMeta, resolveSymLink } from './util.mjs' const basePath = fileURLToPath(join(import.meta.url, '../../..')) const testPath = join(basePath, 'tests') @@ -155,7 +155,10 @@ export class WPTRunner extends EventEmitter { resolveMeta (code, path) { const meta = parseMeta(code) const scripts = meta.scripts.map((script) => { - if (isAbsolute(script)) { + if (script === '/resources/WebIDLParser.js') { + // See https://github.com/web-platform-tests/wpt/pull/731 + return resolveSymLink(join(testPath, script)) + } else if (isAbsolute(script)) { return readFileSync(join(testPath, script), 'utf-8') } diff --git a/test/wpt/runner/runner/util.mjs b/test/wpt/runner/runner/util.mjs index a816e700a50..9f75a5f0b60 100644 --- a/test/wpt/runner/runner/util.mjs +++ b/test/wpt/runner/runner/util.mjs @@ -1,4 +1,5 @@ import { exit } from 'node:process' +import { readFileSync, readlinkSync } from 'node:fs' /** * Parse the `Meta:` tags sometimes included in tests. @@ -63,3 +64,8 @@ export function parseMeta (fileContents) { return meta } + +export function resolveSymLink (path) { + const symlink = readlinkSync(path) + return readFileSync(symlink, 'utf-8') +} diff --git a/test/wpt/runner/runner/worker.mjs b/test/wpt/runner/runner/worker.mjs index 51fb95d03fe..cea71058bd8 100644 --- a/test/wpt/runner/runner/worker.mjs +++ b/test/wpt/runner/runner/worker.mjs @@ -26,6 +26,7 @@ const globalPropertyDescriptors = { Object.defineProperties(globalThis, { fetch: { ...globalPropertyDescriptors, + enumerable: true, value: fetch }, File: { @@ -60,10 +61,14 @@ runInThisContext(` }, isShadowRealm () { return false + }, + isWindow () { + return false } } globalThis.window = globalThis globalThis.location = new URL('${url}') + globalThis.Window = Object.getPrototypeOf(globalThis).constructor `) const harness = readFileSync(join(basePath, '../runner/resources/testharness.cjs'), 'utf-8') diff --git a/test/wpt/server/server.mjs b/test/wpt/server/server.mjs index f8ad71accab..3e71b71cf33 100644 --- a/test/wpt/server/server.mjs +++ b/test/wpt/server/server.mjs @@ -30,6 +30,10 @@ const server = createServer(async (req, res) => { const fullUrl = new URL(req.url, `http://localhost:${server.address().port}`) switch (fullUrl.pathname) { + case '/interfaces/dom.idl': + case '/interfaces/html.idl': + case '/interfaces/fetch.idl': + case '/interfaces/referrer-policy.idl': case '/xhr/resources/utf16-bom.json': case '/fetch/data-urls/resources/base64.json': case '/fetch/data-urls/resources/data-urls.json': diff --git a/test/wpt/status/fetch.status.json b/test/wpt/status/fetch.status.json index b3ba1325ab8..1a034fa16a1 100644 --- a/test/wpt/status/fetch.status.json +++ b/test/wpt/status/fetch.status.json @@ -23,5 +23,10 @@ "Consume response's body as formData with correct multipart type (error case)", "Consume empty FormData response body as text" ] + }, + "idlharness.any.js": { + "fail": [ + "Response interface: operation json(any, optional ResponseInit)" + ] } } \ No newline at end of file diff --git a/test/wpt/tests/fetch/api/idlharness.any.js b/test/wpt/tests/fetch/api/idlharness.any.js new file mode 100644 index 00000000000..7b3c694e16a --- /dev/null +++ b/test/wpt/tests/fetch/api/idlharness.any.js @@ -0,0 +1,21 @@ +// META: global=window,worker +// META: script=/resources/WebIDLParser.js +// META: script=/resources/idlharness.js +// META: timeout=long + +idl_test( + ['fetch'], + ['referrer-policy', 'html', 'dom'], + idl_array => { + idl_array.add_objects({ + Headers: ["new Headers()"], + Request: ["new Request('about:blank')"], + Response: ["new Response()"], + }); + if (self.GLOBAL.isWindow()) { + idl_array.add_objects({ Window: ['window'] }); + } else if (self.GLOBAL.isWorker()) { + idl_array.add_objects({ WorkerGlobalScope: ['self'] }); + } + } +); diff --git a/test/wpt/tests/interfaces/dom.idl b/test/wpt/tests/interfaces/dom.idl new file mode 100644 index 00000000000..96acfc6a717 --- /dev/null +++ b/test/wpt/tests/interfaces/dom.idl @@ -0,0 +1,645 @@ +// GENERATED CONTENT - DO NOT EDIT +// Content was automatically extracted by Reffy into webref +// (https://github.com/w3c/webref) +// Source: DOM Standard (https://dom.spec.whatwg.org/) + +[Exposed=*] +interface Event { + constructor(DOMString type, optional EventInit eventInitDict = {}); + + readonly attribute DOMString type; + readonly attribute EventTarget? target; + readonly attribute EventTarget? srcElement; // legacy + readonly attribute EventTarget? currentTarget; + sequence composedPath(); + + const unsigned short NONE = 0; + const unsigned short CAPTURING_PHASE = 1; + const unsigned short AT_TARGET = 2; + const unsigned short BUBBLING_PHASE = 3; + readonly attribute unsigned short eventPhase; + + undefined stopPropagation(); + attribute boolean cancelBubble; // legacy alias of .stopPropagation() + undefined stopImmediatePropagation(); + + readonly attribute boolean bubbles; + readonly attribute boolean cancelable; + attribute boolean returnValue; // legacy + undefined preventDefault(); + readonly attribute boolean defaultPrevented; + readonly attribute boolean composed; + + [LegacyUnforgeable] readonly attribute boolean isTrusted; + readonly attribute DOMHighResTimeStamp timeStamp; + + undefined initEvent(DOMString type, optional boolean bubbles = false, optional boolean cancelable = false); // legacy +}; + +dictionary EventInit { + boolean bubbles = false; + boolean cancelable = false; + boolean composed = false; +}; + +partial interface Window { + [Replaceable] readonly attribute (Event or undefined) event; // legacy +}; + +[Exposed=*] +interface CustomEvent : Event { + constructor(DOMString type, optional CustomEventInit eventInitDict = {}); + + readonly attribute any detail; + + undefined initCustomEvent(DOMString type, optional boolean bubbles = false, optional boolean cancelable = false, optional any detail = null); // legacy +}; + +dictionary CustomEventInit : EventInit { + any detail = null; +}; + +[Exposed=*] +interface EventTarget { + constructor(); + + undefined addEventListener(DOMString type, EventListener? callback, optional (AddEventListenerOptions or boolean) options = {}); + undefined removeEventListener(DOMString type, EventListener? callback, optional (EventListenerOptions or boolean) options = {}); + boolean dispatchEvent(Event event); +}; + +callback interface EventListener { + undefined handleEvent(Event event); +}; + +dictionary EventListenerOptions { + boolean capture = false; +}; + +dictionary AddEventListenerOptions : EventListenerOptions { + boolean passive; + boolean once = false; + AbortSignal signal; +}; + +[Exposed=*] +interface AbortController { + constructor(); + + [SameObject] readonly attribute AbortSignal signal; + + undefined abort(optional any reason); +}; + +[Exposed=*] +interface AbortSignal : EventTarget { + [NewObject] static AbortSignal abort(optional any reason); + [Exposed=(Window,Worker), NewObject] static AbortSignal timeout([EnforceRange] unsigned long long milliseconds); + + readonly attribute boolean aborted; + readonly attribute any reason; + undefined throwIfAborted(); + + attribute EventHandler onabort; +}; +interface mixin NonElementParentNode { + Element? getElementById(DOMString elementId); +}; +Document includes NonElementParentNode; +DocumentFragment includes NonElementParentNode; + +interface mixin DocumentOrShadowRoot { +}; +Document includes DocumentOrShadowRoot; +ShadowRoot includes DocumentOrShadowRoot; + +interface mixin ParentNode { + [SameObject] readonly attribute HTMLCollection children; + readonly attribute Element? firstElementChild; + readonly attribute Element? lastElementChild; + readonly attribute unsigned long childElementCount; + + [CEReactions, Unscopable] undefined prepend((Node or DOMString)... nodes); + [CEReactions, Unscopable] undefined append((Node or DOMString)... nodes); + [CEReactions, Unscopable] undefined replaceChildren((Node or DOMString)... nodes); + + Element? querySelector(DOMString selectors); + [NewObject] NodeList querySelectorAll(DOMString selectors); +}; +Document includes ParentNode; +DocumentFragment includes ParentNode; +Element includes ParentNode; + +interface mixin NonDocumentTypeChildNode { + readonly attribute Element? previousElementSibling; + readonly attribute Element? nextElementSibling; +}; +Element includes NonDocumentTypeChildNode; +CharacterData includes NonDocumentTypeChildNode; + +interface mixin ChildNode { + [CEReactions, Unscopable] undefined before((Node or DOMString)... nodes); + [CEReactions, Unscopable] undefined after((Node or DOMString)... nodes); + [CEReactions, Unscopable] undefined replaceWith((Node or DOMString)... nodes); + [CEReactions, Unscopable] undefined remove(); +}; +DocumentType includes ChildNode; +Element includes ChildNode; +CharacterData includes ChildNode; + +interface mixin Slottable { + readonly attribute HTMLSlotElement? assignedSlot; +}; +Element includes Slottable; +Text includes Slottable; + +[Exposed=Window] +interface NodeList { + getter Node? item(unsigned long index); + readonly attribute unsigned long length; + iterable; +}; + +[Exposed=Window, LegacyUnenumerableNamedProperties] +interface HTMLCollection { + readonly attribute unsigned long length; + getter Element? item(unsigned long index); + getter Element? namedItem(DOMString name); +}; + +[Exposed=Window] +interface MutationObserver { + constructor(MutationCallback callback); + + undefined observe(Node target, optional MutationObserverInit options = {}); + undefined disconnect(); + sequence takeRecords(); +}; + +callback MutationCallback = undefined (sequence mutations, MutationObserver observer); + +dictionary MutationObserverInit { + boolean childList = false; + boolean attributes; + boolean characterData; + boolean subtree = false; + boolean attributeOldValue; + boolean characterDataOldValue; + sequence attributeFilter; +}; + +[Exposed=Window] +interface MutationRecord { + readonly attribute DOMString type; + [SameObject] readonly attribute Node target; + [SameObject] readonly attribute NodeList addedNodes; + [SameObject] readonly attribute NodeList removedNodes; + readonly attribute Node? previousSibling; + readonly attribute Node? nextSibling; + readonly attribute DOMString? attributeName; + readonly attribute DOMString? attributeNamespace; + readonly attribute DOMString? oldValue; +}; + +[Exposed=Window] +interface Node : EventTarget { + const unsigned short ELEMENT_NODE = 1; + const unsigned short ATTRIBUTE_NODE = 2; + const unsigned short TEXT_NODE = 3; + const unsigned short CDATA_SECTION_NODE = 4; + const unsigned short ENTITY_REFERENCE_NODE = 5; // legacy + const unsigned short ENTITY_NODE = 6; // legacy + const unsigned short PROCESSING_INSTRUCTION_NODE = 7; + const unsigned short COMMENT_NODE = 8; + const unsigned short DOCUMENT_NODE = 9; + const unsigned short DOCUMENT_TYPE_NODE = 10; + const unsigned short DOCUMENT_FRAGMENT_NODE = 11; + const unsigned short NOTATION_NODE = 12; // legacy + readonly attribute unsigned short nodeType; + readonly attribute DOMString nodeName; + + readonly attribute USVString baseURI; + + readonly attribute boolean isConnected; + readonly attribute Document? ownerDocument; + Node getRootNode(optional GetRootNodeOptions options = {}); + readonly attribute Node? parentNode; + readonly attribute Element? parentElement; + boolean hasChildNodes(); + [SameObject] readonly attribute NodeList childNodes; + readonly attribute Node? firstChild; + readonly attribute Node? lastChild; + readonly attribute Node? previousSibling; + readonly attribute Node? nextSibling; + + [CEReactions] attribute DOMString? nodeValue; + [CEReactions] attribute DOMString? textContent; + [CEReactions] undefined normalize(); + + [CEReactions, NewObject] Node cloneNode(optional boolean deep = false); + boolean isEqualNode(Node? otherNode); + boolean isSameNode(Node? otherNode); // legacy alias of === + + const unsigned short DOCUMENT_POSITION_DISCONNECTED = 0x01; + const unsigned short DOCUMENT_POSITION_PRECEDING = 0x02; + const unsigned short DOCUMENT_POSITION_FOLLOWING = 0x04; + const unsigned short DOCUMENT_POSITION_CONTAINS = 0x08; + const unsigned short DOCUMENT_POSITION_CONTAINED_BY = 0x10; + const unsigned short DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20; + unsigned short compareDocumentPosition(Node other); + boolean contains(Node? other); + + DOMString? lookupPrefix(DOMString? namespace); + DOMString? lookupNamespaceURI(DOMString? prefix); + boolean isDefaultNamespace(DOMString? namespace); + + [CEReactions] Node insertBefore(Node node, Node? child); + [CEReactions] Node appendChild(Node node); + [CEReactions] Node replaceChild(Node node, Node child); + [CEReactions] Node removeChild(Node child); +}; + +dictionary GetRootNodeOptions { + boolean composed = false; +}; + +[Exposed=Window] +interface Document : Node { + constructor(); + + [SameObject] readonly attribute DOMImplementation implementation; + readonly attribute USVString URL; + readonly attribute USVString documentURI; + readonly attribute DOMString compatMode; + readonly attribute DOMString characterSet; + readonly attribute DOMString charset; // legacy alias of .characterSet + readonly attribute DOMString inputEncoding; // legacy alias of .characterSet + readonly attribute DOMString contentType; + + readonly attribute DocumentType? doctype; + readonly attribute Element? documentElement; + HTMLCollection getElementsByTagName(DOMString qualifiedName); + HTMLCollection getElementsByTagNameNS(DOMString? namespace, DOMString localName); + HTMLCollection getElementsByClassName(DOMString classNames); + + [CEReactions, NewObject] Element createElement(DOMString localName, optional (DOMString or ElementCreationOptions) options = {}); + [CEReactions, NewObject] Element createElementNS(DOMString? namespace, DOMString qualifiedName, optional (DOMString or ElementCreationOptions) options = {}); + [NewObject] DocumentFragment createDocumentFragment(); + [NewObject] Text createTextNode(DOMString data); + [NewObject] CDATASection createCDATASection(DOMString data); + [NewObject] Comment createComment(DOMString data); + [NewObject] ProcessingInstruction createProcessingInstruction(DOMString target, DOMString data); + + [CEReactions, NewObject] Node importNode(Node node, optional boolean deep = false); + [CEReactions] Node adoptNode(Node node); + + [NewObject] Attr createAttribute(DOMString localName); + [NewObject] Attr createAttributeNS(DOMString? namespace, DOMString qualifiedName); + + [NewObject] Event createEvent(DOMString interface); // legacy + + [NewObject] Range createRange(); + + // NodeFilter.SHOW_ALL = 0xFFFFFFFF + [NewObject] NodeIterator createNodeIterator(Node root, optional unsigned long whatToShow = 0xFFFFFFFF, optional NodeFilter? filter = null); + [NewObject] TreeWalker createTreeWalker(Node root, optional unsigned long whatToShow = 0xFFFFFFFF, optional NodeFilter? filter = null); +}; + +[Exposed=Window] +interface XMLDocument : Document {}; + +dictionary ElementCreationOptions { + DOMString is; +}; + +[Exposed=Window] +interface DOMImplementation { + [NewObject] DocumentType createDocumentType(DOMString qualifiedName, DOMString publicId, DOMString systemId); + [NewObject] XMLDocument createDocument(DOMString? namespace, [LegacyNullToEmptyString] DOMString qualifiedName, optional DocumentType? doctype = null); + [NewObject] Document createHTMLDocument(optional DOMString title); + + boolean hasFeature(); // useless; always returns true +}; + +[Exposed=Window] +interface DocumentType : Node { + readonly attribute DOMString name; + readonly attribute DOMString publicId; + readonly attribute DOMString systemId; +}; + +[Exposed=Window] +interface DocumentFragment : Node { + constructor(); +}; + +[Exposed=Window] +interface ShadowRoot : DocumentFragment { + readonly attribute ShadowRootMode mode; + readonly attribute boolean delegatesFocus; + readonly attribute SlotAssignmentMode slotAssignment; + readonly attribute Element host; + attribute EventHandler onslotchange; +}; + +enum ShadowRootMode { "open", "closed" }; +enum SlotAssignmentMode { "manual", "named" }; + +[Exposed=Window] +interface Element : Node { + readonly attribute DOMString? namespaceURI; + readonly attribute DOMString? prefix; + readonly attribute DOMString localName; + readonly attribute DOMString tagName; + + [CEReactions] attribute DOMString id; + [CEReactions] attribute DOMString className; + [SameObject, PutForwards=value] readonly attribute DOMTokenList classList; + [CEReactions, Unscopable] attribute DOMString slot; + + boolean hasAttributes(); + [SameObject] readonly attribute NamedNodeMap attributes; + sequence getAttributeNames(); + DOMString? getAttribute(DOMString qualifiedName); + DOMString? getAttributeNS(DOMString? namespace, DOMString localName); + [CEReactions] undefined setAttribute(DOMString qualifiedName, DOMString value); + [CEReactions] undefined setAttributeNS(DOMString? namespace, DOMString qualifiedName, DOMString value); + [CEReactions] undefined removeAttribute(DOMString qualifiedName); + [CEReactions] undefined removeAttributeNS(DOMString? namespace, DOMString localName); + [CEReactions] boolean toggleAttribute(DOMString qualifiedName, optional boolean force); + boolean hasAttribute(DOMString qualifiedName); + boolean hasAttributeNS(DOMString? namespace, DOMString localName); + + Attr? getAttributeNode(DOMString qualifiedName); + Attr? getAttributeNodeNS(DOMString? namespace, DOMString localName); + [CEReactions] Attr? setAttributeNode(Attr attr); + [CEReactions] Attr? setAttributeNodeNS(Attr attr); + [CEReactions] Attr removeAttributeNode(Attr attr); + + ShadowRoot attachShadow(ShadowRootInit init); + readonly attribute ShadowRoot? shadowRoot; + + Element? closest(DOMString selectors); + boolean matches(DOMString selectors); + boolean webkitMatchesSelector(DOMString selectors); // legacy alias of .matches + + HTMLCollection getElementsByTagName(DOMString qualifiedName); + HTMLCollection getElementsByTagNameNS(DOMString? namespace, DOMString localName); + HTMLCollection getElementsByClassName(DOMString classNames); + + [CEReactions] Element? insertAdjacentElement(DOMString where, Element element); // legacy + undefined insertAdjacentText(DOMString where, DOMString data); // legacy +}; + +dictionary ShadowRootInit { + required ShadowRootMode mode; + boolean delegatesFocus = false; + SlotAssignmentMode slotAssignment = "named"; +}; + +[Exposed=Window, + LegacyUnenumerableNamedProperties] +interface NamedNodeMap { + readonly attribute unsigned long length; + getter Attr? item(unsigned long index); + getter Attr? getNamedItem(DOMString qualifiedName); + Attr? getNamedItemNS(DOMString? namespace, DOMString localName); + [CEReactions] Attr? setNamedItem(Attr attr); + [CEReactions] Attr? setNamedItemNS(Attr attr); + [CEReactions] Attr removeNamedItem(DOMString qualifiedName); + [CEReactions] Attr removeNamedItemNS(DOMString? namespace, DOMString localName); +}; + +[Exposed=Window] +interface Attr : Node { + readonly attribute DOMString? namespaceURI; + readonly attribute DOMString? prefix; + readonly attribute DOMString localName; + readonly attribute DOMString name; + [CEReactions] attribute DOMString value; + + readonly attribute Element? ownerElement; + + readonly attribute boolean specified; // useless; always returns true +}; +[Exposed=Window] +interface CharacterData : Node { + attribute [LegacyNullToEmptyString] DOMString data; + readonly attribute unsigned long length; + DOMString substringData(unsigned long offset, unsigned long count); + undefined appendData(DOMString data); + undefined insertData(unsigned long offset, DOMString data); + undefined deleteData(unsigned long offset, unsigned long count); + undefined replaceData(unsigned long offset, unsigned long count, DOMString data); +}; + +[Exposed=Window] +interface Text : CharacterData { + constructor(optional DOMString data = ""); + + [NewObject] Text splitText(unsigned long offset); + readonly attribute DOMString wholeText; +}; + +[Exposed=Window] +interface CDATASection : Text { +}; +[Exposed=Window] +interface ProcessingInstruction : CharacterData { + readonly attribute DOMString target; +}; +[Exposed=Window] +interface Comment : CharacterData { + constructor(optional DOMString data = ""); +}; + +[Exposed=Window] +interface AbstractRange { + readonly attribute Node startContainer; + readonly attribute unsigned long startOffset; + readonly attribute Node endContainer; + readonly attribute unsigned long endOffset; + readonly attribute boolean collapsed; +}; + +dictionary StaticRangeInit { + required Node startContainer; + required unsigned long startOffset; + required Node endContainer; + required unsigned long endOffset; +}; + +[Exposed=Window] +interface StaticRange : AbstractRange { + constructor(StaticRangeInit init); +}; + +[Exposed=Window] +interface Range : AbstractRange { + constructor(); + + readonly attribute Node commonAncestorContainer; + + undefined setStart(Node node, unsigned long offset); + undefined setEnd(Node node, unsigned long offset); + undefined setStartBefore(Node node); + undefined setStartAfter(Node node); + undefined setEndBefore(Node node); + undefined setEndAfter(Node node); + undefined collapse(optional boolean toStart = false); + undefined selectNode(Node node); + undefined selectNodeContents(Node node); + + const unsigned short START_TO_START = 0; + const unsigned short START_TO_END = 1; + const unsigned short END_TO_END = 2; + const unsigned short END_TO_START = 3; + short compareBoundaryPoints(unsigned short how, Range sourceRange); + + [CEReactions] undefined deleteContents(); + [CEReactions, NewObject] DocumentFragment extractContents(); + [CEReactions, NewObject] DocumentFragment cloneContents(); + [CEReactions] undefined insertNode(Node node); + [CEReactions] undefined surroundContents(Node newParent); + + [NewObject] Range cloneRange(); + undefined detach(); + + boolean isPointInRange(Node node, unsigned long offset); + short comparePoint(Node node, unsigned long offset); + + boolean intersectsNode(Node node); + + stringifier; +}; + +[Exposed=Window] +interface NodeIterator { + [SameObject] readonly attribute Node root; + readonly attribute Node referenceNode; + readonly attribute boolean pointerBeforeReferenceNode; + readonly attribute unsigned long whatToShow; + readonly attribute NodeFilter? filter; + + Node? nextNode(); + Node? previousNode(); + + undefined detach(); +}; + +[Exposed=Window] +interface TreeWalker { + [SameObject] readonly attribute Node root; + readonly attribute unsigned long whatToShow; + readonly attribute NodeFilter? filter; + attribute Node currentNode; + + Node? parentNode(); + Node? firstChild(); + Node? lastChild(); + Node? previousSibling(); + Node? nextSibling(); + Node? previousNode(); + Node? nextNode(); +}; +[Exposed=Window] +callback interface NodeFilter { + // Constants for acceptNode() + const unsigned short FILTER_ACCEPT = 1; + const unsigned short FILTER_REJECT = 2; + const unsigned short FILTER_SKIP = 3; + + // Constants for whatToShow + const unsigned long SHOW_ALL = 0xFFFFFFFF; + const unsigned long SHOW_ELEMENT = 0x1; + const unsigned long SHOW_ATTRIBUTE = 0x2; + const unsigned long SHOW_TEXT = 0x4; + const unsigned long SHOW_CDATA_SECTION = 0x8; + const unsigned long SHOW_ENTITY_REFERENCE = 0x10; // legacy + const unsigned long SHOW_ENTITY = 0x20; // legacy + const unsigned long SHOW_PROCESSING_INSTRUCTION = 0x40; + const unsigned long SHOW_COMMENT = 0x80; + const unsigned long SHOW_DOCUMENT = 0x100; + const unsigned long SHOW_DOCUMENT_TYPE = 0x200; + const unsigned long SHOW_DOCUMENT_FRAGMENT = 0x400; + const unsigned long SHOW_NOTATION = 0x800; // legacy + + unsigned short acceptNode(Node node); +}; + +[Exposed=Window] +interface DOMTokenList { + readonly attribute unsigned long length; + getter DOMString? item(unsigned long index); + boolean contains(DOMString token); + [CEReactions] undefined add(DOMString... tokens); + [CEReactions] undefined remove(DOMString... tokens); + [CEReactions] boolean toggle(DOMString token, optional boolean force); + [CEReactions] boolean replace(DOMString token, DOMString newToken); + boolean supports(DOMString token); + [CEReactions] stringifier attribute DOMString value; + iterable; +}; + +[Exposed=Window] +interface XPathResult { + const unsigned short ANY_TYPE = 0; + const unsigned short NUMBER_TYPE = 1; + const unsigned short STRING_TYPE = 2; + const unsigned short BOOLEAN_TYPE = 3; + const unsigned short UNORDERED_NODE_ITERATOR_TYPE = 4; + const unsigned short ORDERED_NODE_ITERATOR_TYPE = 5; + const unsigned short UNORDERED_NODE_SNAPSHOT_TYPE = 6; + const unsigned short ORDERED_NODE_SNAPSHOT_TYPE = 7; + const unsigned short ANY_UNORDERED_NODE_TYPE = 8; + const unsigned short FIRST_ORDERED_NODE_TYPE = 9; + + readonly attribute unsigned short resultType; + readonly attribute unrestricted double numberValue; + readonly attribute DOMString stringValue; + readonly attribute boolean booleanValue; + readonly attribute Node? singleNodeValue; + readonly attribute boolean invalidIteratorState; + readonly attribute unsigned long snapshotLength; + + Node? iterateNext(); + Node? snapshotItem(unsigned long index); +}; + +[Exposed=Window] +interface XPathExpression { + // XPathResult.ANY_TYPE = 0 + XPathResult evaluate(Node contextNode, optional unsigned short type = 0, optional XPathResult? result = null); +}; + +callback interface XPathNSResolver { + DOMString? lookupNamespaceURI(DOMString? prefix); +}; + +interface mixin XPathEvaluatorBase { + [NewObject] XPathExpression createExpression(DOMString expression, optional XPathNSResolver? resolver = null); + XPathNSResolver createNSResolver(Node nodeResolver); + // XPathResult.ANY_TYPE = 0 + XPathResult evaluate(DOMString expression, Node contextNode, optional XPathNSResolver? resolver = null, optional unsigned short type = 0, optional XPathResult? result = null); +}; +Document includes XPathEvaluatorBase; + +[Exposed=Window] +interface XPathEvaluator { + constructor(); +}; + +XPathEvaluator includes XPathEvaluatorBase; + +[Exposed=Window] +interface XSLTProcessor { + constructor(); + undefined importStylesheet(Node style); + [CEReactions] DocumentFragment transformToFragment(Node source, Document output); + [CEReactions] Document transformToDocument(Node source); + undefined setParameter([LegacyNullToEmptyString] DOMString namespaceURI, DOMString localName, any value); + any getParameter([LegacyNullToEmptyString] DOMString namespaceURI, DOMString localName); + undefined removeParameter([LegacyNullToEmptyString] DOMString namespaceURI, DOMString localName); + undefined clearParameters(); + undefined reset(); +}; diff --git a/test/wpt/tests/interfaces/fetch.idl b/test/wpt/tests/interfaces/fetch.idl new file mode 100644 index 00000000000..ba69c462f62 --- /dev/null +++ b/test/wpt/tests/interfaces/fetch.idl @@ -0,0 +1,114 @@ +// GENERATED CONTENT - DO NOT EDIT +// Content was automatically extracted by Reffy into webref +// (https://github.com/w3c/webref) +// Source: Fetch Standard (https://fetch.spec.whatwg.org/) + +typedef (sequence> or record) HeadersInit; + +[Exposed=(Window,Worker)] +interface Headers { + constructor(optional HeadersInit init); + + undefined append(ByteString name, ByteString value); + undefined delete(ByteString name); + ByteString? get(ByteString name); + boolean has(ByteString name); + undefined set(ByteString name, ByteString value); + iterable; +}; + +typedef (Blob or BufferSource or FormData or URLSearchParams or USVString) XMLHttpRequestBodyInit; + +typedef (ReadableStream or XMLHttpRequestBodyInit) BodyInit; +interface mixin Body { + readonly attribute ReadableStream? body; + readonly attribute boolean bodyUsed; + [NewObject] Promise arrayBuffer(); + [NewObject] Promise blob(); + [NewObject] Promise formData(); + [NewObject] Promise json(); + [NewObject] Promise text(); +}; +typedef (Request or USVString) RequestInfo; + +[Exposed=(Window,Worker)] +interface Request { + constructor(RequestInfo input, optional RequestInit init = {}); + + readonly attribute ByteString method; + readonly attribute USVString url; + [SameObject] readonly attribute Headers headers; + + readonly attribute RequestDestination destination; + readonly attribute USVString referrer; + readonly attribute ReferrerPolicy referrerPolicy; + readonly attribute RequestMode mode; + readonly attribute RequestCredentials credentials; + readonly attribute RequestCache cache; + readonly attribute RequestRedirect redirect; + readonly attribute DOMString integrity; + readonly attribute boolean keepalive; + readonly attribute boolean isReloadNavigation; + readonly attribute boolean isHistoryNavigation; + readonly attribute AbortSignal signal; + readonly attribute RequestDuplex duplex; + + [NewObject] Request clone(); +}; +Request includes Body; + +dictionary RequestInit { + ByteString method; + HeadersInit headers; + BodyInit? body; + USVString referrer; + ReferrerPolicy referrerPolicy; + RequestMode mode; + RequestCredentials credentials; + RequestCache cache; + RequestRedirect redirect; + DOMString integrity; + boolean keepalive; + AbortSignal? signal; + RequestDuplex duplex; + any window; // can only be set to null +}; + +enum RequestDestination { "", "audio", "audioworklet", "document", "embed", "font", "frame", "iframe", "image", "manifest", "object", "paintworklet", "report", "script", "sharedworker", "style", "track", "video", "worker", "xslt" }; +enum RequestMode { "navigate", "same-origin", "no-cors", "cors" }; +enum RequestCredentials { "omit", "same-origin", "include" }; +enum RequestCache { "default", "no-store", "reload", "no-cache", "force-cache", "only-if-cached" }; +enum RequestRedirect { "follow", "error", "manual" }; +enum RequestDuplex { "half" }; + +[Exposed=(Window,Worker)]interface Response { + constructor(optional BodyInit? body = null, optional ResponseInit init = {}); + + [NewObject] static Response error(); + [NewObject] static Response redirect(USVString url, optional unsigned short status = 302); + [NewObject] static Response json(any data, optional ResponseInit init = {}); + + readonly attribute ResponseType type; + + readonly attribute USVString url; + readonly attribute boolean redirected; + readonly attribute unsigned short status; + readonly attribute boolean ok; + readonly attribute ByteString statusText; + [SameObject] readonly attribute Headers headers; + + [NewObject] Response clone(); +}; +Response includes Body; + +dictionary ResponseInit { + unsigned short status = 200; + ByteString statusText = ""; + HeadersInit headers; +}; + +enum ResponseType { "basic", "cors", "default", "error", "opaque", "opaqueredirect" }; + +partial interface mixin WindowOrWorkerGlobalScope { + [NewObject] Promise fetch(RequestInfo input, optional RequestInit init = {}); +}; diff --git a/test/wpt/tests/interfaces/html.idl b/test/wpt/tests/interfaces/html.idl new file mode 100644 index 00000000000..17fe2890bb7 --- /dev/null +++ b/test/wpt/tests/interfaces/html.idl @@ -0,0 +1,2691 @@ +// GENERATED CONTENT - DO NOT EDIT +// Content was automatically extracted by Reffy into webref +// (https://github.com/w3c/webref) +// Source: HTML Standard (https://html.spec.whatwg.org/multipage/) + +[Exposed=Window, + LegacyUnenumerableNamedProperties] +interface HTMLAllCollection { + readonly attribute unsigned long length; + getter Element (unsigned long index); + getter (HTMLCollection or Element)? namedItem(DOMString name); + (HTMLCollection or Element)? item(optional DOMString nameOrIndex); + + // Note: HTMLAllCollection objects have a custom [[Call]] internal method and an [[IsHTMLDDA]] internal slot. +}; + +[Exposed=Window] +interface HTMLFormControlsCollection : HTMLCollection { + // inherits length and item() + getter (RadioNodeList or Element)? namedItem(DOMString name); // shadows inherited namedItem() +}; + +[Exposed=Window] +interface RadioNodeList : NodeList { + attribute DOMString value; +}; + +[Exposed=Window] +interface HTMLOptionsCollection : HTMLCollection { + // inherits item(), namedItem() + [CEReactions] attribute unsigned long length; // shadows inherited length + [CEReactions] setter undefined (unsigned long index, HTMLOptionElement? option); + [CEReactions] undefined add((HTMLOptionElement or HTMLOptGroupElement) element, optional (HTMLElement or long)? before = null); + [CEReactions] undefined remove(long index); + attribute long selectedIndex; +}; + +[Exposed=(Window,Worker)] +interface DOMStringList { + readonly attribute unsigned long length; + getter DOMString? item(unsigned long index); + boolean contains(DOMString string); +}; + +enum DocumentReadyState { "loading", "interactive", "complete" }; +enum DocumentVisibilityState { "visible", "hidden" }; +typedef (HTMLScriptElement or SVGScriptElement) HTMLOrSVGScriptElement; + +[LegacyOverrideBuiltIns] +partial interface Document { + // resource metadata management + [PutForwards=href, LegacyUnforgeable] readonly attribute Location? location; + attribute USVString domain; + readonly attribute USVString referrer; + attribute USVString cookie; + readonly attribute DOMString lastModified; + readonly attribute DocumentReadyState readyState; + + // DOM tree accessors + getter object (DOMString name); + [CEReactions] attribute DOMString title; + [CEReactions] attribute DOMString dir; + [CEReactions] attribute HTMLElement? body; + readonly attribute HTMLHeadElement? head; + [SameObject] readonly attribute HTMLCollection images; + [SameObject] readonly attribute HTMLCollection embeds; + [SameObject] readonly attribute HTMLCollection plugins; + [SameObject] readonly attribute HTMLCollection links; + [SameObject] readonly attribute HTMLCollection forms; + [SameObject] readonly attribute HTMLCollection scripts; + NodeList getElementsByName(DOMString elementName); + readonly attribute HTMLOrSVGScriptElement? currentScript; // classic scripts in a document tree only + + // dynamic markup insertion + [CEReactions] Document open(optional DOMString unused1, optional DOMString unused2); // both arguments are ignored + WindowProxy? open(USVString url, DOMString name, DOMString features); + [CEReactions] undefined close(); + [CEReactions] undefined write(DOMString... text); + [CEReactions] undefined writeln(DOMString... text); + + // user interaction + readonly attribute WindowProxy? defaultView; + boolean hasFocus(); + [CEReactions] attribute DOMString designMode; + [CEReactions] boolean execCommand(DOMString commandId, optional boolean showUI = false, optional DOMString value = ""); + boolean queryCommandEnabled(DOMString commandId); + boolean queryCommandIndeterm(DOMString commandId); + boolean queryCommandState(DOMString commandId); + boolean queryCommandSupported(DOMString commandId); + DOMString queryCommandValue(DOMString commandId); + readonly attribute boolean hidden; + readonly attribute DocumentVisibilityState visibilityState; + + // special event handler IDL attributes that only apply to Document objects + [LegacyLenientThis] attribute EventHandler onreadystatechange; + attribute EventHandler onvisibilitychange; + + // also has obsolete members +}; +Document includes GlobalEventHandlers; +Document includes DocumentAndElementEventHandlers; + +partial interface mixin DocumentOrShadowRoot { + readonly attribute Element? activeElement; +}; + +[Exposed=Window] +interface HTMLElement : Element { + [HTMLConstructor] constructor(); + + // metadata attributes + [CEReactions] attribute DOMString title; + [CEReactions] attribute DOMString lang; + [CEReactions] attribute boolean translate; + [CEReactions] attribute DOMString dir; + + // user interaction + [CEReactions] attribute (boolean or unrestricted double or DOMString)? hidden; + [CEReactions] attribute boolean inert; + undefined click(); + [CEReactions] attribute DOMString accessKey; + readonly attribute DOMString accessKeyLabel; + [CEReactions] attribute boolean draggable; + [CEReactions] attribute boolean spellcheck; + [CEReactions] attribute DOMString autocapitalize; + + [CEReactions] attribute [LegacyNullToEmptyString] DOMString innerText; + [CEReactions] attribute [LegacyNullToEmptyString] DOMString outerText; + + ElementInternals attachInternals(); +}; + +HTMLElement includes GlobalEventHandlers; +HTMLElement includes DocumentAndElementEventHandlers; +HTMLElement includes ElementContentEditable; +HTMLElement includes HTMLOrSVGElement; + +[Exposed=Window] +interface HTMLUnknownElement : HTMLElement { + // Note: intentionally no [HTMLConstructor] +}; + +interface mixin HTMLOrSVGElement { + [SameObject] readonly attribute DOMStringMap dataset; + attribute DOMString nonce; // intentionally no [CEReactions] + + [CEReactions] attribute boolean autofocus; + [CEReactions] attribute long tabIndex; + undefined focus(optional FocusOptions options = {}); + undefined blur(); +}; + +[Exposed=Window, + LegacyOverrideBuiltIns] +interface DOMStringMap { + getter DOMString (DOMString name); + [CEReactions] setter undefined (DOMString name, DOMString value); + [CEReactions] deleter undefined (DOMString name); +}; + +[Exposed=Window] +interface HTMLHtmlElement : HTMLElement { + [HTMLConstructor] constructor(); + + // also has obsolete members +}; + +[Exposed=Window] +interface HTMLHeadElement : HTMLElement { + [HTMLConstructor] constructor(); +}; + +[Exposed=Window] +interface HTMLTitleElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute DOMString text; +}; + +[Exposed=Window] +interface HTMLBaseElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute USVString href; + [CEReactions] attribute DOMString target; +}; + +[Exposed=Window] +interface HTMLLinkElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute USVString href; + [CEReactions] attribute DOMString? crossOrigin; + [CEReactions] attribute DOMString rel; + [CEReactions] attribute DOMString as; + [SameObject, PutForwards=value] readonly attribute DOMTokenList relList; + [CEReactions] attribute DOMString media; + [CEReactions] attribute DOMString integrity; + [CEReactions] attribute DOMString hreflang; + [CEReactions] attribute DOMString type; + [SameObject, PutForwards=value] readonly attribute DOMTokenList sizes; + [CEReactions] attribute USVString imageSrcset; + [CEReactions] attribute DOMString imageSizes; + [CEReactions] attribute DOMString referrerPolicy; + [SameObject, PutForwards=value] readonly attribute DOMTokenList blocking; + [CEReactions] attribute boolean disabled; + + // also has obsolete members +}; +HTMLLinkElement includes LinkStyle; + +[Exposed=Window] +interface HTMLMetaElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute DOMString name; + [CEReactions] attribute DOMString httpEquiv; + [CEReactions] attribute DOMString content; + [CEReactions] attribute DOMString media; + + // also has obsolete members +}; + +[Exposed=Window] +interface HTMLStyleElement : HTMLElement { + [HTMLConstructor] constructor(); + + attribute boolean disabled; + [CEReactions] attribute DOMString media; + [SameObject, PutForwards=value] readonly attribute DOMTokenList blocking; + + // also has obsolete members +}; +HTMLStyleElement includes LinkStyle; + +[Exposed=Window] +interface HTMLBodyElement : HTMLElement { + [HTMLConstructor] constructor(); + + // also has obsolete members +}; + +HTMLBodyElement includes WindowEventHandlers; + +[Exposed=Window] +interface HTMLHeadingElement : HTMLElement { + [HTMLConstructor] constructor(); + + // also has obsolete members +}; + +[Exposed=Window] +interface HTMLParagraphElement : HTMLElement { + [HTMLConstructor] constructor(); + + // also has obsolete members +}; + +[Exposed=Window] +interface HTMLHRElement : HTMLElement { + [HTMLConstructor] constructor(); + + // also has obsolete members +}; + +[Exposed=Window] +interface HTMLPreElement : HTMLElement { + [HTMLConstructor] constructor(); + + // also has obsolete members +}; + +[Exposed=Window] +interface HTMLQuoteElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute USVString cite; +}; + +[Exposed=Window] +interface HTMLOListElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute boolean reversed; + [CEReactions] attribute long start; + [CEReactions] attribute DOMString type; + + // also has obsolete members +}; + +[Exposed=Window] +interface HTMLUListElement : HTMLElement { + [HTMLConstructor] constructor(); + + // also has obsolete members +}; + +[Exposed=Window] +interface HTMLMenuElement : HTMLElement { + [HTMLConstructor] constructor(); + + // also has obsolete members +}; + +[Exposed=Window] +interface HTMLLIElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute long value; + + // also has obsolete members +}; + +[Exposed=Window] +interface HTMLDListElement : HTMLElement { + [HTMLConstructor] constructor(); + + // also has obsolete members +}; + +[Exposed=Window] +interface HTMLDivElement : HTMLElement { + [HTMLConstructor] constructor(); + + // also has obsolete members +}; + +[Exposed=Window] +interface HTMLAnchorElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute DOMString target; + [CEReactions] attribute DOMString download; + [CEReactions] attribute USVString ping; + [CEReactions] attribute DOMString rel; + [SameObject, PutForwards=value] readonly attribute DOMTokenList relList; + [CEReactions] attribute DOMString hreflang; + [CEReactions] attribute DOMString type; + + [CEReactions] attribute DOMString text; + + [CEReactions] attribute DOMString referrerPolicy; + + // also has obsolete members +}; +HTMLAnchorElement includes HTMLHyperlinkElementUtils; + +[Exposed=Window] +interface HTMLDataElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute DOMString value; +}; + +[Exposed=Window] +interface HTMLTimeElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute DOMString dateTime; +}; + +[Exposed=Window] +interface HTMLSpanElement : HTMLElement { + [HTMLConstructor] constructor(); +}; + +[Exposed=Window] +interface HTMLBRElement : HTMLElement { + [HTMLConstructor] constructor(); + + // also has obsolete members +}; + +interface mixin HTMLHyperlinkElementUtils { + [CEReactions] stringifier attribute USVString href; + readonly attribute USVString origin; + [CEReactions] attribute USVString protocol; + [CEReactions] attribute USVString username; + [CEReactions] attribute USVString password; + [CEReactions] attribute USVString host; + [CEReactions] attribute USVString hostname; + [CEReactions] attribute USVString port; + [CEReactions] attribute USVString pathname; + [CEReactions] attribute USVString search; + [CEReactions] attribute USVString hash; +}; + +[Exposed=Window] +interface HTMLModElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute USVString cite; + [CEReactions] attribute DOMString dateTime; +}; + +[Exposed=Window] +interface HTMLPictureElement : HTMLElement { + [HTMLConstructor] constructor(); +}; + +[Exposed=Window] +interface HTMLSourceElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute USVString src; + [CEReactions] attribute DOMString type; + [CEReactions] attribute USVString srcset; + [CEReactions] attribute DOMString sizes; + [CEReactions] attribute DOMString media; + [CEReactions] attribute unsigned long width; + [CEReactions] attribute unsigned long height; +}; + +[Exposed=Window, + LegacyFactoryFunction=Image(optional unsigned long width, optional unsigned long height)] +interface HTMLImageElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute DOMString alt; + [CEReactions] attribute USVString src; + [CEReactions] attribute USVString srcset; + [CEReactions] attribute DOMString sizes; + [CEReactions] attribute DOMString? crossOrigin; + [CEReactions] attribute DOMString useMap; + [CEReactions] attribute boolean isMap; + [CEReactions] attribute unsigned long width; + [CEReactions] attribute unsigned long height; + readonly attribute unsigned long naturalWidth; + readonly attribute unsigned long naturalHeight; + readonly attribute boolean complete; + readonly attribute USVString currentSrc; + [CEReactions] attribute DOMString referrerPolicy; + [CEReactions] attribute DOMString decoding; + [CEReactions] attribute DOMString loading; + + Promise decode(); + + // also has obsolete members +}; + +[Exposed=Window] +interface HTMLIFrameElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute USVString src; + [CEReactions] attribute DOMString srcdoc; + [CEReactions] attribute DOMString name; + [SameObject, PutForwards=value] readonly attribute DOMTokenList sandbox; + [CEReactions] attribute DOMString allow; + [CEReactions] attribute boolean allowFullscreen; + [CEReactions] attribute DOMString width; + [CEReactions] attribute DOMString height; + [CEReactions] attribute DOMString referrerPolicy; + [CEReactions] attribute DOMString loading; + readonly attribute Document? contentDocument; + readonly attribute WindowProxy? contentWindow; + Document? getSVGDocument(); + + // also has obsolete members +}; + +[Exposed=Window] +interface HTMLEmbedElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute USVString src; + [CEReactions] attribute DOMString type; + [CEReactions] attribute DOMString width; + [CEReactions] attribute DOMString height; + Document? getSVGDocument(); + + // also has obsolete members +}; + +[Exposed=Window] +interface HTMLObjectElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute USVString data; + [CEReactions] attribute DOMString type; + [CEReactions] attribute DOMString name; + readonly attribute HTMLFormElement? form; + [CEReactions] attribute DOMString width; + [CEReactions] attribute DOMString height; + readonly attribute Document? contentDocument; + readonly attribute WindowProxy? contentWindow; + Document? getSVGDocument(); + + readonly attribute boolean willValidate; + readonly attribute ValidityState validity; + readonly attribute DOMString validationMessage; + boolean checkValidity(); + boolean reportValidity(); + undefined setCustomValidity(DOMString error); + + // also has obsolete members +}; + +[Exposed=Window] +interface HTMLVideoElement : HTMLMediaElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute unsigned long width; + [CEReactions] attribute unsigned long height; + readonly attribute unsigned long videoWidth; + readonly attribute unsigned long videoHeight; + [CEReactions] attribute USVString poster; + [CEReactions] attribute boolean playsInline; +}; + +[Exposed=Window, + LegacyFactoryFunction=Audio(optional DOMString src)] +interface HTMLAudioElement : HTMLMediaElement { + [HTMLConstructor] constructor(); +}; + +[Exposed=Window] +interface HTMLTrackElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute DOMString kind; + [CEReactions] attribute USVString src; + [CEReactions] attribute DOMString srclang; + [CEReactions] attribute DOMString label; + [CEReactions] attribute boolean default; + + const unsigned short NONE = 0; + const unsigned short LOADING = 1; + const unsigned short LOADED = 2; + const unsigned short ERROR = 3; + readonly attribute unsigned short readyState; + + readonly attribute TextTrack track; +}; + +enum CanPlayTypeResult { "" /* empty string */, "maybe", "probably" }; +typedef (MediaStream or MediaSource or Blob) MediaProvider; + +[Exposed=Window] +interface HTMLMediaElement : HTMLElement { + + // error state + readonly attribute MediaError? error; + + // network state + [CEReactions] attribute USVString src; + attribute MediaProvider? srcObject; + readonly attribute USVString currentSrc; + [CEReactions] attribute DOMString? crossOrigin; + const unsigned short NETWORK_EMPTY = 0; + const unsigned short NETWORK_IDLE = 1; + const unsigned short NETWORK_LOADING = 2; + const unsigned short NETWORK_NO_SOURCE = 3; + readonly attribute unsigned short networkState; + [CEReactions] attribute DOMString preload; + readonly attribute TimeRanges buffered; + undefined load(); + CanPlayTypeResult canPlayType(DOMString type); + + // ready state + const unsigned short HAVE_NOTHING = 0; + const unsigned short HAVE_METADATA = 1; + const unsigned short HAVE_CURRENT_DATA = 2; + const unsigned short HAVE_FUTURE_DATA = 3; + const unsigned short HAVE_ENOUGH_DATA = 4; + readonly attribute unsigned short readyState; + readonly attribute boolean seeking; + + // playback state + attribute double currentTime; + undefined fastSeek(double time); + readonly attribute unrestricted double duration; + object getStartDate(); + readonly attribute boolean paused; + attribute double defaultPlaybackRate; + attribute double playbackRate; + attribute boolean preservesPitch; + readonly attribute TimeRanges played; + readonly attribute TimeRanges seekable; + readonly attribute boolean ended; + [CEReactions] attribute boolean autoplay; + [CEReactions] attribute boolean loop; + Promise play(); + undefined pause(); + + // controls + [CEReactions] attribute boolean controls; + attribute double volume; + attribute boolean muted; + [CEReactions] attribute boolean defaultMuted; + + // tracks + [SameObject] readonly attribute AudioTrackList audioTracks; + [SameObject] readonly attribute VideoTrackList videoTracks; + [SameObject] readonly attribute TextTrackList textTracks; + TextTrack addTextTrack(TextTrackKind kind, optional DOMString label = "", optional DOMString language = ""); +}; + +[Exposed=Window] +interface MediaError { + const unsigned short MEDIA_ERR_ABORTED = 1; + const unsigned short MEDIA_ERR_NETWORK = 2; + const unsigned short MEDIA_ERR_DECODE = 3; + const unsigned short MEDIA_ERR_SRC_NOT_SUPPORTED = 4; + + readonly attribute unsigned short code; + readonly attribute DOMString message; +}; + +[Exposed=Window] +interface AudioTrackList : EventTarget { + readonly attribute unsigned long length; + getter AudioTrack (unsigned long index); + AudioTrack? getTrackById(DOMString id); + + attribute EventHandler onchange; + attribute EventHandler onaddtrack; + attribute EventHandler onremovetrack; +}; + +[Exposed=Window] +interface AudioTrack { + readonly attribute DOMString id; + readonly attribute DOMString kind; + readonly attribute DOMString label; + readonly attribute DOMString language; + attribute boolean enabled; +}; + +[Exposed=Window] +interface VideoTrackList : EventTarget { + readonly attribute unsigned long length; + getter VideoTrack (unsigned long index); + VideoTrack? getTrackById(DOMString id); + readonly attribute long selectedIndex; + + attribute EventHandler onchange; + attribute EventHandler onaddtrack; + attribute EventHandler onremovetrack; +}; + +[Exposed=Window] +interface VideoTrack { + readonly attribute DOMString id; + readonly attribute DOMString kind; + readonly attribute DOMString label; + readonly attribute DOMString language; + attribute boolean selected; +}; + +[Exposed=Window] +interface TextTrackList : EventTarget { + readonly attribute unsigned long length; + getter TextTrack (unsigned long index); + TextTrack? getTrackById(DOMString id); + + attribute EventHandler onchange; + attribute EventHandler onaddtrack; + attribute EventHandler onremovetrack; +}; + +enum TextTrackMode { "disabled", "hidden", "showing" }; +enum TextTrackKind { "subtitles", "captions", "descriptions", "chapters", "metadata" }; + +[Exposed=Window] +interface TextTrack : EventTarget { + readonly attribute TextTrackKind kind; + readonly attribute DOMString label; + readonly attribute DOMString language; + + readonly attribute DOMString id; + readonly attribute DOMString inBandMetadataTrackDispatchType; + + attribute TextTrackMode mode; + + readonly attribute TextTrackCueList? cues; + readonly attribute TextTrackCueList? activeCues; + + undefined addCue(TextTrackCue cue); + undefined removeCue(TextTrackCue cue); + + attribute EventHandler oncuechange; +}; + +[Exposed=Window] +interface TextTrackCueList { + readonly attribute unsigned long length; + getter TextTrackCue (unsigned long index); + TextTrackCue? getCueById(DOMString id); +}; + +[Exposed=Window] +interface TextTrackCue : EventTarget { + readonly attribute TextTrack? track; + + attribute DOMString id; + attribute double startTime; + attribute unrestricted double endTime; + attribute boolean pauseOnExit; + + attribute EventHandler onenter; + attribute EventHandler onexit; +}; + +[Exposed=Window] +interface TimeRanges { + readonly attribute unsigned long length; + double start(unsigned long index); + double end(unsigned long index); +}; + +[Exposed=Window] +interface TrackEvent : Event { + constructor(DOMString type, optional TrackEventInit eventInitDict = {}); + + readonly attribute (VideoTrack or AudioTrack or TextTrack)? track; +}; + +dictionary TrackEventInit : EventInit { + (VideoTrack or AudioTrack or TextTrack)? track = null; +}; + +[Exposed=Window] +interface HTMLMapElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute DOMString name; + [SameObject] readonly attribute HTMLCollection areas; +}; + +[Exposed=Window] +interface HTMLAreaElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute DOMString alt; + [CEReactions] attribute DOMString coords; + [CEReactions] attribute DOMString shape; + [CEReactions] attribute DOMString target; + [CEReactions] attribute DOMString download; + [CEReactions] attribute USVString ping; + [CEReactions] attribute DOMString rel; + [SameObject, PutForwards=value] readonly attribute DOMTokenList relList; + [CEReactions] attribute DOMString referrerPolicy; + + // also has obsolete members +}; +HTMLAreaElement includes HTMLHyperlinkElementUtils; + +[Exposed=Window] +interface HTMLTableElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute HTMLTableCaptionElement? caption; + HTMLTableCaptionElement createCaption(); + [CEReactions] undefined deleteCaption(); + + [CEReactions] attribute HTMLTableSectionElement? tHead; + HTMLTableSectionElement createTHead(); + [CEReactions] undefined deleteTHead(); + + [CEReactions] attribute HTMLTableSectionElement? tFoot; + HTMLTableSectionElement createTFoot(); + [CEReactions] undefined deleteTFoot(); + + [SameObject] readonly attribute HTMLCollection tBodies; + HTMLTableSectionElement createTBody(); + + [SameObject] readonly attribute HTMLCollection rows; + HTMLTableRowElement insertRow(optional long index = -1); + [CEReactions] undefined deleteRow(long index); + + // also has obsolete members +}; + +[Exposed=Window] +interface HTMLTableCaptionElement : HTMLElement { + [HTMLConstructor] constructor(); + + // also has obsolete members +}; + +[Exposed=Window] +interface HTMLTableColElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute unsigned long span; + + // also has obsolete members +}; + +[Exposed=Window] +interface HTMLTableSectionElement : HTMLElement { + [HTMLConstructor] constructor(); + + [SameObject] readonly attribute HTMLCollection rows; + HTMLTableRowElement insertRow(optional long index = -1); + [CEReactions] undefined deleteRow(long index); + + // also has obsolete members +}; + +[Exposed=Window] +interface HTMLTableRowElement : HTMLElement { + [HTMLConstructor] constructor(); + + readonly attribute long rowIndex; + readonly attribute long sectionRowIndex; + [SameObject] readonly attribute HTMLCollection cells; + HTMLTableCellElement insertCell(optional long index = -1); + [CEReactions] undefined deleteCell(long index); + + // also has obsolete members +}; + +[Exposed=Window] +interface HTMLTableCellElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute unsigned long colSpan; + [CEReactions] attribute unsigned long rowSpan; + [CEReactions] attribute DOMString headers; + readonly attribute long cellIndex; + + [CEReactions] attribute DOMString scope; // only conforming for th elements + [CEReactions] attribute DOMString abbr; // only conforming for th elements + + // also has obsolete members +}; + +[Exposed=Window, + LegacyOverrideBuiltIns, + LegacyUnenumerableNamedProperties] +interface HTMLFormElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute DOMString acceptCharset; + [CEReactions] attribute USVString action; + [CEReactions] attribute DOMString autocomplete; + [CEReactions] attribute DOMString enctype; + [CEReactions] attribute DOMString encoding; + [CEReactions] attribute DOMString method; + [CEReactions] attribute DOMString name; + [CEReactions] attribute boolean noValidate; + [CEReactions] attribute DOMString target; + [CEReactions] attribute DOMString rel; + [SameObject, PutForwards=value] readonly attribute DOMTokenList relList; + + [SameObject] readonly attribute HTMLFormControlsCollection elements; + readonly attribute unsigned long length; + getter Element (unsigned long index); + getter (RadioNodeList or Element) (DOMString name); + + undefined submit(); + undefined requestSubmit(optional HTMLElement? submitter = null); + [CEReactions] undefined reset(); + boolean checkValidity(); + boolean reportValidity(); +}; + +[Exposed=Window] +interface HTMLLabelElement : HTMLElement { + [HTMLConstructor] constructor(); + + readonly attribute HTMLFormElement? form; + [CEReactions] attribute DOMString htmlFor; + readonly attribute HTMLElement? control; +}; + +[Exposed=Window] +interface HTMLInputElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute DOMString accept; + [CEReactions] attribute DOMString alt; + [CEReactions] attribute DOMString autocomplete; + [CEReactions] attribute boolean defaultChecked; + attribute boolean checked; + [CEReactions] attribute DOMString dirName; + [CEReactions] attribute boolean disabled; + readonly attribute HTMLFormElement? form; + attribute FileList? files; + [CEReactions] attribute USVString formAction; + [CEReactions] attribute DOMString formEnctype; + [CEReactions] attribute DOMString formMethod; + [CEReactions] attribute boolean formNoValidate; + [CEReactions] attribute DOMString formTarget; + [CEReactions] attribute unsigned long height; + attribute boolean indeterminate; + readonly attribute HTMLElement? list; + [CEReactions] attribute DOMString max; + [CEReactions] attribute long maxLength; + [CEReactions] attribute DOMString min; + [CEReactions] attribute long minLength; + [CEReactions] attribute boolean multiple; + [CEReactions] attribute DOMString name; + [CEReactions] attribute DOMString pattern; + [CEReactions] attribute DOMString placeholder; + [CEReactions] attribute boolean readOnly; + [CEReactions] attribute boolean required; + [CEReactions] attribute unsigned long size; + [CEReactions] attribute USVString src; + [CEReactions] attribute DOMString step; + [CEReactions] attribute DOMString type; + [CEReactions] attribute DOMString defaultValue; + [CEReactions] attribute [LegacyNullToEmptyString] DOMString value; + attribute object? valueAsDate; + attribute unrestricted double valueAsNumber; + [CEReactions] attribute unsigned long width; + + undefined stepUp(optional long n = 1); + undefined stepDown(optional long n = 1); + + readonly attribute boolean willValidate; + readonly attribute ValidityState validity; + readonly attribute DOMString validationMessage; + boolean checkValidity(); + boolean reportValidity(); + undefined setCustomValidity(DOMString error); + + readonly attribute NodeList? labels; + + undefined select(); + attribute unsigned long? selectionStart; + attribute unsigned long? selectionEnd; + attribute DOMString? selectionDirection; + undefined setRangeText(DOMString replacement); + undefined setRangeText(DOMString replacement, unsigned long start, unsigned long end, optional SelectionMode selectionMode = "preserve"); + undefined setSelectionRange(unsigned long start, unsigned long end, optional DOMString direction); + + undefined showPicker(); + + // also has obsolete members +}; + +[Exposed=Window] +interface HTMLButtonElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute boolean disabled; + readonly attribute HTMLFormElement? form; + [CEReactions] attribute USVString formAction; + [CEReactions] attribute DOMString formEnctype; + [CEReactions] attribute DOMString formMethod; + [CEReactions] attribute boolean formNoValidate; + [CEReactions] attribute DOMString formTarget; + [CEReactions] attribute DOMString name; + [CEReactions] attribute DOMString type; + [CEReactions] attribute DOMString value; + + readonly attribute boolean willValidate; + readonly attribute ValidityState validity; + readonly attribute DOMString validationMessage; + boolean checkValidity(); + boolean reportValidity(); + undefined setCustomValidity(DOMString error); + + readonly attribute NodeList labels; +}; + +[Exposed=Window] +interface HTMLSelectElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute DOMString autocomplete; + [CEReactions] attribute boolean disabled; + readonly attribute HTMLFormElement? form; + [CEReactions] attribute boolean multiple; + [CEReactions] attribute DOMString name; + [CEReactions] attribute boolean required; + [CEReactions] attribute unsigned long size; + + readonly attribute DOMString type; + + [SameObject] readonly attribute HTMLOptionsCollection options; + [CEReactions] attribute unsigned long length; + getter HTMLOptionElement? item(unsigned long index); + HTMLOptionElement? namedItem(DOMString name); + [CEReactions] undefined add((HTMLOptionElement or HTMLOptGroupElement) element, optional (HTMLElement or long)? before = null); + [CEReactions] undefined remove(); // ChildNode overload + [CEReactions] undefined remove(long index); + [CEReactions] setter undefined (unsigned long index, HTMLOptionElement? option); + + [SameObject] readonly attribute HTMLCollection selectedOptions; + attribute long selectedIndex; + attribute DOMString value; + + readonly attribute boolean willValidate; + readonly attribute ValidityState validity; + readonly attribute DOMString validationMessage; + boolean checkValidity(); + boolean reportValidity(); + undefined setCustomValidity(DOMString error); + + readonly attribute NodeList labels; +}; + +[Exposed=Window] +interface HTMLDataListElement : HTMLElement { + [HTMLConstructor] constructor(); + + [SameObject] readonly attribute HTMLCollection options; +}; + +[Exposed=Window] +interface HTMLOptGroupElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute boolean disabled; + [CEReactions] attribute DOMString label; +}; + +[Exposed=Window, + LegacyFactoryFunction=Option(optional DOMString text = "", optional DOMString value, optional boolean defaultSelected = false, optional boolean selected = false)] +interface HTMLOptionElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute boolean disabled; + readonly attribute HTMLFormElement? form; + [CEReactions] attribute DOMString label; + [CEReactions] attribute boolean defaultSelected; + attribute boolean selected; + [CEReactions] attribute DOMString value; + + [CEReactions] attribute DOMString text; + readonly attribute long index; +}; + +[Exposed=Window] +interface HTMLTextAreaElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute DOMString autocomplete; + [CEReactions] attribute unsigned long cols; + [CEReactions] attribute DOMString dirName; + [CEReactions] attribute boolean disabled; + readonly attribute HTMLFormElement? form; + [CEReactions] attribute long maxLength; + [CEReactions] attribute long minLength; + [CEReactions] attribute DOMString name; + [CEReactions] attribute DOMString placeholder; + [CEReactions] attribute boolean readOnly; + [CEReactions] attribute boolean required; + [CEReactions] attribute unsigned long rows; + [CEReactions] attribute DOMString wrap; + + readonly attribute DOMString type; + [CEReactions] attribute DOMString defaultValue; + attribute [LegacyNullToEmptyString] DOMString value; + readonly attribute unsigned long textLength; + + readonly attribute boolean willValidate; + readonly attribute ValidityState validity; + readonly attribute DOMString validationMessage; + boolean checkValidity(); + boolean reportValidity(); + undefined setCustomValidity(DOMString error); + + readonly attribute NodeList labels; + + undefined select(); + attribute unsigned long selectionStart; + attribute unsigned long selectionEnd; + attribute DOMString selectionDirection; + undefined setRangeText(DOMString replacement); + undefined setRangeText(DOMString replacement, unsigned long start, unsigned long end, optional SelectionMode selectionMode = "preserve"); + undefined setSelectionRange(unsigned long start, unsigned long end, optional DOMString direction); +}; + +[Exposed=Window] +interface HTMLOutputElement : HTMLElement { + [HTMLConstructor] constructor(); + + [SameObject, PutForwards=value] readonly attribute DOMTokenList htmlFor; + readonly attribute HTMLFormElement? form; + [CEReactions] attribute DOMString name; + + readonly attribute DOMString type; + [CEReactions] attribute DOMString defaultValue; + [CEReactions] attribute DOMString value; + + readonly attribute boolean willValidate; + readonly attribute ValidityState validity; + readonly attribute DOMString validationMessage; + boolean checkValidity(); + boolean reportValidity(); + undefined setCustomValidity(DOMString error); + + readonly attribute NodeList labels; +}; + +[Exposed=Window] +interface HTMLProgressElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute double value; + [CEReactions] attribute double max; + readonly attribute double position; + readonly attribute NodeList labels; +}; + +[Exposed=Window] +interface HTMLMeterElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute double value; + [CEReactions] attribute double min; + [CEReactions] attribute double max; + [CEReactions] attribute double low; + [CEReactions] attribute double high; + [CEReactions] attribute double optimum; + readonly attribute NodeList labels; +}; + +[Exposed=Window] +interface HTMLFieldSetElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute boolean disabled; + readonly attribute HTMLFormElement? form; + [CEReactions] attribute DOMString name; + + readonly attribute DOMString type; + + [SameObject] readonly attribute HTMLCollection elements; + + readonly attribute boolean willValidate; + [SameObject] readonly attribute ValidityState validity; + readonly attribute DOMString validationMessage; + boolean checkValidity(); + boolean reportValidity(); + undefined setCustomValidity(DOMString error); +}; + +[Exposed=Window] +interface HTMLLegendElement : HTMLElement { + [HTMLConstructor] constructor(); + + readonly attribute HTMLFormElement? form; + + // also has obsolete members +}; + +enum SelectionMode { + "select", + "start", + "end", + "preserve" // default +}; + +[Exposed=Window] +interface ValidityState { + readonly attribute boolean valueMissing; + readonly attribute boolean typeMismatch; + readonly attribute boolean patternMismatch; + readonly attribute boolean tooLong; + readonly attribute boolean tooShort; + readonly attribute boolean rangeUnderflow; + readonly attribute boolean rangeOverflow; + readonly attribute boolean stepMismatch; + readonly attribute boolean badInput; + readonly attribute boolean customError; + readonly attribute boolean valid; +}; + +[Exposed=Window] +interface SubmitEvent : Event { + constructor(DOMString type, optional SubmitEventInit eventInitDict = {}); + + readonly attribute HTMLElement? submitter; +}; + +dictionary SubmitEventInit : EventInit { + HTMLElement? submitter = null; +}; + +[Exposed=Window] +interface FormDataEvent : Event { + constructor(DOMString type, FormDataEventInit eventInitDict); + + readonly attribute FormData formData; +}; + +dictionary FormDataEventInit : EventInit { + required FormData formData; +}; + +[Exposed=Window] +interface HTMLDetailsElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute boolean open; +}; + +[Exposed=Window] +interface HTMLDialogElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute boolean open; + attribute DOMString returnValue; + [CEReactions] undefined show(); + [CEReactions] undefined showModal(); + [CEReactions] undefined close(optional DOMString returnValue); +}; + +[Exposed=Window] +interface HTMLScriptElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute USVString src; + [CEReactions] attribute DOMString type; + [CEReactions] attribute boolean noModule; + [CEReactions] attribute boolean async; + [CEReactions] attribute boolean defer; + [CEReactions] attribute DOMString? crossOrigin; + [CEReactions] attribute DOMString text; + [CEReactions] attribute DOMString integrity; + [CEReactions] attribute DOMString referrerPolicy; + [SameObject, PutForwards=value] readonly attribute DOMTokenList blocking; + + static boolean supports(DOMString type); + + // also has obsolete members +}; + +[Exposed=Window] +interface HTMLTemplateElement : HTMLElement { + [HTMLConstructor] constructor(); + + readonly attribute DocumentFragment content; +}; + +[Exposed=Window] +interface HTMLSlotElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute DOMString name; + sequence assignedNodes(optional AssignedNodesOptions options = {}); + sequence assignedElements(optional AssignedNodesOptions options = {}); + undefined assign((Element or Text)... nodes); +}; + +dictionary AssignedNodesOptions { + boolean flatten = false; +}; + +typedef (CanvasRenderingContext2D or ImageBitmapRenderingContext or WebGLRenderingContext or WebGL2RenderingContext or GPUCanvasContext) RenderingContext; + +[Exposed=Window] +interface HTMLCanvasElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute unsigned long width; + [CEReactions] attribute unsigned long height; + + RenderingContext? getContext(DOMString contextId, optional any options = null); + + USVString toDataURL(optional DOMString type = "image/png", optional any quality); + undefined toBlob(BlobCallback _callback, optional DOMString type = "image/png", optional any quality); + OffscreenCanvas transferControlToOffscreen(); +}; + +callback BlobCallback = undefined (Blob? blob); + +typedef (HTMLImageElement or + SVGImageElement) HTMLOrSVGImageElement; + +typedef (HTMLOrSVGImageElement or + HTMLVideoElement or + HTMLCanvasElement or + ImageBitmap or + OffscreenCanvas or + VideoFrame) CanvasImageSource; + +enum PredefinedColorSpace { "srgb", "display-p3" }; + +enum CanvasFillRule { "nonzero", "evenodd" }; + +dictionary CanvasRenderingContext2DSettings { + boolean alpha = true; + boolean desynchronized = false; + PredefinedColorSpace colorSpace = "srgb"; + boolean willReadFrequently = false; +}; + +enum ImageSmoothingQuality { "low", "medium", "high" }; + +[Exposed=Window] +interface CanvasRenderingContext2D { + // back-reference to the canvas + readonly attribute HTMLCanvasElement canvas; + + CanvasRenderingContext2DSettings getContextAttributes(); +}; +CanvasRenderingContext2D includes CanvasState; +CanvasRenderingContext2D includes CanvasTransform; +CanvasRenderingContext2D includes CanvasCompositing; +CanvasRenderingContext2D includes CanvasImageSmoothing; +CanvasRenderingContext2D includes CanvasFillStrokeStyles; +CanvasRenderingContext2D includes CanvasShadowStyles; +CanvasRenderingContext2D includes CanvasFilters; +CanvasRenderingContext2D includes CanvasRect; +CanvasRenderingContext2D includes CanvasDrawPath; +CanvasRenderingContext2D includes CanvasUserInterface; +CanvasRenderingContext2D includes CanvasText; +CanvasRenderingContext2D includes CanvasDrawImage; +CanvasRenderingContext2D includes CanvasImageData; +CanvasRenderingContext2D includes CanvasPathDrawingStyles; +CanvasRenderingContext2D includes CanvasTextDrawingStyles; +CanvasRenderingContext2D includes CanvasPath; + +interface mixin CanvasState { + // state + undefined save(); // push state on state stack + undefined restore(); // pop state stack and restore state + undefined reset(); // reset the rendering context to its default state + boolean isContextLost(); // return whether context is lost +}; + +interface mixin CanvasTransform { + // transformations (default transform is the identity matrix) + undefined scale(unrestricted double x, unrestricted double y); + undefined rotate(unrestricted double angle); + undefined translate(unrestricted double x, unrestricted double y); + undefined transform(unrestricted double a, unrestricted double b, unrestricted double c, unrestricted double d, unrestricted double e, unrestricted double f); + + [NewObject] DOMMatrix getTransform(); + undefined setTransform(unrestricted double a, unrestricted double b, unrestricted double c, unrestricted double d, unrestricted double e, unrestricted double f); + undefined setTransform(optional DOMMatrix2DInit transform = {}); + undefined resetTransform(); + +}; + +interface mixin CanvasCompositing { + // compositing + attribute unrestricted double globalAlpha; // (default 1.0) + attribute DOMString globalCompositeOperation; // (default "source-over") +}; + +interface mixin CanvasImageSmoothing { + // image smoothing + attribute boolean imageSmoothingEnabled; // (default true) + attribute ImageSmoothingQuality imageSmoothingQuality; // (default low) + +}; + +interface mixin CanvasFillStrokeStyles { + // colors and styles (see also the CanvasPathDrawingStyles and CanvasTextDrawingStyles interfaces) + attribute (DOMString or CanvasGradient or CanvasPattern) strokeStyle; // (default black) + attribute (DOMString or CanvasGradient or CanvasPattern) fillStyle; // (default black) + CanvasGradient createLinearGradient(double x0, double y0, double x1, double y1); + CanvasGradient createRadialGradient(double x0, double y0, double r0, double x1, double y1, double r1); + CanvasGradient createConicGradient(double startAngle, double x, double y); + CanvasPattern? createPattern(CanvasImageSource image, [LegacyNullToEmptyString] DOMString repetition); + +}; + +interface mixin CanvasShadowStyles { + // shadows + attribute unrestricted double shadowOffsetX; // (default 0) + attribute unrestricted double shadowOffsetY; // (default 0) + attribute unrestricted double shadowBlur; // (default 0) + attribute DOMString shadowColor; // (default transparent black) +}; + +interface mixin CanvasFilters { + // filters + attribute DOMString filter; // (default "none") +}; + +interface mixin CanvasRect { + // rects + undefined clearRect(unrestricted double x, unrestricted double y, unrestricted double w, unrestricted double h); + undefined fillRect(unrestricted double x, unrestricted double y, unrestricted double w, unrestricted double h); + undefined strokeRect(unrestricted double x, unrestricted double y, unrestricted double w, unrestricted double h); +}; + +interface mixin CanvasDrawPath { + // path API (see also CanvasPath) + undefined beginPath(); + undefined fill(optional CanvasFillRule fillRule = "nonzero"); + undefined fill(Path2D path, optional CanvasFillRule fillRule = "nonzero"); + undefined stroke(); + undefined stroke(Path2D path); + undefined clip(optional CanvasFillRule fillRule = "nonzero"); + undefined clip(Path2D path, optional CanvasFillRule fillRule = "nonzero"); + boolean isPointInPath(unrestricted double x, unrestricted double y, optional CanvasFillRule fillRule = "nonzero"); + boolean isPointInPath(Path2D path, unrestricted double x, unrestricted double y, optional CanvasFillRule fillRule = "nonzero"); + boolean isPointInStroke(unrestricted double x, unrestricted double y); + boolean isPointInStroke(Path2D path, unrestricted double x, unrestricted double y); +}; + +interface mixin CanvasUserInterface { + undefined drawFocusIfNeeded(Element element); + undefined drawFocusIfNeeded(Path2D path, Element element); + undefined scrollPathIntoView(); + undefined scrollPathIntoView(Path2D path); +}; + +interface mixin CanvasText { + // text (see also the CanvasPathDrawingStyles and CanvasTextDrawingStyles interfaces) + undefined fillText(DOMString text, unrestricted double x, unrestricted double y, optional unrestricted double maxWidth); + undefined strokeText(DOMString text, unrestricted double x, unrestricted double y, optional unrestricted double maxWidth); + TextMetrics measureText(DOMString text); +}; + +interface mixin CanvasDrawImage { + // drawing images + undefined drawImage(CanvasImageSource image, unrestricted double dx, unrestricted double dy); + undefined drawImage(CanvasImageSource image, unrestricted double dx, unrestricted double dy, unrestricted double dw, unrestricted double dh); + undefined drawImage(CanvasImageSource image, unrestricted double sx, unrestricted double sy, unrestricted double sw, unrestricted double sh, unrestricted double dx, unrestricted double dy, unrestricted double dw, unrestricted double dh); +}; + +interface mixin CanvasImageData { + // pixel manipulation + ImageData createImageData([EnforceRange] long sw, [EnforceRange] long sh, optional ImageDataSettings settings = {}); + ImageData createImageData(ImageData imagedata); + ImageData getImageData([EnforceRange] long sx, [EnforceRange] long sy, [EnforceRange] long sw, [EnforceRange] long sh, optional ImageDataSettings settings = {}); + undefined putImageData(ImageData imagedata, [EnforceRange] long dx, [EnforceRange] long dy); + undefined putImageData(ImageData imagedata, [EnforceRange] long dx, [EnforceRange] long dy, [EnforceRange] long dirtyX, [EnforceRange] long dirtyY, [EnforceRange] long dirtyWidth, [EnforceRange] long dirtyHeight); +}; + +enum CanvasLineCap { "butt", "round", "square" }; +enum CanvasLineJoin { "round", "bevel", "miter" }; +enum CanvasTextAlign { "start", "end", "left", "right", "center" }; +enum CanvasTextBaseline { "top", "hanging", "middle", "alphabetic", "ideographic", "bottom" }; +enum CanvasDirection { "ltr", "rtl", "inherit" }; +enum CanvasFontKerning { "auto", "normal", "none" }; +enum CanvasFontStretch { "ultra-condensed", "extra-condensed", "condensed", "semi-condensed", "normal", "semi-expanded", "expanded", "extra-expanded", "ultra-expanded" }; +enum CanvasFontVariantCaps { "normal", "small-caps", "all-small-caps", "petite-caps", "all-petite-caps", "unicase", "titling-caps" }; +enum CanvasTextRendering { "auto", "optimizeSpeed", "optimizeLegibility", "geometricPrecision" }; + +interface mixin CanvasPathDrawingStyles { + // line caps/joins + attribute unrestricted double lineWidth; // (default 1) + attribute CanvasLineCap lineCap; // (default "butt") + attribute CanvasLineJoin lineJoin; // (default "miter") + attribute unrestricted double miterLimit; // (default 10) + + // dashed lines + undefined setLineDash(sequence segments); // default empty + sequence getLineDash(); + attribute unrestricted double lineDashOffset; +}; + +interface mixin CanvasTextDrawingStyles { + // text + attribute DOMString font; // (default 10px sans-serif) + attribute CanvasTextAlign textAlign; // (default: "start") + attribute CanvasTextBaseline textBaseline; // (default: "alphabetic") + attribute CanvasDirection direction; // (default: "inherit") + attribute DOMString letterSpacing; // (default: "0px") + attribute CanvasFontKerning fontKerning; // (default: "auto") + attribute CanvasFontStretch fontStretch; // (default: "normal") + attribute CanvasFontVariantCaps fontVariantCaps; // (default: "normal") + attribute CanvasTextRendering textRendering; // (default: "auto") + attribute DOMString wordSpacing; // (default: "0px") +}; + +interface mixin CanvasPath { + // shared path API methods + undefined closePath(); + undefined moveTo(unrestricted double x, unrestricted double y); + undefined lineTo(unrestricted double x, unrestricted double y); + undefined quadraticCurveTo(unrestricted double cpx, unrestricted double cpy, unrestricted double x, unrestricted double y); + undefined bezierCurveTo(unrestricted double cp1x, unrestricted double cp1y, unrestricted double cp2x, unrestricted double cp2y, unrestricted double x, unrestricted double y); + undefined arcTo(unrestricted double x1, unrestricted double y1, unrestricted double x2, unrestricted double y2, unrestricted double radius); + undefined rect(unrestricted double x, unrestricted double y, unrestricted double w, unrestricted double h); + undefined roundRect(unrestricted double x, unrestricted double y, unrestricted double w, unrestricted double h, optional (unrestricted double or DOMPointInit or sequence<(unrestricted double or DOMPointInit)>) radii = 0); + undefined arc(unrestricted double x, unrestricted double y, unrestricted double radius, unrestricted double startAngle, unrestricted double endAngle, optional boolean counterclockwise = false); + undefined ellipse(unrestricted double x, unrestricted double y, unrestricted double radiusX, unrestricted double radiusY, unrestricted double rotation, unrestricted double startAngle, unrestricted double endAngle, optional boolean counterclockwise = false); +}; + +[Exposed=(Window,Worker)] +interface CanvasGradient { + // opaque object + undefined addColorStop(double offset, DOMString color); +}; + +[Exposed=(Window,Worker)] +interface CanvasPattern { + // opaque object + undefined setTransform(optional DOMMatrix2DInit transform = {}); +}; + +[Exposed=(Window,Worker)] +interface TextMetrics { + // x-direction + readonly attribute double width; // advance width + readonly attribute double actualBoundingBoxLeft; + readonly attribute double actualBoundingBoxRight; + + // y-direction + readonly attribute double fontBoundingBoxAscent; + readonly attribute double fontBoundingBoxDescent; + readonly attribute double actualBoundingBoxAscent; + readonly attribute double actualBoundingBoxDescent; + readonly attribute double emHeightAscent; + readonly attribute double emHeightDescent; + readonly attribute double hangingBaseline; + readonly attribute double alphabeticBaseline; + readonly attribute double ideographicBaseline; +}; + +dictionary ImageDataSettings { + PredefinedColorSpace colorSpace; +}; + +[Exposed=(Window,Worker), + Serializable] +interface ImageData { + constructor(unsigned long sw, unsigned long sh, optional ImageDataSettings settings = {}); + constructor(Uint8ClampedArray data, unsigned long sw, optional unsigned long sh, optional ImageDataSettings settings = {}); + + readonly attribute unsigned long width; + readonly attribute unsigned long height; + readonly attribute Uint8ClampedArray data; + readonly attribute PredefinedColorSpace colorSpace; +}; + +[Exposed=(Window,Worker)] +interface Path2D { + constructor(optional (Path2D or DOMString) path); + + undefined addPath(Path2D path, optional DOMMatrix2DInit transform = {}); +}; +Path2D includes CanvasPath; + +[Exposed=(Window,Worker)] +interface ImageBitmapRenderingContext { + readonly attribute (HTMLCanvasElement or OffscreenCanvas) canvas; + undefined transferFromImageBitmap(ImageBitmap? bitmap); +}; + +dictionary ImageBitmapRenderingContextSettings { + boolean alpha = true; +}; + +typedef (OffscreenCanvasRenderingContext2D or ImageBitmapRenderingContext or WebGLRenderingContext or WebGL2RenderingContext or GPUCanvasContext) OffscreenRenderingContext; + +dictionary ImageEncodeOptions { + DOMString type = "image/png"; + unrestricted double quality; +}; + +enum OffscreenRenderingContextId { "2d", "bitmaprenderer", "webgl", "webgl2", "webgpu" }; + +[Exposed=(Window,Worker), Transferable] +interface OffscreenCanvas : EventTarget { + constructor([EnforceRange] unsigned long long width, [EnforceRange] unsigned long long height); + + attribute [EnforceRange] unsigned long long width; + attribute [EnforceRange] unsigned long long height; + + OffscreenRenderingContext? getContext(OffscreenRenderingContextId contextId, optional any options = null); + ImageBitmap transferToImageBitmap(); + Promise convertToBlob(optional ImageEncodeOptions options = {}); + + attribute EventHandler oncontextlost; + attribute EventHandler oncontextrestored; +}; + +[Exposed=(Window,Worker)] +interface OffscreenCanvasRenderingContext2D { + undefined commit(); + readonly attribute OffscreenCanvas canvas; +}; + +OffscreenCanvasRenderingContext2D includes CanvasState; +OffscreenCanvasRenderingContext2D includes CanvasTransform; +OffscreenCanvasRenderingContext2D includes CanvasCompositing; +OffscreenCanvasRenderingContext2D includes CanvasImageSmoothing; +OffscreenCanvasRenderingContext2D includes CanvasFillStrokeStyles; +OffscreenCanvasRenderingContext2D includes CanvasShadowStyles; +OffscreenCanvasRenderingContext2D includes CanvasFilters; +OffscreenCanvasRenderingContext2D includes CanvasRect; +OffscreenCanvasRenderingContext2D includes CanvasDrawPath; +OffscreenCanvasRenderingContext2D includes CanvasText; +OffscreenCanvasRenderingContext2D includes CanvasDrawImage; +OffscreenCanvasRenderingContext2D includes CanvasImageData; +OffscreenCanvasRenderingContext2D includes CanvasPathDrawingStyles; +OffscreenCanvasRenderingContext2D includes CanvasTextDrawingStyles; +OffscreenCanvasRenderingContext2D includes CanvasPath; + +[Exposed=Window] +interface CustomElementRegistry { + [CEReactions] undefined define(DOMString name, CustomElementConstructor constructor, optional ElementDefinitionOptions options = {}); + (CustomElementConstructor or undefined) get(DOMString name); + Promise whenDefined(DOMString name); + [CEReactions] undefined upgrade(Node root); +}; + +callback CustomElementConstructor = HTMLElement (); + +dictionary ElementDefinitionOptions { + DOMString extends; +}; + +[Exposed=Window] +interface ElementInternals { + // Shadow root access + readonly attribute ShadowRoot? shadowRoot; + + // Form-associated custom elements + undefined setFormValue((File or USVString or FormData)? value, + optional (File or USVString or FormData)? state); + + readonly attribute HTMLFormElement? form; + + undefined setValidity(optional ValidityStateFlags flags = {}, + optional DOMString message, + optional HTMLElement anchor); + readonly attribute boolean willValidate; + readonly attribute ValidityState validity; + readonly attribute DOMString validationMessage; + boolean checkValidity(); + boolean reportValidity(); + + readonly attribute NodeList labels; +}; + +// Accessibility semantics +ElementInternals includes ARIAMixin; + +dictionary ValidityStateFlags { + boolean valueMissing = false; + boolean typeMismatch = false; + boolean patternMismatch = false; + boolean tooLong = false; + boolean tooShort = false; + boolean rangeUnderflow = false; + boolean rangeOverflow = false; + boolean stepMismatch = false; + boolean badInput = false; + boolean customError = false; +}; + +[Exposed=Window] +interface UserActivation { + readonly attribute boolean hasBeenActive; + readonly attribute boolean isActive; +}; + +partial interface Navigator { + [SameObject] readonly attribute UserActivation userActivation; +}; + +dictionary FocusOptions { + boolean preventScroll = false; + boolean focusVisible; +}; + +interface mixin ElementContentEditable { + [CEReactions] attribute DOMString contentEditable; + [CEReactions] attribute DOMString enterKeyHint; + readonly attribute boolean isContentEditable; + [CEReactions] attribute DOMString inputMode; +}; + +[Exposed=Window] +interface DataTransfer { + constructor(); + + attribute DOMString dropEffect; + attribute DOMString effectAllowed; + + [SameObject] readonly attribute DataTransferItemList items; + + undefined setDragImage(Element image, long x, long y); + + /* old interface */ + readonly attribute FrozenArray types; + DOMString getData(DOMString format); + undefined setData(DOMString format, DOMString data); + undefined clearData(optional DOMString format); + [SameObject] readonly attribute FileList files; +}; + +[Exposed=Window] +interface DataTransferItemList { + readonly attribute unsigned long length; + getter DataTransferItem (unsigned long index); + DataTransferItem? add(DOMString data, DOMString type); + DataTransferItem? add(File data); + undefined remove(unsigned long index); + undefined clear(); +}; + +[Exposed=Window] +interface DataTransferItem { + readonly attribute DOMString kind; + readonly attribute DOMString type; + undefined getAsString(FunctionStringCallback? _callback); + File? getAsFile(); +}; + +callback FunctionStringCallback = undefined (DOMString data); + +[Exposed=Window] +interface DragEvent : MouseEvent { + constructor(DOMString type, optional DragEventInit eventInitDict = {}); + + readonly attribute DataTransfer? dataTransfer; +}; + +dictionary DragEventInit : MouseEventInit { + DataTransfer? dataTransfer = null; +}; + +[Global=Window, + Exposed=Window, + LegacyUnenumerableNamedProperties] +interface Window : EventTarget { + // the current browsing context + [LegacyUnforgeable] readonly attribute WindowProxy window; + [Replaceable] readonly attribute WindowProxy self; + [LegacyUnforgeable] readonly attribute Document document; + attribute DOMString name; + [PutForwards=href, LegacyUnforgeable] readonly attribute Location location; + readonly attribute History history; + readonly attribute CustomElementRegistry customElements; + [Replaceable] readonly attribute BarProp locationbar; + [Replaceable] readonly attribute BarProp menubar; + [Replaceable] readonly attribute BarProp personalbar; + [Replaceable] readonly attribute BarProp scrollbars; + [Replaceable] readonly attribute BarProp statusbar; + [Replaceable] readonly attribute BarProp toolbar; + attribute DOMString status; + undefined close(); + readonly attribute boolean closed; + undefined stop(); + undefined focus(); + undefined blur(); + + // other browsing contexts + [Replaceable] readonly attribute WindowProxy frames; + [Replaceable] readonly attribute unsigned long length; + [LegacyUnforgeable] readonly attribute WindowProxy? top; + attribute any opener; + [Replaceable] readonly attribute WindowProxy? parent; + readonly attribute Element? frameElement; + WindowProxy? open(optional USVString url = "", optional DOMString target = "_blank", optional [LegacyNullToEmptyString] DOMString features = ""); + getter object (DOMString name); + // Since this is the global object, the IDL named getter adds a NamedPropertiesObject exotic + // object on the prototype chain. Indeed, this does not make the global object an exotic object. + // Indexed access is taken care of by the WindowProxy exotic object. + + // the user agent + readonly attribute Navigator navigator; + readonly attribute Navigator clientInformation; // legacy alias of .navigator + readonly attribute boolean originAgentCluster; + + // user prompts + undefined alert(); + undefined alert(DOMString message); + boolean confirm(optional DOMString message = ""); + DOMString? prompt(optional DOMString message = "", optional DOMString default = ""); + undefined print(); + + undefined postMessage(any message, USVString targetOrigin, optional sequence transfer = []); + undefined postMessage(any message, optional WindowPostMessageOptions options = {}); + + // also has obsolete members +}; +Window includes GlobalEventHandlers; +Window includes WindowEventHandlers; + +dictionary WindowPostMessageOptions : StructuredSerializeOptions { + USVString targetOrigin = "/"; +}; + +[Exposed=Window] +interface BarProp { + readonly attribute boolean visible; +}; + +enum ScrollRestoration { "auto", "manual" }; + +[Exposed=Window] +interface History { + readonly attribute unsigned long length; + attribute ScrollRestoration scrollRestoration; + readonly attribute any state; + undefined go(optional long delta = 0); + undefined back(); + undefined forward(); + undefined pushState(any data, DOMString unused, optional USVString? url = null); + undefined replaceState(any data, DOMString unused, optional USVString? url = null); +}; + +[Exposed=Window] +interface Location { // but see also additional creation steps and overridden internal methods + [LegacyUnforgeable] stringifier attribute USVString href; + [LegacyUnforgeable] readonly attribute USVString origin; + [LegacyUnforgeable] attribute USVString protocol; + [LegacyUnforgeable] attribute USVString host; + [LegacyUnforgeable] attribute USVString hostname; + [LegacyUnforgeable] attribute USVString port; + [LegacyUnforgeable] attribute USVString pathname; + [LegacyUnforgeable] attribute USVString search; + [LegacyUnforgeable] attribute USVString hash; + + [LegacyUnforgeable] undefined assign(USVString url); + [LegacyUnforgeable] undefined replace(USVString url); + [LegacyUnforgeable] undefined reload(); + + [LegacyUnforgeable, SameObject] readonly attribute DOMStringList ancestorOrigins; +}; + +[Exposed=Window] +interface PopStateEvent : Event { + constructor(DOMString type, optional PopStateEventInit eventInitDict = {}); + + readonly attribute any state; +}; + +dictionary PopStateEventInit : EventInit { + any state = null; +}; + +[Exposed=Window] +interface HashChangeEvent : Event { + constructor(DOMString type, optional HashChangeEventInit eventInitDict = {}); + + readonly attribute USVString oldURL; + readonly attribute USVString newURL; +}; + +dictionary HashChangeEventInit : EventInit { + USVString oldURL = ""; + USVString newURL = ""; +}; + +[Exposed=Window] +interface PageTransitionEvent : Event { + constructor(DOMString type, optional PageTransitionEventInit eventInitDict = {}); + + readonly attribute boolean persisted; +}; + +dictionary PageTransitionEventInit : EventInit { + boolean persisted = false; +}; + +[Exposed=Window] +interface BeforeUnloadEvent : Event { + attribute DOMString returnValue; +}; + +[Exposed=*] +interface ErrorEvent : Event { + constructor(DOMString type, optional ErrorEventInit eventInitDict = {}); + + readonly attribute DOMString message; + readonly attribute USVString filename; + readonly attribute unsigned long lineno; + readonly attribute unsigned long colno; + readonly attribute any error; +}; + +dictionary ErrorEventInit : EventInit { + DOMString message = ""; + USVString filename = ""; + unsigned long lineno = 0; + unsigned long colno = 0; + any error; +}; + +[Exposed=*] +interface PromiseRejectionEvent : Event { + constructor(DOMString type, PromiseRejectionEventInit eventInitDict); + + readonly attribute Promise promise; + readonly attribute any reason; +}; + +dictionary PromiseRejectionEventInit : EventInit { + required Promise promise; + any reason; +}; + +[LegacyTreatNonObjectAsNull] +callback EventHandlerNonNull = any (Event event); +typedef EventHandlerNonNull? EventHandler; + +[LegacyTreatNonObjectAsNull] +callback OnErrorEventHandlerNonNull = any ((Event or DOMString) event, optional DOMString source, optional unsigned long lineno, optional unsigned long colno, optional any error); +typedef OnErrorEventHandlerNonNull? OnErrorEventHandler; + +[LegacyTreatNonObjectAsNull] +callback OnBeforeUnloadEventHandlerNonNull = DOMString? (Event event); +typedef OnBeforeUnloadEventHandlerNonNull? OnBeforeUnloadEventHandler; + +interface mixin GlobalEventHandlers { + attribute EventHandler onabort; + attribute EventHandler onauxclick; + attribute EventHandler onbeforeinput; + attribute EventHandler onbeforematch; + attribute EventHandler onblur; + attribute EventHandler oncancel; + attribute EventHandler oncanplay; + attribute EventHandler oncanplaythrough; + attribute EventHandler onchange; + attribute EventHandler onclick; + attribute EventHandler onclose; + attribute EventHandler oncontextlost; + attribute EventHandler oncontextmenu; + attribute EventHandler oncontextrestored; + attribute EventHandler oncuechange; + attribute EventHandler ondblclick; + attribute EventHandler ondrag; + attribute EventHandler ondragend; + attribute EventHandler ondragenter; + attribute EventHandler ondragleave; + attribute EventHandler ondragover; + attribute EventHandler ondragstart; + attribute EventHandler ondrop; + attribute EventHandler ondurationchange; + attribute EventHandler onemptied; + attribute EventHandler onended; + attribute OnErrorEventHandler onerror; + attribute EventHandler onfocus; + attribute EventHandler onformdata; + attribute EventHandler oninput; + attribute EventHandler oninvalid; + attribute EventHandler onkeydown; + attribute EventHandler onkeypress; + attribute EventHandler onkeyup; + attribute EventHandler onload; + attribute EventHandler onloadeddata; + attribute EventHandler onloadedmetadata; + attribute EventHandler onloadstart; + attribute EventHandler onmousedown; + [LegacyLenientThis] attribute EventHandler onmouseenter; + [LegacyLenientThis] attribute EventHandler onmouseleave; + attribute EventHandler onmousemove; + attribute EventHandler onmouseout; + attribute EventHandler onmouseover; + attribute EventHandler onmouseup; + attribute EventHandler onpause; + attribute EventHandler onplay; + attribute EventHandler onplaying; + attribute EventHandler onprogress; + attribute EventHandler onratechange; + attribute EventHandler onreset; + attribute EventHandler onresize; + attribute EventHandler onscroll; + attribute EventHandler onsecuritypolicyviolation; + attribute EventHandler onseeked; + attribute EventHandler onseeking; + attribute EventHandler onselect; + attribute EventHandler onslotchange; + attribute EventHandler onstalled; + attribute EventHandler onsubmit; + attribute EventHandler onsuspend; + attribute EventHandler ontimeupdate; + attribute EventHandler ontoggle; + attribute EventHandler onvolumechange; + attribute EventHandler onwaiting; + attribute EventHandler onwebkitanimationend; + attribute EventHandler onwebkitanimationiteration; + attribute EventHandler onwebkitanimationstart; + attribute EventHandler onwebkittransitionend; + attribute EventHandler onwheel; +}; + +interface mixin WindowEventHandlers { + attribute EventHandler onafterprint; + attribute EventHandler onbeforeprint; + attribute OnBeforeUnloadEventHandler onbeforeunload; + attribute EventHandler onhashchange; + attribute EventHandler onlanguagechange; + attribute EventHandler onmessage; + attribute EventHandler onmessageerror; + attribute EventHandler onoffline; + attribute EventHandler ononline; + attribute EventHandler onpagehide; + attribute EventHandler onpageshow; + attribute EventHandler onpopstate; + attribute EventHandler onrejectionhandled; + attribute EventHandler onstorage; + attribute EventHandler onunhandledrejection; + attribute EventHandler onunload; +}; + +interface mixin DocumentAndElementEventHandlers { + attribute EventHandler oncopy; + attribute EventHandler oncut; + attribute EventHandler onpaste; +}; + +typedef (DOMString or Function) TimerHandler; + +interface mixin WindowOrWorkerGlobalScope { + [Replaceable] readonly attribute USVString origin; + readonly attribute boolean isSecureContext; + readonly attribute boolean crossOriginIsolated; + + undefined reportError(any e); + + // base64 utility methods + DOMString btoa(DOMString data); + ByteString atob(DOMString data); + + // timers + long setTimeout(TimerHandler handler, optional long timeout = 0, any... arguments); + undefined clearTimeout(optional long id = 0); + long setInterval(TimerHandler handler, optional long timeout = 0, any... arguments); + undefined clearInterval(optional long id = 0); + + // microtask queuing + undefined queueMicrotask(VoidFunction callback); + + // ImageBitmap + Promise createImageBitmap(ImageBitmapSource image, optional ImageBitmapOptions options = {}); + Promise createImageBitmap(ImageBitmapSource image, long sx, long sy, long sw, long sh, optional ImageBitmapOptions options = {}); + + // structured cloning + any structuredClone(any value, optional StructuredSerializeOptions options = {}); +}; +Window includes WindowOrWorkerGlobalScope; +WorkerGlobalScope includes WindowOrWorkerGlobalScope; + +[Exposed=Window] +interface DOMParser { + constructor(); + + [NewObject] Document parseFromString(DOMString string, DOMParserSupportedType type); +}; + +enum DOMParserSupportedType { + "text/html", + "text/xml", + "application/xml", + "application/xhtml+xml", + "image/svg+xml" +}; + +[Exposed=Window] +interface Navigator { + // objects implementing this interface also implement the interfaces given below +}; +Navigator includes NavigatorID; +Navigator includes NavigatorLanguage; +Navigator includes NavigatorOnLine; +Navigator includes NavigatorContentUtils; +Navigator includes NavigatorCookies; +Navigator includes NavigatorPlugins; +Navigator includes NavigatorConcurrentHardware; + +interface mixin NavigatorID { + readonly attribute DOMString appCodeName; // constant "Mozilla" + readonly attribute DOMString appName; // constant "Netscape" + readonly attribute DOMString appVersion; + readonly attribute DOMString platform; + readonly attribute DOMString product; // constant "Gecko" + [Exposed=Window] readonly attribute DOMString productSub; + readonly attribute DOMString userAgent; + [Exposed=Window] readonly attribute DOMString vendor; + [Exposed=Window] readonly attribute DOMString vendorSub; // constant "" +}; + +partial interface mixin NavigatorID { + [Exposed=Window] boolean taintEnabled(); // constant false + [Exposed=Window] readonly attribute DOMString oscpu; +}; + +interface mixin NavigatorLanguage { + readonly attribute DOMString language; + readonly attribute FrozenArray languages; +}; + +interface mixin NavigatorOnLine { + readonly attribute boolean onLine; +}; + +interface mixin NavigatorContentUtils { + [SecureContext] undefined registerProtocolHandler(DOMString scheme, USVString url); + [SecureContext] undefined unregisterProtocolHandler(DOMString scheme, USVString url); +}; + +interface mixin NavigatorCookies { + readonly attribute boolean cookieEnabled; +}; + +interface mixin NavigatorPlugins { + [SameObject] readonly attribute PluginArray plugins; + [SameObject] readonly attribute MimeTypeArray mimeTypes; + boolean javaEnabled(); + readonly attribute boolean pdfViewerEnabled; +}; + +[Exposed=Window, + LegacyUnenumerableNamedProperties] +interface PluginArray { + undefined refresh(); + readonly attribute unsigned long length; + getter Plugin? item(unsigned long index); + getter Plugin? namedItem(DOMString name); +}; + +[Exposed=Window, + LegacyUnenumerableNamedProperties] +interface MimeTypeArray { + readonly attribute unsigned long length; + getter MimeType? item(unsigned long index); + getter MimeType? namedItem(DOMString name); +}; + +[Exposed=Window, + LegacyUnenumerableNamedProperties] +interface Plugin { + readonly attribute DOMString name; + readonly attribute DOMString description; + readonly attribute DOMString filename; + readonly attribute unsigned long length; + getter MimeType? item(unsigned long index); + getter MimeType? namedItem(DOMString name); +}; + +[Exposed=Window] +interface MimeType { + readonly attribute DOMString type; + readonly attribute DOMString description; + readonly attribute DOMString suffixes; + readonly attribute Plugin enabledPlugin; +}; + +[Exposed=(Window,Worker), Serializable, Transferable] +interface ImageBitmap { + readonly attribute unsigned long width; + readonly attribute unsigned long height; + undefined close(); +}; + +typedef (CanvasImageSource or + Blob or + ImageData) ImageBitmapSource; + +enum ImageOrientation { "none", "flipY" }; +enum PremultiplyAlpha { "none", "premultiply", "default" }; +enum ColorSpaceConversion { "none", "default" }; +enum ResizeQuality { "pixelated", "low", "medium", "high" }; + +dictionary ImageBitmapOptions { + ImageOrientation imageOrientation = "none"; + PremultiplyAlpha premultiplyAlpha = "default"; + ColorSpaceConversion colorSpaceConversion = "default"; + [EnforceRange] unsigned long resizeWidth; + [EnforceRange] unsigned long resizeHeight; + ResizeQuality resizeQuality = "low"; +}; + +callback FrameRequestCallback = undefined (DOMHighResTimeStamp time); + +interface mixin AnimationFrameProvider { + unsigned long requestAnimationFrame(FrameRequestCallback callback); + undefined cancelAnimationFrame(unsigned long handle); +}; +Window includes AnimationFrameProvider; +DedicatedWorkerGlobalScope includes AnimationFrameProvider; + +[Exposed=(Window,Worker,AudioWorklet)] +interface MessageEvent : Event { + constructor(DOMString type, optional MessageEventInit eventInitDict = {}); + + readonly attribute any data; + readonly attribute USVString origin; + readonly attribute DOMString lastEventId; + readonly attribute MessageEventSource? source; + readonly attribute FrozenArray ports; + + undefined initMessageEvent(DOMString type, optional boolean bubbles = false, optional boolean cancelable = false, optional any data = null, optional USVString origin = "", optional DOMString lastEventId = "", optional MessageEventSource? source = null, optional sequence ports = []); +}; + +dictionary MessageEventInit : EventInit { + any data = null; + USVString origin = ""; + DOMString lastEventId = ""; + MessageEventSource? source = null; + sequence ports = []; +}; + +typedef (WindowProxy or MessagePort or ServiceWorker) MessageEventSource; + +[Exposed=(Window,Worker)] +interface EventSource : EventTarget { + constructor(USVString url, optional EventSourceInit eventSourceInitDict = {}); + + readonly attribute USVString url; + readonly attribute boolean withCredentials; + + // ready state + const unsigned short CONNECTING = 0; + const unsigned short OPEN = 1; + const unsigned short CLOSED = 2; + readonly attribute unsigned short readyState; + + // networking + attribute EventHandler onopen; + attribute EventHandler onmessage; + attribute EventHandler onerror; + undefined close(); +}; + +dictionary EventSourceInit { + boolean withCredentials = false; +}; + +[Exposed=(Window,Worker)] +interface MessageChannel { + constructor(); + + readonly attribute MessagePort port1; + readonly attribute MessagePort port2; +}; + +[Exposed=(Window,Worker,AudioWorklet), Transferable] +interface MessagePort : EventTarget { + undefined postMessage(any message, sequence transfer); + undefined postMessage(any message, optional StructuredSerializeOptions options = {}); + undefined start(); + undefined close(); + + // event handlers + attribute EventHandler onmessage; + attribute EventHandler onmessageerror; +}; + +dictionary StructuredSerializeOptions { + sequence transfer = []; +}; + +[Exposed=(Window,Worker)] +interface BroadcastChannel : EventTarget { + constructor(DOMString name); + + readonly attribute DOMString name; + undefined postMessage(any message); + undefined close(); + attribute EventHandler onmessage; + attribute EventHandler onmessageerror; +}; + +[Exposed=Worker] +interface WorkerGlobalScope : EventTarget { + readonly attribute WorkerGlobalScope self; + readonly attribute WorkerLocation location; + readonly attribute WorkerNavigator navigator; + undefined importScripts(USVString... urls); + + attribute OnErrorEventHandler onerror; + attribute EventHandler onlanguagechange; + attribute EventHandler onoffline; + attribute EventHandler ononline; + attribute EventHandler onrejectionhandled; + attribute EventHandler onunhandledrejection; +}; + +[Global=(Worker,DedicatedWorker),Exposed=DedicatedWorker] +interface DedicatedWorkerGlobalScope : WorkerGlobalScope { + [Replaceable] readonly attribute DOMString name; + + undefined postMessage(any message, sequence transfer); + undefined postMessage(any message, optional StructuredSerializeOptions options = {}); + + undefined close(); + + attribute EventHandler onmessage; + attribute EventHandler onmessageerror; +}; + +[Global=(Worker,SharedWorker),Exposed=SharedWorker] +interface SharedWorkerGlobalScope : WorkerGlobalScope { + [Replaceable] readonly attribute DOMString name; + + undefined close(); + + attribute EventHandler onconnect; +}; + +interface mixin AbstractWorker { + attribute EventHandler onerror; +}; + +[Exposed=(Window,DedicatedWorker,SharedWorker)] +interface Worker : EventTarget { + constructor(USVString scriptURL, optional WorkerOptions options = {}); + + undefined terminate(); + + undefined postMessage(any message, sequence transfer); + undefined postMessage(any message, optional StructuredSerializeOptions options = {}); + attribute EventHandler onmessage; + attribute EventHandler onmessageerror; +}; + +dictionary WorkerOptions { + WorkerType type = "classic"; + RequestCredentials credentials = "same-origin"; // credentials is only used if type is "module" + DOMString name = ""; +}; + +enum WorkerType { "classic", "module" }; + +Worker includes AbstractWorker; + +[Exposed=Window] +interface SharedWorker : EventTarget { + constructor(USVString scriptURL, optional (DOMString or WorkerOptions) options = {}); + + readonly attribute MessagePort port; +}; +SharedWorker includes AbstractWorker; + +interface mixin NavigatorConcurrentHardware { + readonly attribute unsigned long long hardwareConcurrency; +}; + +[Exposed=Worker] +interface WorkerNavigator {}; +WorkerNavigator includes NavigatorID; +WorkerNavigator includes NavigatorLanguage; +WorkerNavigator includes NavigatorOnLine; +WorkerNavigator includes NavigatorConcurrentHardware; + +[Exposed=Worker] +interface WorkerLocation { + stringifier readonly attribute USVString href; + readonly attribute USVString origin; + readonly attribute USVString protocol; + readonly attribute USVString host; + readonly attribute USVString hostname; + readonly attribute USVString port; + readonly attribute USVString pathname; + readonly attribute USVString search; + readonly attribute USVString hash; +}; + +[Exposed=Worklet, SecureContext] +interface WorkletGlobalScope {}; + +[Exposed=Window, SecureContext] +interface Worklet { + [NewObject] Promise addModule(USVString moduleURL, optional WorkletOptions options = {}); +}; + +dictionary WorkletOptions { + RequestCredentials credentials = "same-origin"; +}; + +[Exposed=Window] +interface Storage { + readonly attribute unsigned long length; + DOMString? key(unsigned long index); + getter DOMString? getItem(DOMString key); + setter undefined setItem(DOMString key, DOMString value); + deleter undefined removeItem(DOMString key); + undefined clear(); +}; + +interface mixin WindowSessionStorage { + readonly attribute Storage sessionStorage; +}; +Window includes WindowSessionStorage; + +interface mixin WindowLocalStorage { + readonly attribute Storage localStorage; +}; +Window includes WindowLocalStorage; + +[Exposed=Window] +interface StorageEvent : Event { + constructor(DOMString type, optional StorageEventInit eventInitDict = {}); + + readonly attribute DOMString? key; + readonly attribute DOMString? oldValue; + readonly attribute DOMString? newValue; + readonly attribute USVString url; + readonly attribute Storage? storageArea; + + undefined initStorageEvent(DOMString type, optional boolean bubbles = false, optional boolean cancelable = false, optional DOMString? key = null, optional DOMString? oldValue = null, optional DOMString? newValue = null, optional USVString url = "", optional Storage? storageArea = null); +}; + +dictionary StorageEventInit : EventInit { + DOMString? key = null; + DOMString? oldValue = null; + DOMString? newValue = null; + USVString url = ""; + Storage? storageArea = null; +}; + +[Exposed=Window] +interface HTMLMarqueeElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute DOMString behavior; + [CEReactions] attribute DOMString bgColor; + [CEReactions] attribute DOMString direction; + [CEReactions] attribute DOMString height; + [CEReactions] attribute unsigned long hspace; + [CEReactions] attribute long loop; + [CEReactions] attribute unsigned long scrollAmount; + [CEReactions] attribute unsigned long scrollDelay; + [CEReactions] attribute boolean trueSpeed; + [CEReactions] attribute unsigned long vspace; + [CEReactions] attribute DOMString width; + + undefined start(); + undefined stop(); +}; + +[Exposed=Window] +interface HTMLFrameSetElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute DOMString cols; + [CEReactions] attribute DOMString rows; +}; +HTMLFrameSetElement includes WindowEventHandlers; + +[Exposed=Window] +interface HTMLFrameElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute DOMString name; + [CEReactions] attribute DOMString scrolling; + [CEReactions] attribute USVString src; + [CEReactions] attribute DOMString frameBorder; + [CEReactions] attribute USVString longDesc; + [CEReactions] attribute boolean noResize; + readonly attribute Document? contentDocument; + readonly attribute WindowProxy? contentWindow; + + [CEReactions] attribute [LegacyNullToEmptyString] DOMString marginHeight; + [CEReactions] attribute [LegacyNullToEmptyString] DOMString marginWidth; +}; + +partial interface HTMLAnchorElement { + [CEReactions] attribute DOMString coords; + [CEReactions] attribute DOMString charset; + [CEReactions] attribute DOMString name; + [CEReactions] attribute DOMString rev; + [CEReactions] attribute DOMString shape; +}; + +partial interface HTMLAreaElement { + [CEReactions] attribute boolean noHref; +}; + +partial interface HTMLBodyElement { + [CEReactions] attribute [LegacyNullToEmptyString] DOMString text; + [CEReactions] attribute [LegacyNullToEmptyString] DOMString link; + [CEReactions] attribute [LegacyNullToEmptyString] DOMString vLink; + [CEReactions] attribute [LegacyNullToEmptyString] DOMString aLink; + [CEReactions] attribute [LegacyNullToEmptyString] DOMString bgColor; + [CEReactions] attribute DOMString background; +}; + +partial interface HTMLBRElement { + [CEReactions] attribute DOMString clear; +}; + +partial interface HTMLTableCaptionElement { + [CEReactions] attribute DOMString align; +}; + +partial interface HTMLTableColElement { + [CEReactions] attribute DOMString align; + [CEReactions] attribute DOMString ch; + [CEReactions] attribute DOMString chOff; + [CEReactions] attribute DOMString vAlign; + [CEReactions] attribute DOMString width; +}; + +[Exposed=Window] +interface HTMLDirectoryElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute boolean compact; +}; + +partial interface HTMLDivElement { + [CEReactions] attribute DOMString align; +}; + +partial interface HTMLDListElement { + [CEReactions] attribute boolean compact; +}; + +partial interface HTMLEmbedElement { + [CEReactions] attribute DOMString align; + [CEReactions] attribute DOMString name; +}; + +[Exposed=Window] +interface HTMLFontElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute [LegacyNullToEmptyString] DOMString color; + [CEReactions] attribute DOMString face; + [CEReactions] attribute DOMString size; +}; + +partial interface HTMLHeadingElement { + [CEReactions] attribute DOMString align; +}; + +partial interface HTMLHRElement { + [CEReactions] attribute DOMString align; + [CEReactions] attribute DOMString color; + [CEReactions] attribute boolean noShade; + [CEReactions] attribute DOMString size; + [CEReactions] attribute DOMString width; +}; + +partial interface HTMLHtmlElement { + [CEReactions] attribute DOMString version; +}; + +partial interface HTMLIFrameElement { + [CEReactions] attribute DOMString align; + [CEReactions] attribute DOMString scrolling; + [CEReactions] attribute DOMString frameBorder; + [CEReactions] attribute USVString longDesc; + + [CEReactions] attribute [LegacyNullToEmptyString] DOMString marginHeight; + [CEReactions] attribute [LegacyNullToEmptyString] DOMString marginWidth; +}; + +partial interface HTMLImageElement { + [CEReactions] attribute DOMString name; + [CEReactions] attribute USVString lowsrc; + [CEReactions] attribute DOMString align; + [CEReactions] attribute unsigned long hspace; + [CEReactions] attribute unsigned long vspace; + [CEReactions] attribute USVString longDesc; + + [CEReactions] attribute [LegacyNullToEmptyString] DOMString border; +}; + +partial interface HTMLInputElement { + [CEReactions] attribute DOMString align; + [CEReactions] attribute DOMString useMap; +}; + +partial interface HTMLLegendElement { + [CEReactions] attribute DOMString align; +}; + +partial interface HTMLLIElement { + [CEReactions] attribute DOMString type; +}; + +partial interface HTMLLinkElement { + [CEReactions] attribute DOMString charset; + [CEReactions] attribute DOMString rev; + [CEReactions] attribute DOMString target; +}; + +partial interface HTMLMenuElement { + [CEReactions] attribute boolean compact; +}; + +partial interface HTMLMetaElement { + [CEReactions] attribute DOMString scheme; +}; + +partial interface HTMLObjectElement { + [CEReactions] attribute DOMString align; + [CEReactions] attribute DOMString archive; + [CEReactions] attribute DOMString code; + [CEReactions] attribute boolean declare; + [CEReactions] attribute unsigned long hspace; + [CEReactions] attribute DOMString standby; + [CEReactions] attribute unsigned long vspace; + [CEReactions] attribute DOMString codeBase; + [CEReactions] attribute DOMString codeType; + [CEReactions] attribute DOMString useMap; + + [CEReactions] attribute [LegacyNullToEmptyString] DOMString border; +}; + +partial interface HTMLOListElement { + [CEReactions] attribute boolean compact; +}; + +partial interface HTMLParagraphElement { + [CEReactions] attribute DOMString align; +}; + +[Exposed=Window] +interface HTMLParamElement : HTMLElement { + [HTMLConstructor] constructor(); + + [CEReactions] attribute DOMString name; + [CEReactions] attribute DOMString value; + [CEReactions] attribute DOMString type; + [CEReactions] attribute DOMString valueType; +}; + +partial interface HTMLPreElement { + [CEReactions] attribute long width; +}; + +partial interface HTMLStyleElement { + [CEReactions] attribute DOMString type; +}; + +partial interface HTMLScriptElement { + [CEReactions] attribute DOMString charset; + [CEReactions] attribute DOMString event; + [CEReactions] attribute DOMString htmlFor; +}; + +partial interface HTMLTableElement { + [CEReactions] attribute DOMString align; + [CEReactions] attribute DOMString border; + [CEReactions] attribute DOMString frame; + [CEReactions] attribute DOMString rules; + [CEReactions] attribute DOMString summary; + [CEReactions] attribute DOMString width; + + [CEReactions] attribute [LegacyNullToEmptyString] DOMString bgColor; + [CEReactions] attribute [LegacyNullToEmptyString] DOMString cellPadding; + [CEReactions] attribute [LegacyNullToEmptyString] DOMString cellSpacing; +}; + +partial interface HTMLTableSectionElement { + [CEReactions] attribute DOMString align; + [CEReactions] attribute DOMString ch; + [CEReactions] attribute DOMString chOff; + [CEReactions] attribute DOMString vAlign; +}; + +partial interface HTMLTableCellElement { + [CEReactions] attribute DOMString align; + [CEReactions] attribute DOMString axis; + [CEReactions] attribute DOMString height; + [CEReactions] attribute DOMString width; + + [CEReactions] attribute DOMString ch; + [CEReactions] attribute DOMString chOff; + [CEReactions] attribute boolean noWrap; + [CEReactions] attribute DOMString vAlign; + + [CEReactions] attribute [LegacyNullToEmptyString] DOMString bgColor; +}; + +partial interface HTMLTableRowElement { + [CEReactions] attribute DOMString align; + [CEReactions] attribute DOMString ch; + [CEReactions] attribute DOMString chOff; + [CEReactions] attribute DOMString vAlign; + + [CEReactions] attribute [LegacyNullToEmptyString] DOMString bgColor; +}; + +partial interface HTMLUListElement { + [CEReactions] attribute boolean compact; + [CEReactions] attribute DOMString type; +}; + +partial interface Document { + [CEReactions] attribute [LegacyNullToEmptyString] DOMString fgColor; + [CEReactions] attribute [LegacyNullToEmptyString] DOMString linkColor; + [CEReactions] attribute [LegacyNullToEmptyString] DOMString vlinkColor; + [CEReactions] attribute [LegacyNullToEmptyString] DOMString alinkColor; + [CEReactions] attribute [LegacyNullToEmptyString] DOMString bgColor; + + [SameObject] readonly attribute HTMLCollection anchors; + [SameObject] readonly attribute HTMLCollection applets; + + undefined clear(); + undefined captureEvents(); + undefined releaseEvents(); + + [SameObject] readonly attribute HTMLAllCollection all; +}; + +partial interface Window { + undefined captureEvents(); + undefined releaseEvents(); + + [Replaceable, SameObject] readonly attribute External external; +}; + +[Exposed=Window] +interface External { + undefined AddSearchProvider(); + undefined IsSearchProviderInstalled(); +}; diff --git a/test/wpt/tests/interfaces/referrer-policy.idl b/test/wpt/tests/interfaces/referrer-policy.idl new file mode 100644 index 00000000000..0ef9a1fdecc --- /dev/null +++ b/test/wpt/tests/interfaces/referrer-policy.idl @@ -0,0 +1,16 @@ +// GENERATED CONTENT - DO NOT EDIT +// Content was automatically extracted by Reffy into webref +// (https://github.com/w3c/webref) +// Source: Referrer Policy (https://w3c.github.io/webappsec-referrer-policy/) + +enum ReferrerPolicy { + "", + "no-referrer", + "no-referrer-when-downgrade", + "same-origin", + "origin", + "strict-origin", + "origin-when-cross-origin", + "strict-origin-when-cross-origin", + "unsafe-url" +}; diff --git a/test/wpt/tests/resources/WebIDLParser.js b/test/wpt/tests/resources/WebIDLParser.js new file mode 120000 index 00000000000..d4d590faf9d --- /dev/null +++ b/test/wpt/tests/resources/WebIDLParser.js @@ -0,0 +1 @@ +./test/wpt/tests/resources/webidl2/lib/webidl2.js \ No newline at end of file diff --git a/test/wpt/tests/resources/idlharness.js b/test/wpt/tests/resources/idlharness.js new file mode 100644 index 00000000000..d2fb0366c80 --- /dev/null +++ b/test/wpt/tests/resources/idlharness.js @@ -0,0 +1,3526 @@ +/* For user documentation see docs/_writing-tests/idlharness.md */ + +/** + * Notes for people who want to edit this file (not just use it as a library): + * + * Most of the interesting stuff happens in the derived classes of IdlObject, + * especially IdlInterface. The entry point for all IdlObjects is .test(), + * which is called by IdlArray.test(). An IdlObject is conceptually just + * "thing we want to run tests on", and an IdlArray is an array of IdlObjects + * with some additional data thrown in. + * + * The object model is based on what WebIDLParser.js produces, which is in turn + * based on its pegjs grammar. If you want to figure out what properties an + * object will have from WebIDLParser.js, the best way is to look at the + * grammar: + * + * https://github.com/darobin/webidl.js/blob/master/lib/grammar.peg + * + * So for instance: + * + * // interface definition + * interface + * = extAttrs:extendedAttributeList? S? "interface" S name:identifier w herit:ifInheritance? w "{" w mem:ifMember* w "}" w ";" w + * { return { type: "interface", name: name, inheritance: herit, members: mem, extAttrs: extAttrs }; } + * + * This means that an "interface" object will have a .type property equal to + * the string "interface", a .name property equal to the identifier that the + * parser found, an .inheritance property equal to either null or the result of + * the "ifInheritance" production found elsewhere in the grammar, and so on. + * After each grammatical production is a JavaScript function in curly braces + * that gets called with suitable arguments and returns some JavaScript value. + * + * (Note that the version of WebIDLParser.js we use might sometimes be + * out-of-date or forked.) + * + * The members and methods of the classes defined by this file are all at least + * briefly documented, hopefully. + */ +(function(){ +"use strict"; +// Support subsetTestByKey from /common/subset-tests-by-key.js, but make it optional +if (!('subsetTestByKey' in self)) { + self.subsetTestByKey = function(key, callback, ...args) { + return callback(...args); + } + self.shouldRunSubTest = () => true; +} +/// Helpers /// +function constValue (cnt) +{ + if (cnt.type === "null") return null; + if (cnt.type === "NaN") return NaN; + if (cnt.type === "Infinity") return cnt.negative ? -Infinity : Infinity; + if (cnt.type === "number") return +cnt.value; + return cnt.value; +} + +function minOverloadLength(overloads) +{ + // "The value of the Function object’s “length” property is + // a Number determined as follows: + // ". . . + // "Return the length of the shortest argument list of the + // entries in S." + if (!overloads.length) { + return 0; + } + + return overloads.map(function(attr) { + return attr.arguments ? attr.arguments.filter(function(arg) { + return !arg.optional && !arg.variadic; + }).length : 0; + }) + .reduce(function(m, n) { return Math.min(m, n); }); +} + +// A helper to get the global of a Function object. This is needed to determine +// which global exceptions the function throws will come from. +function globalOf(func) +{ + try { + // Use the fact that .constructor for a Function object is normally the + // Function constructor, which can be used to mint a new function in the + // right global. + return func.constructor("return this;")(); + } catch (e) { + } + // If the above fails, because someone gave us a non-function, or a function + // with a weird proto chain or weird .constructor property, just fall back + // to 'self'. + return self; +} + +// https://esdiscuss.org/topic/isconstructor#content-11 +function isConstructor(o) { + try { + new (new Proxy(o, {construct: () => ({})})); + return true; + } catch(e) { + return false; + } +} + +function throwOrReject(a_test, operation, fn, obj, args, message, cb) +{ + if (operation.idlType.generic !== "Promise") { + assert_throws_js(globalOf(fn).TypeError, function() { + fn.apply(obj, args); + }, message); + cb(); + } else { + try { + promise_rejects_js(a_test, TypeError, fn.apply(obj, args), message).then(cb, cb); + } catch (e){ + a_test.step(function() { + assert_unreached("Throws \"" + e + "\" instead of rejecting promise"); + cb(); + }); + } + } +} + +function awaitNCallbacks(n, cb, ctx) +{ + var counter = 0; + return function() { + counter++; + if (counter >= n) { + cb(); + } + }; +} + +/// IdlHarnessError /// +// Entry point +self.IdlHarnessError = function(message) +{ + /** + * Message to be printed as the error's toString invocation. + */ + this.message = message; +}; + +IdlHarnessError.prototype = Object.create(Error.prototype); + +IdlHarnessError.prototype.toString = function() +{ + return this.message; +}; + + +/// IdlArray /// +// Entry point +self.IdlArray = function() +{ + /** + * A map from strings to the corresponding named IdlObject, such as + * IdlInterface or IdlException. These are the things that test() will run + * tests on. + */ + this.members = {}; + + /** + * A map from strings to arrays of strings. The keys are interface or + * exception names, and are expected to also exist as keys in this.members + * (otherwise they'll be ignored). This is populated by add_objects() -- + * see documentation at the start of the file. The actual tests will be + * run by calling this.members[name].test_object(obj) for each obj in + * this.objects[name]. obj is a string that will be eval'd to produce a + * JavaScript value, which is supposed to be an object implementing the + * given IdlObject (interface, exception, etc.). + */ + this.objects = {}; + + /** + * When adding multiple collections of IDLs one at a time, an earlier one + * might contain a partial interface or includes statement that depends + * on a later one. Save these up and handle them right before we run + * tests. + * + * Both this.partials and this.includes will be the objects as parsed by + * WebIDLParser.js, not wrapped in IdlInterface or similar. + */ + this.partials = []; + this.includes = []; + + /** + * Record of skipped IDL items, in case we later realize that they are a + * dependency (to retroactively process them). + */ + this.skipped = new Map(); +}; + +IdlArray.prototype.add_idls = function(raw_idls, options) +{ + /** Entry point. See documentation at beginning of file. */ + this.internal_add_idls(WebIDL2.parse(raw_idls), options); +}; + +IdlArray.prototype.add_untested_idls = function(raw_idls, options) +{ + /** Entry point. See documentation at beginning of file. */ + var parsed_idls = WebIDL2.parse(raw_idls); + this.mark_as_untested(parsed_idls); + this.internal_add_idls(parsed_idls, options); +}; + +IdlArray.prototype.mark_as_untested = function (parsed_idls) +{ + for (var i = 0; i < parsed_idls.length; i++) { + parsed_idls[i].untested = true; + if ("members" in parsed_idls[i]) { + for (var j = 0; j < parsed_idls[i].members.length; j++) { + parsed_idls[i].members[j].untested = true; + } + } + } +}; + +IdlArray.prototype.is_excluded_by_options = function (name, options) +{ + return options && + (options.except && options.except.includes(name) + || options.only && !options.only.includes(name)); +}; + +IdlArray.prototype.add_dependency_idls = function(raw_idls, options) +{ + return this.internal_add_dependency_idls(WebIDL2.parse(raw_idls), options); +}; + +IdlArray.prototype.internal_add_dependency_idls = function(parsed_idls, options) +{ + const new_options = { only: [] } + + const all_deps = new Set(); + Object.values(this.members).forEach(v => { + if (v.base) { + all_deps.add(v.base); + } + }); + // Add both 'A' and 'B' for each 'A includes B' entry. + this.includes.forEach(i => { + all_deps.add(i.target); + all_deps.add(i.includes); + }); + this.partials.forEach(p => all_deps.add(p.name)); + // Add 'TypeOfType' for each "typedef TypeOfType MyType;" entry. + Object.entries(this.members).forEach(([k, v]) => { + if (v instanceof IdlTypedef) { + let defs = v.idlType.union + ? v.idlType.idlType.map(t => t.idlType) + : [v.idlType.idlType]; + defs.forEach(d => all_deps.add(d)); + } + }); + + // Add the attribute idlTypes of all the nested members of idls. + const attrDeps = parsedIdls => { + return parsedIdls.reduce((deps, parsed) => { + if (parsed.members) { + for (const attr of Object.values(parsed.members).filter(m => m.type === 'attribute')) { + let attrType = attr.idlType; + // Check for generic members (e.g. FrozenArray) + if (attrType.generic) { + deps.add(attrType.generic); + attrType = attrType.idlType; + } + deps.add(attrType.idlType); + } + } + if (parsed.base in this.members) { + attrDeps([this.members[parsed.base]]).forEach(dep => deps.add(dep)); + } + return deps; + }, new Set()); + }; + + const testedMembers = Object.values(this.members).filter(m => !m.untested && m.members); + attrDeps(testedMembers).forEach(dep => all_deps.add(dep)); + + const testedPartials = this.partials.filter(m => !m.untested && m.members); + attrDeps(testedPartials).forEach(dep => all_deps.add(dep)); + + + if (options && options.except && options.only) { + throw new IdlHarnessError("The only and except options can't be used together."); + } + + const defined_or_untested = name => { + // NOTE: Deps are untested, so we're lenient, and skip re-encountered definitions. + // e.g. for 'idl' containing A:B, B:C, C:D + // array.add_idls(idl, {only: ['A','B']}). + // array.add_dependency_idls(idl); + // B would be encountered as tested, and encountered as a dep, so we ignore. + return name in this.members + || this.is_excluded_by_options(name, options); + } + // Maps name -> [parsed_idl, ...] + const process = function(parsed) { + var deps = []; + if (parsed.name) { + deps.push(parsed.name); + } else if (parsed.type === "includes") { + deps.push(parsed.target); + deps.push(parsed.includes); + } + + deps = deps.filter(function(name) { + if (!name + || name === parsed.name && defined_or_untested(name) + || !all_deps.has(name)) { + // Flag as skipped, if it's not already processed, so we can + // come back to it later if we retrospectively call it a dep. + if (name && !(name in this.members)) { + this.skipped.has(name) + ? this.skipped.get(name).push(parsed) + : this.skipped.set(name, [parsed]); + } + return false; + } + return true; + }.bind(this)); + + deps.forEach(function(name) { + if (!new_options.only.includes(name)) { + new_options.only.push(name); + } + + const follow_up = new Set(); + for (const dep_type of ["inheritance", "includes"]) { + if (parsed[dep_type]) { + const inheriting = parsed[dep_type]; + const inheritor = parsed.name || parsed.target; + const deps = [inheriting]; + // For A includes B, we can ignore A, unless B (or some of its + // members) is being tested. + if (dep_type !== "includes" + || inheriting in this.members && !this.members[inheriting].untested + || this.partials.some(function(p) { + return p.name === inheriting; + })) { + deps.push(inheritor); + } + for (const dep of deps) { + if (!new_options.only.includes(dep)) { + new_options.only.push(dep); + } + all_deps.add(dep); + follow_up.add(dep); + } + } + } + + for (const deferred of follow_up) { + if (this.skipped.has(deferred)) { + const next = this.skipped.get(deferred); + this.skipped.delete(deferred); + next.forEach(process); + } + } + }.bind(this)); + }.bind(this); + + for (let parsed of parsed_idls) { + process(parsed); + } + + this.mark_as_untested(parsed_idls); + + if (new_options.only.length) { + this.internal_add_idls(parsed_idls, new_options); + } +} + +IdlArray.prototype.internal_add_idls = function(parsed_idls, options) +{ + /** + * Internal helper called by add_idls() and add_untested_idls(). + * + * parsed_idls is an array of objects that come from WebIDLParser.js's + * "definitions" production. The add_untested_idls() entry point + * additionally sets an .untested property on each object (and its + * .members) so that they'll be skipped by test() -- they'll only be + * used for base interfaces of tested interfaces, return types, etc. + * + * options is a dictionary that can have an only or except member which are + * arrays. If only is given then only members, partials and interface + * targets listed will be added, and if except is given only those that + * aren't listed will be added. Only one of only and except can be used. + */ + + if (options && options.only && options.except) + { + throw new IdlHarnessError("The only and except options can't be used together."); + } + + var should_skip = name => { + return this.is_excluded_by_options(name, options); + } + + parsed_idls.forEach(function(parsed_idl) + { + var partial_types = [ + "interface", + "interface mixin", + "dictionary", + "namespace", + ]; + if (parsed_idl.partial && partial_types.includes(parsed_idl.type)) + { + if (should_skip(parsed_idl.name)) + { + return; + } + this.partials.push(parsed_idl); + return; + } + + if (parsed_idl.type == "includes") + { + if (should_skip(parsed_idl.target)) + { + return; + } + this.includes.push(parsed_idl); + return; + } + + parsed_idl.array = this; + if (should_skip(parsed_idl.name)) + { + return; + } + if (parsed_idl.name in this.members) + { + throw new IdlHarnessError("Duplicate identifier " + parsed_idl.name); + } + + switch(parsed_idl.type) + { + case "interface": + this.members[parsed_idl.name] = + new IdlInterface(parsed_idl, /* is_callback = */ false, /* is_mixin = */ false); + break; + + case "interface mixin": + this.members[parsed_idl.name] = + new IdlInterface(parsed_idl, /* is_callback = */ false, /* is_mixin = */ true); + break; + + case "dictionary": + // Nothing to test, but we need the dictionary info around for type + // checks + this.members[parsed_idl.name] = new IdlDictionary(parsed_idl); + break; + + case "typedef": + this.members[parsed_idl.name] = new IdlTypedef(parsed_idl); + break; + + case "callback": + this.members[parsed_idl.name] = new IdlCallback(parsed_idl); + break; + + case "enum": + this.members[parsed_idl.name] = new IdlEnum(parsed_idl); + break; + + case "callback interface": + this.members[parsed_idl.name] = + new IdlInterface(parsed_idl, /* is_callback = */ true, /* is_mixin = */ false); + break; + + case "namespace": + this.members[parsed_idl.name] = new IdlNamespace(parsed_idl); + break; + + default: + throw parsed_idl.name + ": " + parsed_idl.type + " not yet supported"; + } + }.bind(this)); +}; + +IdlArray.prototype.add_objects = function(dict) +{ + /** Entry point. See documentation at beginning of file. */ + for (var k in dict) + { + if (k in this.objects) + { + this.objects[k] = this.objects[k].concat(dict[k]); + } + else + { + this.objects[k] = dict[k]; + } + } +}; + +IdlArray.prototype.prevent_multiple_testing = function(name) +{ + /** Entry point. See documentation at beginning of file. */ + this.members[name].prevent_multiple_testing = true; +}; + +IdlArray.prototype.is_json_type = function(type) +{ + /** + * Checks whether type is a JSON type as per + * https://webidl.spec.whatwg.org/#dfn-json-types + */ + + var idlType = type.idlType; + + if (type.generic == "Promise") { return false; } + + // nullable and annotated types don't need to be handled separately, + // as webidl2 doesn't represent them wrapped-up (as they're described + // in WebIDL). + + // union and record types + if (type.union || type.generic == "record") { + return idlType.every(this.is_json_type, this); + } + + // sequence types + if (type.generic == "sequence" || type.generic == "FrozenArray") { + return this.is_json_type(idlType[0]); + } + + if (typeof idlType != "string") { throw new Error("Unexpected type " + JSON.stringify(idlType)); } + + switch (idlType) + { + // Numeric types + case "byte": + case "octet": + case "short": + case "unsigned short": + case "long": + case "unsigned long": + case "long long": + case "unsigned long long": + case "float": + case "double": + case "unrestricted float": + case "unrestricted double": + // boolean + case "boolean": + // string types + case "DOMString": + case "ByteString": + case "USVString": + // object type + case "object": + return true; + case "Error": + case "DOMException": + case "Int8Array": + case "Int16Array": + case "Int32Array": + case "Uint8Array": + case "Uint16Array": + case "Uint32Array": + case "Uint8ClampedArray": + case "BigInt64Array": + case "BigUint64Array": + case "Float32Array": + case "Float64Array": + case "ArrayBuffer": + case "DataView": + case "any": + return false; + default: + var thing = this.members[idlType]; + if (!thing) { throw new Error("Type " + idlType + " not found"); } + if (thing instanceof IdlEnum) { return true; } + + if (thing instanceof IdlTypedef) { + return this.is_json_type(thing.idlType); + } + + // dictionaries where all of their members are JSON types + if (thing instanceof IdlDictionary) { + const map = new Map(); + for (const dict of thing.get_reverse_inheritance_stack()) { + for (const m of dict.members) { + map.set(m.name, m.idlType); + } + } + return Array.from(map.values()).every(this.is_json_type, this); + } + + // interface types that have a toJSON operation declared on themselves or + // one of their inherited interfaces. + if (thing instanceof IdlInterface) { + var base; + while (thing) + { + if (thing.has_to_json_regular_operation()) { return true; } + var mixins = this.includes[thing.name]; + if (mixins) { + mixins = mixins.map(function(id) { + var mixin = this.members[id]; + if (!mixin) { + throw new Error("Interface " + id + " not found (implemented by " + thing.name + ")"); + } + return mixin; + }, this); + if (mixins.some(function(m) { return m.has_to_json_regular_operation() } )) { return true; } + } + if (!thing.base) { return false; } + base = this.members[thing.base]; + if (!base) { + throw new Error("Interface " + thing.base + " not found (inherited by " + thing.name + ")"); + } + thing = base; + } + return false; + } + return false; + } +}; + +function exposure_set(object, default_set) { + var exposed = object.extAttrs && object.extAttrs.filter(a => a.name === "Exposed"); + if (exposed && exposed.length > 1) { + throw new IdlHarnessError( + `Multiple 'Exposed' extended attributes on ${object.name}`); + } + + let result = default_set || ["Window"]; + if (result && !(result instanceof Set)) { + result = new Set(result); + } + if (exposed && exposed.length) { + const { rhs } = exposed[0]; + // Could be a list or a string. + const set = + rhs.type === "*" ? + [ "*" ] : + rhs.type === "identifier-list" ? + rhs.value.map(id => id.value) : + [ rhs.value ]; + result = new Set(set); + } + if (result && result.has("*")) { + return "*"; + } + if (result && result.has("Worker")) { + result.delete("Worker"); + result.add("DedicatedWorker"); + result.add("ServiceWorker"); + result.add("SharedWorker"); + } + return result; +} + +function exposed_in(globals) { + if (globals === "*") { + return true; + } + if ('Window' in self) { + return globals.has("Window"); + } + if ('DedicatedWorkerGlobalScope' in self && + self instanceof DedicatedWorkerGlobalScope) { + return globals.has("DedicatedWorker"); + } + if ('SharedWorkerGlobalScope' in self && + self instanceof SharedWorkerGlobalScope) { + return globals.has("SharedWorker"); + } + if ('ServiceWorkerGlobalScope' in self && + self instanceof ServiceWorkerGlobalScope) { + return globals.has("ServiceWorker"); + } + if (Object.getPrototypeOf(self) === Object.prototype) { + // ShadowRealm - only exposed with `"*"`. + return false; + } + throw new IdlHarnessError("Unexpected global object"); +} + +/** + * Asserts that the given error message is thrown for the given function. + * @param {string|IdlHarnessError} error Expected Error message. + * @param {Function} idlArrayFunc Function operating on an IdlArray that should throw. + */ +IdlArray.prototype.assert_throws = function(error, idlArrayFunc) +{ + try { + idlArrayFunc.call(this, this); + } catch (e) { + if (e instanceof AssertionError) { + throw e; + } + // Assertions for behaviour of the idlharness.js engine. + if (error instanceof IdlHarnessError) { + error = error.message; + } + if (e.message !== error) { + throw new IdlHarnessError(`${idlArrayFunc} threw "${e}", not the expected IdlHarnessError "${error}"`); + } + return; + } + throw new IdlHarnessError(`${idlArrayFunc} did not throw the expected IdlHarnessError`); +} + +IdlArray.prototype.test = function() +{ + /** Entry point. See documentation at beginning of file. */ + + // First merge in all partial definitions and interface mixins. + this.merge_partials(); + this.merge_mixins(); + + // Assert B defined for A : B + for (const member of Object.values(this.members).filter(m => m.base)) { + const lhs = member.name; + const rhs = member.base; + if (!(rhs in this.members)) throw new IdlHarnessError(`${lhs} inherits ${rhs}, but ${rhs} is undefined.`); + const lhs_is_interface = this.members[lhs] instanceof IdlInterface; + const rhs_is_interface = this.members[rhs] instanceof IdlInterface; + if (rhs_is_interface != lhs_is_interface) { + if (!lhs_is_interface) throw new IdlHarnessError(`${lhs} inherits ${rhs}, but ${lhs} is not an interface.`); + if (!rhs_is_interface) throw new IdlHarnessError(`${lhs} inherits ${rhs}, but ${rhs} is not an interface.`); + } + // Check for circular dependencies. + member.get_reverse_inheritance_stack(); + } + + Object.getOwnPropertyNames(this.members).forEach(function(memberName) { + var member = this.members[memberName]; + if (!(member instanceof IdlInterface)) { + return; + } + + var globals = exposure_set(member); + member.exposed = exposed_in(globals); + member.exposureSet = globals; + }.bind(this)); + + // Now run test() on every member, and test_object() for every object. + for (var name in this.members) + { + this.members[name].test(); + if (name in this.objects) + { + const objects = this.objects[name]; + if (!objects || !Array.isArray(objects)) { + throw new IdlHarnessError(`Invalid or empty objects for member ${name}`); + } + objects.forEach(function(str) + { + if (!this.members[name] || !(this.members[name] instanceof IdlInterface)) { + throw new IdlHarnessError(`Invalid object member name ${name}`); + } + this.members[name].test_object(str); + }.bind(this)); + } + } +}; + +IdlArray.prototype.merge_partials = function() +{ + const testedPartials = new Map(); + this.partials.forEach(function(parsed_idl) + { + const originalExists = parsed_idl.name in this.members + && (this.members[parsed_idl.name] instanceof IdlInterface + || this.members[parsed_idl.name] instanceof IdlDictionary + || this.members[parsed_idl.name] instanceof IdlNamespace); + + // Ensure unique test name in case of multiple partials. + let partialTestName = parsed_idl.name; + let partialTestCount = 1; + if (testedPartials.has(parsed_idl.name)) { + partialTestCount += testedPartials.get(parsed_idl.name); + partialTestName = `${partialTestName}[${partialTestCount}]`; + } + testedPartials.set(parsed_idl.name, partialTestCount); + + if (!parsed_idl.untested) { + test(function () { + assert_true(originalExists, `Original ${parsed_idl.type} should be defined`); + + var expected; + switch (parsed_idl.type) { + case 'dictionary': expected = IdlDictionary; break; + case 'namespace': expected = IdlNamespace; break; + case 'interface': + case 'interface mixin': + default: + expected = IdlInterface; break; + } + assert_true( + expected.prototype.isPrototypeOf(this.members[parsed_idl.name]), + `Original ${parsed_idl.name} definition should have type ${parsed_idl.type}`); + }.bind(this), `Partial ${parsed_idl.type} ${partialTestName}: original ${parsed_idl.type} defined`); + } + if (!originalExists) { + // Not good.. but keep calm and carry on. + return; + } + + if (parsed_idl.extAttrs) + { + // Special-case "Exposed". Must be a subset of original interface's exposure. + // Exposed on a partial is the equivalent of having the same Exposed on all nested members. + // See https://github.com/heycam/webidl/issues/154 for discrepency between Exposed and + // other extended attributes on partial interfaces. + const exposureAttr = parsed_idl.extAttrs.find(a => a.name === "Exposed"); + if (exposureAttr) { + if (!parsed_idl.untested) { + test(function () { + const partialExposure = exposure_set(parsed_idl); + const memberExposure = exposure_set(this.members[parsed_idl.name]); + if (memberExposure === "*") { + return; + } + if (partialExposure === "*") { + throw new IdlHarnessError( + `Partial ${parsed_idl.name} ${parsed_idl.type} is exposed everywhere, the original ${parsed_idl.type} is not.`); + } + partialExposure.forEach(name => { + if (!memberExposure || !memberExposure.has(name)) { + throw new IdlHarnessError( + `Partial ${parsed_idl.name} ${parsed_idl.type} is exposed to '${name}', the original ${parsed_idl.type} is not.`); + } + }); + }.bind(this), `Partial ${parsed_idl.type} ${partialTestName}: valid exposure set`); + } + parsed_idl.members.forEach(function (member) { + member.extAttrs.push(exposureAttr); + }.bind(this)); + } + + parsed_idl.extAttrs.forEach(function(extAttr) + { + // "Exposed" already handled above. + if (extAttr.name === "Exposed") { + return; + } + this.members[parsed_idl.name].extAttrs.push(extAttr); + }.bind(this)); + } + if (parsed_idl.members.length) { + test(function () { + var clash = parsed_idl.members.find(function(member) { + return this.members[parsed_idl.name].members.find(function(m) { + return this.are_duplicate_members(m, member); + }.bind(this)); + }.bind(this)); + parsed_idl.members.forEach(function(member) + { + this.members[parsed_idl.name].members.push(new IdlInterfaceMember(member)); + }.bind(this)); + assert_true(!clash, "member " + (clash && clash.name) + " is unique"); + }.bind(this), `Partial ${parsed_idl.type} ${partialTestName}: member names are unique`); + } + }.bind(this)); + this.partials = []; +} + +IdlArray.prototype.merge_mixins = function() +{ + for (const parsed_idl of this.includes) + { + const lhs = parsed_idl.target; + const rhs = parsed_idl.includes; + + var errStr = lhs + " includes " + rhs + ", but "; + if (!(lhs in this.members)) throw errStr + lhs + " is undefined."; + if (!(this.members[lhs] instanceof IdlInterface)) throw errStr + lhs + " is not an interface."; + if (!(rhs in this.members)) throw errStr + rhs + " is undefined."; + if (!(this.members[rhs] instanceof IdlInterface)) throw errStr + rhs + " is not an interface."; + + if (this.members[rhs].members.length) { + test(function () { + var clash = this.members[rhs].members.find(function(member) { + return this.members[lhs].members.find(function(m) { + return this.are_duplicate_members(m, member); + }.bind(this)); + }.bind(this)); + this.members[rhs].members.forEach(function(member) { + assert_true( + this.members[lhs].members.every(m => !this.are_duplicate_members(m, member)), + "member " + member.name + " is unique"); + this.members[lhs].members.push(new IdlInterfaceMember(member)); + }.bind(this)); + assert_true(!clash, "member " + (clash && clash.name) + " is unique"); + }.bind(this), lhs + " includes " + rhs + ": member names are unique"); + } + } + this.includes = []; +} + +IdlArray.prototype.are_duplicate_members = function(m1, m2) { + if (m1.name !== m2.name) { + return false; + } + if (m1.type === 'operation' && m2.type === 'operation' + && m1.arguments.length !== m2.arguments.length) { + // Method overload. TODO: Deep comparison of arguments. + return false; + } + return true; +} + +IdlArray.prototype.assert_type_is = function(value, type) +{ + if (type.idlType in this.members + && this.members[type.idlType] instanceof IdlTypedef) { + this.assert_type_is(value, this.members[type.idlType].idlType); + return; + } + + if (type.nullable && value === null) + { + // This is fine + return; + } + + if (type.union) { + for (var i = 0; i < type.idlType.length; i++) { + try { + this.assert_type_is(value, type.idlType[i]); + // No AssertionError, so we match one type in the union + return; + } catch(e) { + if (e instanceof AssertionError) { + // We didn't match this type, let's try some others + continue; + } + throw e; + } + } + // TODO: Is there a nice way to list the union's types in the message? + assert_true(false, "Attribute has value " + format_value(value) + + " which doesn't match any of the types in the union"); + + } + + /** + * Helper function that tests that value is an instance of type according + * to the rules of WebIDL. value is any JavaScript value, and type is an + * object produced by WebIDLParser.js' "type" production. That production + * is fairly elaborate due to the complexity of WebIDL's types, so it's + * best to look at the grammar to figure out what properties it might have. + */ + if (type.idlType == "any") + { + // No assertions to make + return; + } + + if (type.array) + { + // TODO: not supported yet + return; + } + + if (type.generic === "sequence" || type.generic == "ObservableArray") + { + assert_true(Array.isArray(value), "should be an Array"); + if (!value.length) + { + // Nothing we can do. + return; + } + this.assert_type_is(value[0], type.idlType[0]); + return; + } + + if (type.generic === "Promise") { + assert_true("then" in value, "Attribute with a Promise type should have a then property"); + // TODO: Ideally, we would check on project fulfillment + // that we get the right type + // but that would require making the type check async + return; + } + + if (type.generic === "FrozenArray") { + assert_true(Array.isArray(value), "Value should be array"); + assert_true(Object.isFrozen(value), "Value should be frozen"); + if (!value.length) + { + // Nothing we can do. + return; + } + this.assert_type_is(value[0], type.idlType[0]); + return; + } + + type = Array.isArray(type.idlType) ? type.idlType[0] : type.idlType; + + switch(type) + { + case "undefined": + assert_equals(value, undefined); + return; + + case "boolean": + assert_equals(typeof value, "boolean"); + return; + + case "byte": + assert_equals(typeof value, "number"); + assert_equals(value, Math.floor(value), "should be an integer"); + assert_true(-128 <= value && value <= 127, "byte " + value + " should be in range [-128, 127]"); + return; + + case "octet": + assert_equals(typeof value, "number"); + assert_equals(value, Math.floor(value), "should be an integer"); + assert_true(0 <= value && value <= 255, "octet " + value + " should be in range [0, 255]"); + return; + + case "short": + assert_equals(typeof value, "number"); + assert_equals(value, Math.floor(value), "should be an integer"); + assert_true(-32768 <= value && value <= 32767, "short " + value + " should be in range [-32768, 32767]"); + return; + + case "unsigned short": + assert_equals(typeof value, "number"); + assert_equals(value, Math.floor(value), "should be an integer"); + assert_true(0 <= value && value <= 65535, "unsigned short " + value + " should be in range [0, 65535]"); + return; + + case "long": + assert_equals(typeof value, "number"); + assert_equals(value, Math.floor(value), "should be an integer"); + assert_true(-2147483648 <= value && value <= 2147483647, "long " + value + " should be in range [-2147483648, 2147483647]"); + return; + + case "unsigned long": + assert_equals(typeof value, "number"); + assert_equals(value, Math.floor(value), "should be an integer"); + assert_true(0 <= value && value <= 4294967295, "unsigned long " + value + " should be in range [0, 4294967295]"); + return; + + case "long long": + assert_equals(typeof value, "number"); + return; + + case "unsigned long long": + case "DOMTimeStamp": + assert_equals(typeof value, "number"); + assert_true(0 <= value, "unsigned long long should be positive"); + return; + + case "float": + assert_equals(typeof value, "number"); + assert_equals(value, Math.fround(value), "float rounded to 32-bit float should be itself"); + assert_not_equals(value, Infinity); + assert_not_equals(value, -Infinity); + assert_not_equals(value, NaN); + return; + + case "DOMHighResTimeStamp": + case "double": + assert_equals(typeof value, "number"); + assert_not_equals(value, Infinity); + assert_not_equals(value, -Infinity); + assert_not_equals(value, NaN); + return; + + case "unrestricted float": + assert_equals(typeof value, "number"); + assert_equals(value, Math.fround(value), "unrestricted float rounded to 32-bit float should be itself"); + return; + + case "unrestricted double": + assert_equals(typeof value, "number"); + return; + + case "DOMString": + assert_equals(typeof value, "string"); + return; + + case "ByteString": + assert_equals(typeof value, "string"); + assert_regexp_match(value, /^[\x00-\x7F]*$/); + return; + + case "USVString": + assert_equals(typeof value, "string"); + assert_regexp_match(value, /^([\x00-\ud7ff\ue000-\uffff]|[\ud800-\udbff][\udc00-\udfff])*$/); + return; + + case "ArrayBufferView": + assert_true(ArrayBuffer.isView(value)); + return; + + case "object": + assert_in_array(typeof value, ["object", "function"], "wrong type: not object or function"); + return; + } + + // This is a catch-all for any IDL type name which follows JS class + // semantics. This includes some non-interface IDL types (e.g. Int8Array, + // Function, ...), as well as any interface types that are not in the IDL + // that is fed to the harness. If an IDL type does not follow JS class + // semantics then it should go in the switch statement above. If an IDL + // type needs full checking, then the test should include it in the IDL it + // feeds to the harness. + if (!(type in this.members)) + { + assert_true(value instanceof self[type], "wrong type: not a " + type); + return; + } + + if (this.members[type] instanceof IdlInterface) + { + // We don't want to run the full + // IdlInterface.prototype.test_instance_of, because that could result + // in an infinite loop. TODO: This means we don't have tests for + // LegacyNoInterfaceObject interfaces, and we also can't test objects + // that come from another self. + assert_in_array(typeof value, ["object", "function"], "wrong type: not object or function"); + if (value instanceof Object + && !this.members[type].has_extended_attribute("LegacyNoInterfaceObject") + && type in self) + { + assert_true(value instanceof self[type], "instanceof " + type); + } + } + else if (this.members[type] instanceof IdlEnum) + { + assert_equals(typeof value, "string"); + } + else if (this.members[type] instanceof IdlDictionary) + { + // TODO: Test when we actually have something to test this on + } + else if (this.members[type] instanceof IdlCallback) + { + assert_equals(typeof value, "function"); + } + else + { + throw new IdlHarnessError("Type " + type + " isn't an interface, callback or dictionary"); + } +}; + +/// IdlObject /// +function IdlObject() {} +IdlObject.prototype.test = function() +{ + /** + * By default, this does nothing, so no actual tests are run for IdlObjects + * that don't define any (e.g., IdlDictionary at the time of this writing). + */ +}; + +IdlObject.prototype.has_extended_attribute = function(name) +{ + /** + * This is only meaningful for things that support extended attributes, + * such as interfaces, exceptions, and members. + */ + return this.extAttrs.some(function(o) + { + return o.name == name; + }); +}; + + +/// IdlDictionary /// +// Used for IdlArray.prototype.assert_type_is +function IdlDictionary(obj) +{ + /** + * obj is an object produced by the WebIDLParser.js "dictionary" + * production. + */ + + /** Self-explanatory. */ + this.name = obj.name; + + /** A back-reference to our IdlArray. */ + this.array = obj.array; + + /** An array of objects produced by the "dictionaryMember" production. */ + this.members = obj.members; + + /** + * The name (as a string) of the dictionary type we inherit from, or null + * if there is none. + */ + this.base = obj.inheritance; +} + +IdlDictionary.prototype = Object.create(IdlObject.prototype); + +IdlDictionary.prototype.get_reverse_inheritance_stack = function() { + return IdlInterface.prototype.get_reverse_inheritance_stack.call(this); +}; + +/// IdlInterface /// +function IdlInterface(obj, is_callback, is_mixin) +{ + /** + * obj is an object produced by the WebIDLParser.js "interface" production. + */ + + /** Self-explanatory. */ + this.name = obj.name; + + /** A back-reference to our IdlArray. */ + this.array = obj.array; + + /** + * An indicator of whether we should run tests on the interface object and + * interface prototype object. Tests on members are controlled by .untested + * on each member, not this. + */ + this.untested = obj.untested; + + /** An array of objects produced by the "ExtAttr" production. */ + this.extAttrs = obj.extAttrs; + + /** An array of IdlInterfaceMembers. */ + this.members = obj.members.map(function(m){return new IdlInterfaceMember(m); }); + if (this.has_extended_attribute("LegacyUnforgeable")) { + this.members + .filter(function(m) { return m.special !== "static" && (m.type == "attribute" || m.type == "operation"); }) + .forEach(function(m) { return m.isUnforgeable = true; }); + } + + /** + * The name (as a string) of the type we inherit from, or null if there is + * none. + */ + this.base = obj.inheritance; + + this._is_callback = is_callback; + this._is_mixin = is_mixin; +} +IdlInterface.prototype = Object.create(IdlObject.prototype); +IdlInterface.prototype.is_callback = function() +{ + return this._is_callback; +}; + +IdlInterface.prototype.is_mixin = function() +{ + return this._is_mixin; +}; + +IdlInterface.prototype.has_constants = function() +{ + return this.members.some(function(member) { + return member.type === "const"; + }); +}; + +IdlInterface.prototype.get_unscopables = function() +{ + return this.members.filter(function(member) { + return member.isUnscopable; + }); +}; + +IdlInterface.prototype.is_global = function() +{ + return this.extAttrs.some(function(attribute) { + return attribute.name === "Global"; + }); +}; + +/** + * Value of the LegacyNamespace extended attribute, if any. + * + * https://webidl.spec.whatwg.org/#LegacyNamespace + */ +IdlInterface.prototype.get_legacy_namespace = function() +{ + var legacyNamespace = this.extAttrs.find(function(attribute) { + return attribute.name === "LegacyNamespace"; + }); + return legacyNamespace ? legacyNamespace.rhs.value : undefined; +}; + +IdlInterface.prototype.get_interface_object_owner = function() +{ + var legacyNamespace = this.get_legacy_namespace(); + return legacyNamespace ? self[legacyNamespace] : self; +}; + +IdlInterface.prototype.should_have_interface_object = function() +{ + // "For every interface that is exposed in a given ECMAScript global + // environment and: + // * is a callback interface that has constants declared on it, or + // * is a non-callback interface that is not declared with the + // [LegacyNoInterfaceObject] extended attribute, + // a corresponding property MUST exist on the ECMAScript global object. + + return this.is_callback() ? this.has_constants() : !this.has_extended_attribute("LegacyNoInterfaceObject"); +}; + +IdlInterface.prototype.assert_interface_object_exists = function() +{ + var owner = this.get_legacy_namespace() || "self"; + assert_own_property(self[owner], this.name, owner + " does not have own property " + format_value(this.name)); +}; + +IdlInterface.prototype.get_interface_object = function() { + if (!this.should_have_interface_object()) { + var reason = this.is_callback() ? "lack of declared constants" : "declared [LegacyNoInterfaceObject] attribute"; + throw new IdlHarnessError(this.name + " has no interface object due to " + reason); + } + + return this.get_interface_object_owner()[this.name]; +}; + +IdlInterface.prototype.get_qualified_name = function() { + // https://webidl.spec.whatwg.org/#qualified-name + var legacyNamespace = this.get_legacy_namespace(); + if (legacyNamespace) { + return legacyNamespace + "." + this.name; + } + return this.name; +}; + +IdlInterface.prototype.has_to_json_regular_operation = function() { + return this.members.some(function(m) { + return m.is_to_json_regular_operation(); + }); +}; + +IdlInterface.prototype.has_default_to_json_regular_operation = function() { + return this.members.some(function(m) { + return m.is_to_json_regular_operation() && m.has_extended_attribute("Default"); + }); +}; + +/** + * Implementation of https://webidl.spec.whatwg.org/#create-an-inheritance-stack + * with the order reversed. + * + * The order is reversed so that the base class comes first in the list, because + * this is what all call sites need. + * + * So given: + * + * A : B {}; + * B : C {}; + * C {}; + * + * then A.get_reverse_inheritance_stack() returns [C, B, A], + * and B.get_reverse_inheritance_stack() returns [C, B]. + * + * Note: as dictionary inheritance is expressed identically by the AST, + * this works just as well for getting a stack of inherited dictionaries. + */ +IdlInterface.prototype.get_reverse_inheritance_stack = function() { + const stack = [this]; + let idl_interface = this; + while (idl_interface.base) { + const base = this.array.members[idl_interface.base]; + if (!base) { + throw new Error(idl_interface.type + " " + idl_interface.base + " not found (inherited by " + idl_interface.name + ")"); + } else if (stack.indexOf(base) > -1) { + stack.unshift(base); + const dep_chain = stack.map(i => i.name).join(','); + throw new IdlHarnessError(`${this.name} has a circular dependency: ${dep_chain}`); + } + idl_interface = base; + stack.unshift(idl_interface); + } + return stack; +}; + +/** + * Implementation of + * https://webidl.spec.whatwg.org/#default-tojson-operation + * for testing purposes. + * + * Collects the IDL types of the attributes that meet the criteria + * for inclusion in the default toJSON operation for easy + * comparison with actual value + */ +IdlInterface.prototype.default_to_json_operation = function() { + const map = new Map() + let isDefault = false; + for (const I of this.get_reverse_inheritance_stack()) { + if (I.has_default_to_json_regular_operation()) { + isDefault = true; + for (const m of I.members) { + if (m.special !== "static" && m.type == "attribute" && I.array.is_json_type(m.idlType)) { + map.set(m.name, m.idlType); + } + } + } else if (I.has_to_json_regular_operation()) { + isDefault = false; + } + } + return isDefault ? map : null; +}; + +IdlInterface.prototype.test = function() +{ + if (this.has_extended_attribute("LegacyNoInterfaceObject") || this.is_mixin()) + { + // No tests to do without an instance. TODO: We should still be able + // to run tests on the prototype object, if we obtain one through some + // other means. + return; + } + + // If the interface object is not exposed, only test that. Members can't be + // tested either, but objects could still be tested in |test_object|. + if (!this.exposed) + { + if (!this.untested) + { + subsetTestByKey(this.name, test, function() { + assert_false(this.name in self); + }.bind(this), this.name + " interface: existence and properties of interface object"); + } + return; + } + + if (!this.untested) + { + // First test things to do with the exception/interface object and + // exception/interface prototype object. + this.test_self(); + } + // Then test things to do with its members (constants, fields, attributes, + // operations, . . .). These are run even if .untested is true, because + // members might themselves be marked as .untested. This might happen to + // interfaces if the interface itself is untested but a partial interface + // that extends it is tested -- then the interface itself and its initial + // members will be marked as untested, but the members added by the partial + // interface are still tested. + this.test_members(); +}; + +IdlInterface.prototype.constructors = function() +{ + return this.members + .filter(function(m) { return m.type == "constructor"; }); +} + +IdlInterface.prototype.test_self = function() +{ + subsetTestByKey(this.name, test, function() + { + if (!this.should_have_interface_object()) { + return; + } + + // The name of the property is the identifier of the interface, and its + // value is an object called the interface object. + // The property has the attributes { [[Writable]]: true, + // [[Enumerable]]: false, [[Configurable]]: true }." + // TODO: Should we test here that the property is actually writable + // etc., or trust getOwnPropertyDescriptor? + this.assert_interface_object_exists(); + var desc = Object.getOwnPropertyDescriptor(this.get_interface_object_owner(), this.name); + assert_false("get" in desc, "self's property " + format_value(this.name) + " should not have a getter"); + assert_false("set" in desc, "self's property " + format_value(this.name) + " should not have a setter"); + assert_true(desc.writable, "self's property " + format_value(this.name) + " should be writable"); + assert_false(desc.enumerable, "self's property " + format_value(this.name) + " should not be enumerable"); + assert_true(desc.configurable, "self's property " + format_value(this.name) + " should be configurable"); + + if (this.is_callback()) { + // "The internal [[Prototype]] property of an interface object for + // a callback interface must be the Function.prototype object." + assert_equals(Object.getPrototypeOf(this.get_interface_object()), Function.prototype, + "prototype of self's property " + format_value(this.name) + " is not Object.prototype"); + + return; + } + + // "The interface object for a given non-callback interface is a + // function object." + // "If an object is defined to be a function object, then it has + // characteristics as follows:" + + // Its [[Prototype]] internal property is otherwise specified (see + // below). + + // "* Its [[Get]] internal property is set as described in ECMA-262 + // section 9.1.8." + // Not much to test for this. + + // "* Its [[Construct]] internal property is set as described in + // ECMA-262 section 19.2.2.3." + + // "* Its @@hasInstance property is set as described in ECMA-262 + // section 19.2.3.8, unless otherwise specified." + // TODO + + // ES6 (rev 30) 19.1.3.6: + // "Else, if O has a [[Call]] internal method, then let builtinTag be + // "Function"." + assert_class_string(this.get_interface_object(), "Function", "class string of " + this.name); + + // "The [[Prototype]] internal property of an interface object for a + // non-callback interface is determined as follows:" + var prototype = Object.getPrototypeOf(this.get_interface_object()); + if (this.base) { + // "* If the interface inherits from some other interface, the + // value of [[Prototype]] is the interface object for that other + // interface." + var inherited_interface = this.array.members[this.base]; + if (!inherited_interface.has_extended_attribute("LegacyNoInterfaceObject")) { + inherited_interface.assert_interface_object_exists(); + assert_equals(prototype, inherited_interface.get_interface_object(), + 'prototype of ' + this.name + ' is not ' + + this.base); + } + } else { + // "If the interface doesn't inherit from any other interface, the + // value of [[Prototype]] is %FunctionPrototype% ([ECMA-262], + // section 6.1.7.4)." + assert_equals(prototype, Function.prototype, + "prototype of self's property " + format_value(this.name) + " is not Function.prototype"); + } + + // Always test for [[Construct]]: + // https://github.com/heycam/webidl/issues/698 + assert_true(isConstructor(this.get_interface_object()), "interface object must pass IsConstructor check"); + + var interface_object = this.get_interface_object(); + assert_throws_js(globalOf(interface_object).TypeError, function() { + interface_object(); + }, "interface object didn't throw TypeError when called as a function"); + + if (!this.constructors().length) { + assert_throws_js(globalOf(interface_object).TypeError, function() { + new interface_object(); + }, "interface object didn't throw TypeError when called as a constructor"); + } + }.bind(this), this.name + " interface: existence and properties of interface object"); + + if (this.should_have_interface_object() && !this.is_callback()) { + subsetTestByKey(this.name, test, function() { + // This function tests WebIDL as of 2014-10-25. + // https://webidl.spec.whatwg.org/#es-interface-call + + this.assert_interface_object_exists(); + + // "Interface objects for non-callback interfaces MUST have a + // property named “length” with attributes { [[Writable]]: false, + // [[Enumerable]]: false, [[Configurable]]: true } whose value is + // a Number." + assert_own_property(this.get_interface_object(), "length"); + var desc = Object.getOwnPropertyDescriptor(this.get_interface_object(), "length"); + assert_false("get" in desc, this.name + ".length should not have a getter"); + assert_false("set" in desc, this.name + ".length should not have a setter"); + assert_false(desc.writable, this.name + ".length should not be writable"); + assert_false(desc.enumerable, this.name + ".length should not be enumerable"); + assert_true(desc.configurable, this.name + ".length should be configurable"); + + var constructors = this.constructors(); + var expected_length = minOverloadLength(constructors); + assert_equals(this.get_interface_object().length, expected_length, "wrong value for " + this.name + ".length"); + }.bind(this), this.name + " interface object length"); + } + + if (this.should_have_interface_object()) { + subsetTestByKey(this.name, test, function() { + // This function tests WebIDL as of 2015-11-17. + // https://webidl.spec.whatwg.org/#interface-object + + this.assert_interface_object_exists(); + + // "All interface objects must have a property named “name” with + // attributes { [[Writable]]: false, [[Enumerable]]: false, + // [[Configurable]]: true } whose value is the identifier of the + // corresponding interface." + + assert_own_property(this.get_interface_object(), "name"); + var desc = Object.getOwnPropertyDescriptor(this.get_interface_object(), "name"); + assert_false("get" in desc, this.name + ".name should not have a getter"); + assert_false("set" in desc, this.name + ".name should not have a setter"); + assert_false(desc.writable, this.name + ".name should not be writable"); + assert_false(desc.enumerable, this.name + ".name should not be enumerable"); + assert_true(desc.configurable, this.name + ".name should be configurable"); + assert_equals(this.get_interface_object().name, this.name, "wrong value for " + this.name + ".name"); + }.bind(this), this.name + " interface object name"); + } + + + if (this.has_extended_attribute("LegacyWindowAlias")) { + subsetTestByKey(this.name, test, function() + { + var aliasAttrs = this.extAttrs.filter(function(o) { return o.name === "LegacyWindowAlias"; }); + if (aliasAttrs.length > 1) { + throw new IdlHarnessError("Invalid IDL: multiple LegacyWindowAlias extended attributes on " + this.name); + } + if (this.is_callback()) { + throw new IdlHarnessError("Invalid IDL: LegacyWindowAlias extended attribute on non-interface " + this.name); + } + if (!(this.exposureSet === "*" || this.exposureSet.has("Window"))) { + throw new IdlHarnessError("Invalid IDL: LegacyWindowAlias extended attribute on " + this.name + " which is not exposed in Window"); + } + // TODO: when testing of [LegacyNoInterfaceObject] interfaces is supported, + // check that it's not specified together with LegacyWindowAlias. + + // TODO: maybe check that [LegacyWindowAlias] is not specified on a partial interface. + + var rhs = aliasAttrs[0].rhs; + if (!rhs) { + throw new IdlHarnessError("Invalid IDL: LegacyWindowAlias extended attribute on " + this.name + " without identifier"); + } + var aliases; + if (rhs.type === "identifier-list") { + aliases = rhs.value.map(id => id.value); + } else { // rhs.type === identifier + aliases = [ rhs.value ]; + } + + // OK now actually check the aliases... + var alias; + if (exposed_in(exposure_set(this, this.exposureSet)) && 'document' in self) { + for (alias of aliases) { + assert_true(alias in self, alias + " should exist"); + assert_equals(self[alias], this.get_interface_object(), "self." + alias + " should be the same value as self." + this.get_qualified_name()); + var desc = Object.getOwnPropertyDescriptor(self, alias); + assert_equals(desc.value, this.get_interface_object(), "wrong value in " + alias + " property descriptor"); + assert_true(desc.writable, alias + " should be writable"); + assert_false(desc.enumerable, alias + " should not be enumerable"); + assert_true(desc.configurable, alias + " should be configurable"); + assert_false('get' in desc, alias + " should not have a getter"); + assert_false('set' in desc, alias + " should not have a setter"); + } + } else { + for (alias of aliases) { + assert_false(alias in self, alias + " should not exist"); + } + } + + }.bind(this), this.name + " interface: legacy window alias"); + } + + if (this.has_extended_attribute("LegacyFactoryFunction")) { + var constructors = this.extAttrs + .filter(function(attr) { return attr.name == "LegacyFactoryFunction"; }); + if (constructors.length !== 1) { + throw new IdlHarnessError("Internal error: missing support for multiple LegacyFactoryFunction extended attributes"); + } + var constructor = constructors[0]; + var min_length = minOverloadLength([constructor]); + + subsetTestByKey(this.name, test, function() + { + // This function tests WebIDL as of 2019-01-14. + + // "for every [LegacyFactoryFunction] extended attribute on an exposed + // interface, a corresponding property must exist on the ECMAScript + // global object. The name of the property is the + // [LegacyFactoryFunction]'s identifier, and its value is an object + // called a named constructor, ... . The property has the attributes + // { [[Writable]]: true, [[Enumerable]]: false, + // [[Configurable]]: true }." + var name = constructor.rhs.value; + assert_own_property(self, name); + var desc = Object.getOwnPropertyDescriptor(self, name); + assert_equals(desc.value, self[name], "wrong value in " + name + " property descriptor"); + assert_true(desc.writable, name + " should be writable"); + assert_false(desc.enumerable, name + " should not be enumerable"); + assert_true(desc.configurable, name + " should be configurable"); + assert_false("get" in desc, name + " should not have a getter"); + assert_false("set" in desc, name + " should not have a setter"); + }.bind(this), this.name + " interface: named constructor"); + + subsetTestByKey(this.name, test, function() + { + // This function tests WebIDL as of 2019-01-14. + + // "2. Let F be ! CreateBuiltinFunction(realm, steps, + // realm.[[Intrinsics]].[[%FunctionPrototype%]])." + var name = constructor.rhs.value; + var value = self[name]; + assert_equals(typeof value, "function", "type of value in " + name + " property descriptor"); + assert_not_equals(value, this.get_interface_object(), "wrong value in " + name + " property descriptor"); + assert_equals(Object.getPrototypeOf(value), Function.prototype, "wrong value for " + name + "'s prototype"); + }.bind(this), this.name + " interface: named constructor object"); + + subsetTestByKey(this.name, test, function() + { + // This function tests WebIDL as of 2019-01-14. + + // "7. Let proto be the interface prototype object of interface I + // in realm. + // "8. Perform ! DefinePropertyOrThrow(F, "prototype", + // PropertyDescriptor{ + // [[Value]]: proto, [[Writable]]: false, + // [[Enumerable]]: false, [[Configurable]]: false + // })." + var name = constructor.rhs.value; + var expected = this.get_interface_object().prototype; + var desc = Object.getOwnPropertyDescriptor(self[name], "prototype"); + assert_equals(desc.value, expected, "wrong value for " + name + ".prototype"); + assert_false(desc.writable, "prototype should not be writable"); + assert_false(desc.enumerable, "prototype should not be enumerable"); + assert_false(desc.configurable, "prototype should not be configurable"); + assert_false("get" in desc, "prototype should not have a getter"); + assert_false("set" in desc, "prototype should not have a setter"); + }.bind(this), this.name + " interface: named constructor prototype property"); + + subsetTestByKey(this.name, test, function() + { + // This function tests WebIDL as of 2019-01-14. + + // "3. Perform ! SetFunctionName(F, id)." + var name = constructor.rhs.value; + var desc = Object.getOwnPropertyDescriptor(self[name], "name"); + assert_equals(desc.value, name, "wrong value for " + name + ".name"); + assert_false(desc.writable, "name should not be writable"); + assert_false(desc.enumerable, "name should not be enumerable"); + assert_true(desc.configurable, "name should be configurable"); + assert_false("get" in desc, "name should not have a getter"); + assert_false("set" in desc, "name should not have a setter"); + }.bind(this), this.name + " interface: named constructor name"); + + subsetTestByKey(this.name, test, function() + { + // This function tests WebIDL as of 2019-01-14. + + // "4. Initialize S to the effective overload set for constructors + // with identifier id on interface I and with argument count 0. + // "5. Let length be the length of the shortest argument list of + // the entries in S. + // "6. Perform ! SetFunctionLength(F, length)." + var name = constructor.rhs.value; + var desc = Object.getOwnPropertyDescriptor(self[name], "length"); + assert_equals(desc.value, min_length, "wrong value for " + name + ".length"); + assert_false(desc.writable, "length should not be writable"); + assert_false(desc.enumerable, "length should not be enumerable"); + assert_true(desc.configurable, "length should be configurable"); + assert_false("get" in desc, "length should not have a getter"); + assert_false("set" in desc, "length should not have a setter"); + }.bind(this), this.name + " interface: named constructor length"); + + subsetTestByKey(this.name, test, function() + { + // This function tests WebIDL as of 2019-01-14. + + // "1. Let steps be the following steps: + // " 1. If NewTarget is undefined, then throw a TypeError." + var name = constructor.rhs.value; + var args = constructor.arguments.map(function(arg) { + return create_suitable_object(arg.idlType); + }); + assert_throws_js(globalOf(self[name]).TypeError, function() { + self[name](...args); + }.bind(this)); + }.bind(this), this.name + " interface: named constructor without 'new'"); + } + + subsetTestByKey(this.name, test, function() + { + // This function tests WebIDL as of 2015-01-21. + // https://webidl.spec.whatwg.org/#interface-object + + if (!this.should_have_interface_object()) { + return; + } + + this.assert_interface_object_exists(); + + if (this.is_callback()) { + assert_false("prototype" in this.get_interface_object(), + this.name + ' should not have a "prototype" property'); + return; + } + + // "An interface object for a non-callback interface must have a + // property named “prototype” with attributes { [[Writable]]: false, + // [[Enumerable]]: false, [[Configurable]]: false } whose value is an + // object called the interface prototype object. This object has + // properties that correspond to the regular attributes and regular + // operations defined on the interface, and is described in more detail + // in section 4.5.4 below." + assert_own_property(this.get_interface_object(), "prototype", + 'interface "' + this.name + '" does not have own property "prototype"'); + var desc = Object.getOwnPropertyDescriptor(this.get_interface_object(), "prototype"); + assert_false("get" in desc, this.name + ".prototype should not have a getter"); + assert_false("set" in desc, this.name + ".prototype should not have a setter"); + assert_false(desc.writable, this.name + ".prototype should not be writable"); + assert_false(desc.enumerable, this.name + ".prototype should not be enumerable"); + assert_false(desc.configurable, this.name + ".prototype should not be configurable"); + + // Next, test that the [[Prototype]] of the interface prototype object + // is correct. (This is made somewhat difficult by the existence of + // [LegacyNoInterfaceObject].) + // TODO: Aryeh thinks there's at least other place in this file where + // we try to figure out if an interface prototype object is + // correct. Consolidate that code. + + // "The interface prototype object for a given interface A must have an + // internal [[Prototype]] property whose value is returned from the + // following steps: + // "If A is declared with the [Global] extended + // attribute, and A supports named properties, then return the named + // properties object for A, as defined in §3.6.4 Named properties + // object. + // "Otherwise, if A is declared to inherit from another interface, then + // return the interface prototype object for the inherited interface. + // "Otherwise, return %ObjectPrototype%. + // + // "In the ECMAScript binding, the DOMException type has some additional + // requirements: + // + // "Unlike normal interface types, the interface prototype object + // for DOMException must have as its [[Prototype]] the intrinsic + // object %ErrorPrototype%." + // + if (this.name === "Window") { + assert_class_string(Object.getPrototypeOf(this.get_interface_object().prototype), + 'WindowProperties', + 'Class name for prototype of Window' + + '.prototype is not "WindowProperties"'); + } else { + var inherit_interface, inherit_interface_interface_object; + if (this.base) { + inherit_interface = this.base; + var parent = this.array.members[inherit_interface]; + if (!parent.has_extended_attribute("LegacyNoInterfaceObject")) { + parent.assert_interface_object_exists(); + inherit_interface_interface_object = parent.get_interface_object(); + } + } else if (this.name === "DOMException") { + inherit_interface = 'Error'; + inherit_interface_interface_object = self.Error; + } else { + inherit_interface = 'Object'; + inherit_interface_interface_object = self.Object; + } + if (inherit_interface_interface_object) { + assert_not_equals(inherit_interface_interface_object, undefined, + 'should inherit from ' + inherit_interface + ', but there is no such property'); + assert_own_property(inherit_interface_interface_object, 'prototype', + 'should inherit from ' + inherit_interface + ', but that object has no "prototype" property'); + assert_equals(Object.getPrototypeOf(this.get_interface_object().prototype), + inherit_interface_interface_object.prototype, + 'prototype of ' + this.name + '.prototype is not ' + inherit_interface + '.prototype'); + } else { + // We can't test that we get the correct object, because this is the + // only way to get our hands on it. We only test that its class + // string, at least, is correct. + assert_class_string(Object.getPrototypeOf(this.get_interface_object().prototype), + inherit_interface + 'Prototype', + 'Class name for prototype of ' + this.name + + '.prototype is not "' + inherit_interface + 'Prototype"'); + } + } + + // "The class string of an interface prototype object is the + // concatenation of the interface’s qualified identifier and the string + // “Prototype”." + + // Skip these tests for now due to a specification issue about + // prototype name. + // https://www.w3.org/Bugs/Public/show_bug.cgi?id=28244 + + // assert_class_string(this.get_interface_object().prototype, this.get_qualified_name() + "Prototype", + // "class string of " + this.name + ".prototype"); + + // String() should end up calling {}.toString if nothing defines a + // stringifier. + if (!this.has_stringifier()) { + // assert_equals(String(this.get_interface_object().prototype), "[object " + this.get_qualified_name() + "Prototype]", + // "String(" + this.name + ".prototype)"); + } + }.bind(this), this.name + " interface: existence and properties of interface prototype object"); + + // "If the interface is declared with the [Global] + // extended attribute, or the interface is in the set of inherited + // interfaces for any other interface that is declared with one of these + // attributes, then the interface prototype object must be an immutable + // prototype exotic object." + // https://webidl.spec.whatwg.org/#interface-prototype-object + if (this.is_global()) { + this.test_immutable_prototype("interface prototype object", this.get_interface_object().prototype); + } + + subsetTestByKey(this.name, test, function() + { + if (!this.should_have_interface_object()) { + return; + } + + this.assert_interface_object_exists(); + + if (this.is_callback()) { + assert_false("prototype" in this.get_interface_object(), + this.name + ' should not have a "prototype" property'); + return; + } + + assert_own_property(this.get_interface_object(), "prototype", + 'interface "' + this.name + '" does not have own property "prototype"'); + + // "If the [LegacyNoInterfaceObject] extended attribute was not specified + // on the interface, then the interface prototype object must also have a + // property named “constructor” with attributes { [[Writable]]: true, + // [[Enumerable]]: false, [[Configurable]]: true } whose value is a + // reference to the interface object for the interface." + assert_own_property(this.get_interface_object().prototype, "constructor", + this.name + '.prototype does not have own property "constructor"'); + var desc = Object.getOwnPropertyDescriptor(this.get_interface_object().prototype, "constructor"); + assert_false("get" in desc, this.name + ".prototype.constructor should not have a getter"); + assert_false("set" in desc, this.name + ".prototype.constructor should not have a setter"); + assert_true(desc.writable, this.name + ".prototype.constructor should be writable"); + assert_false(desc.enumerable, this.name + ".prototype.constructor should not be enumerable"); + assert_true(desc.configurable, this.name + ".prototype.constructor should be configurable"); + assert_equals(this.get_interface_object().prototype.constructor, this.get_interface_object(), + this.name + '.prototype.constructor is not the same object as ' + this.name); + }.bind(this), this.name + ' interface: existence and properties of interface prototype object\'s "constructor" property'); + + + subsetTestByKey(this.name, test, function() + { + if (!this.should_have_interface_object()) { + return; + } + + this.assert_interface_object_exists(); + + if (this.is_callback()) { + assert_false("prototype" in this.get_interface_object(), + this.name + ' should not have a "prototype" property'); + return; + } + + assert_own_property(this.get_interface_object(), "prototype", + 'interface "' + this.name + '" does not have own property "prototype"'); + + // If the interface has any member declared with the [Unscopable] extended + // attribute, then there must be a property on the interface prototype object + // whose name is the @@unscopables symbol, which has the attributes + // { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }, + // and whose value is an object created as follows... + var unscopables = this.get_unscopables().map(m => m.name); + var proto = this.get_interface_object().prototype; + if (unscopables.length != 0) { + assert_own_property( + proto, Symbol.unscopables, + this.name + '.prototype should have an @@unscopables property'); + var desc = Object.getOwnPropertyDescriptor(proto, Symbol.unscopables); + assert_false("get" in desc, + this.name + ".prototype[Symbol.unscopables] should not have a getter"); + assert_false("set" in desc, this.name + ".prototype[Symbol.unscopables] should not have a setter"); + assert_false(desc.writable, this.name + ".prototype[Symbol.unscopables] should not be writable"); + assert_false(desc.enumerable, this.name + ".prototype[Symbol.unscopables] should not be enumerable"); + assert_true(desc.configurable, this.name + ".prototype[Symbol.unscopables] should be configurable"); + assert_equals(desc.value, proto[Symbol.unscopables], + this.name + '.prototype[Symbol.unscopables] should be in the descriptor'); + assert_equals(typeof desc.value, "object", + this.name + '.prototype[Symbol.unscopables] should be an object'); + assert_equals(Object.getPrototypeOf(desc.value), null, + this.name + '.prototype[Symbol.unscopables] should have a null prototype'); + assert_equals(Object.getOwnPropertySymbols(desc.value).length, + 0, + this.name + '.prototype[Symbol.unscopables] should have the right number of symbol-named properties'); + + // Check that we do not have _extra_ unscopables. Checking that we + // have all the ones we should will happen in the per-member tests. + var observed = Object.getOwnPropertyNames(desc.value); + for (var prop of observed) { + assert_not_equals(unscopables.indexOf(prop), + -1, + this.name + '.prototype[Symbol.unscopables] has unexpected property "' + prop + '"'); + } + } else { + assert_equals(Object.getOwnPropertyDescriptor(this.get_interface_object().prototype, Symbol.unscopables), + undefined, + this.name + '.prototype should not have @@unscopables'); + } + }.bind(this), this.name + ' interface: existence and properties of interface prototype object\'s @@unscopables property'); +}; + +IdlInterface.prototype.test_immutable_prototype = function(type, obj) +{ + if (typeof Object.setPrototypeOf !== "function") { + return; + } + + subsetTestByKey(this.name, test, function(t) { + var originalValue = Object.getPrototypeOf(obj); + var newValue = Object.create(null); + + t.add_cleanup(function() { + try { + Object.setPrototypeOf(obj, originalValue); + } catch (err) {} + }); + + assert_throws_js(TypeError, function() { + Object.setPrototypeOf(obj, newValue); + }); + + assert_equals( + Object.getPrototypeOf(obj), + originalValue, + "original value not modified" + ); + }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " + + "of " + type + " - setting to a new value via Object.setPrototypeOf " + + "should throw a TypeError"); + + subsetTestByKey(this.name, test, function(t) { + var originalValue = Object.getPrototypeOf(obj); + var newValue = Object.create(null); + + t.add_cleanup(function() { + let setter = Object.getOwnPropertyDescriptor( + Object.prototype, '__proto__' + ).set; + + try { + setter.call(obj, originalValue); + } catch (err) {} + }); + + // We need to find the actual setter for the '__proto__' property, so we + // can determine the right global for it. Walk up the prototype chain + // looking for that property until we find it. + let setter; + { + let cur = obj; + while (cur) { + const desc = Object.getOwnPropertyDescriptor(cur, "__proto__"); + if (desc) { + setter = desc.set; + break; + } + cur = Object.getPrototypeOf(cur); + } + } + assert_throws_js(globalOf(setter).TypeError, function() { + obj.__proto__ = newValue; + }); + + assert_equals( + Object.getPrototypeOf(obj), + originalValue, + "original value not modified" + ); + }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " + + "of " + type + " - setting to a new value via __proto__ " + + "should throw a TypeError"); + + subsetTestByKey(this.name, test, function(t) { + var originalValue = Object.getPrototypeOf(obj); + var newValue = Object.create(null); + + t.add_cleanup(function() { + try { + Reflect.setPrototypeOf(obj, originalValue); + } catch (err) {} + }); + + assert_false(Reflect.setPrototypeOf(obj, newValue)); + + assert_equals( + Object.getPrototypeOf(obj), + originalValue, + "original value not modified" + ); + }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " + + "of " + type + " - setting to a new value via Reflect.setPrototypeOf " + + "should return false"); + + subsetTestByKey(this.name, test, function() { + var originalValue = Object.getPrototypeOf(obj); + + Object.setPrototypeOf(obj, originalValue); + }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " + + "of " + type + " - setting to its original value via Object.setPrototypeOf " + + "should not throw"); + + subsetTestByKey(this.name, test, function() { + var originalValue = Object.getPrototypeOf(obj); + + obj.__proto__ = originalValue; + }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " + + "of " + type + " - setting to its original value via __proto__ " + + "should not throw"); + + subsetTestByKey(this.name, test, function() { + var originalValue = Object.getPrototypeOf(obj); + + assert_true(Reflect.setPrototypeOf(obj, originalValue)); + }.bind(this), this.name + " interface: internal [[SetPrototypeOf]] method " + + "of " + type + " - setting to its original value via Reflect.setPrototypeOf " + + "should return true"); +}; + +IdlInterface.prototype.test_member_const = function(member) +{ + if (!this.has_constants()) { + throw new IdlHarnessError("Internal error: test_member_const called without any constants"); + } + + subsetTestByKey(this.name, test, function() + { + this.assert_interface_object_exists(); + + // "For each constant defined on an interface A, there must be + // a corresponding property on the interface object, if it + // exists." + assert_own_property(this.get_interface_object(), member.name); + // "The value of the property is that which is obtained by + // converting the constant’s IDL value to an ECMAScript + // value." + assert_equals(this.get_interface_object()[member.name], constValue(member.value), + "property has wrong value"); + // "The property has attributes { [[Writable]]: false, + // [[Enumerable]]: true, [[Configurable]]: false }." + var desc = Object.getOwnPropertyDescriptor(this.get_interface_object(), member.name); + assert_false("get" in desc, "property should not have a getter"); + assert_false("set" in desc, "property should not have a setter"); + assert_false(desc.writable, "property should not be writable"); + assert_true(desc.enumerable, "property should be enumerable"); + assert_false(desc.configurable, "property should not be configurable"); + }.bind(this), this.name + " interface: constant " + member.name + " on interface object"); + + // "In addition, a property with the same characteristics must + // exist on the interface prototype object." + subsetTestByKey(this.name, test, function() + { + this.assert_interface_object_exists(); + + if (this.is_callback()) { + assert_false("prototype" in this.get_interface_object(), + this.name + ' should not have a "prototype" property'); + return; + } + + assert_own_property(this.get_interface_object(), "prototype", + 'interface "' + this.name + '" does not have own property "prototype"'); + + assert_own_property(this.get_interface_object().prototype, member.name); + assert_equals(this.get_interface_object().prototype[member.name], constValue(member.value), + "property has wrong value"); + var desc = Object.getOwnPropertyDescriptor(this.get_interface_object(), member.name); + assert_false("get" in desc, "property should not have a getter"); + assert_false("set" in desc, "property should not have a setter"); + assert_false(desc.writable, "property should not be writable"); + assert_true(desc.enumerable, "property should be enumerable"); + assert_false(desc.configurable, "property should not be configurable"); + }.bind(this), this.name + " interface: constant " + member.name + " on interface prototype object"); +}; + + +IdlInterface.prototype.test_member_attribute = function(member) + { + if (!shouldRunSubTest(this.name)) { + return; + } + var a_test = subsetTestByKey(this.name, async_test, this.name + " interface: attribute " + member.name); + a_test.step(function() + { + if (!this.should_have_interface_object()) { + a_test.done(); + return; + } + + this.assert_interface_object_exists(); + assert_own_property(this.get_interface_object(), "prototype", + 'interface "' + this.name + '" does not have own property "prototype"'); + + if (member.special === "static") { + assert_own_property(this.get_interface_object(), member.name, + "The interface object must have a property " + + format_value(member.name)); + a_test.done(); + return; + } + + this.do_member_unscopable_asserts(member); + + if (this.is_global()) { + assert_own_property(self, member.name, + "The global object must have a property " + + format_value(member.name)); + assert_false(member.name in this.get_interface_object().prototype, + "The prototype object should not have a property " + + format_value(member.name)); + + var getter = Object.getOwnPropertyDescriptor(self, member.name).get; + assert_equals(typeof(getter), "function", + format_value(member.name) + " must have a getter"); + + // Try/catch around the get here, since it can legitimately throw. + // If it does, we obviously can't check for equality with direct + // invocation of the getter. + var gotValue; + var propVal; + try { + propVal = self[member.name]; + gotValue = true; + } catch (e) { + gotValue = false; + } + if (gotValue) { + assert_equals(propVal, getter.call(undefined), + "Gets on a global should not require an explicit this"); + } + + // do_interface_attribute_asserts must be the last thing we do, + // since it will call done() on a_test. + this.do_interface_attribute_asserts(self, member, a_test); + } else { + assert_true(member.name in this.get_interface_object().prototype, + "The prototype object must have a property " + + format_value(member.name)); + + if (!member.has_extended_attribute("LegacyLenientThis")) { + if (member.idlType.generic !== "Promise") { + // this.get_interface_object() returns a thing in our global + assert_throws_js(TypeError, function() { + this.get_interface_object().prototype[member.name]; + }.bind(this), "getting property on prototype object must throw TypeError"); + // do_interface_attribute_asserts must be the last thing we + // do, since it will call done() on a_test. + this.do_interface_attribute_asserts(this.get_interface_object().prototype, member, a_test); + } else { + promise_rejects_js(a_test, TypeError, + this.get_interface_object().prototype[member.name]) + .then(a_test.step_func(function() { + // do_interface_attribute_asserts must be the last + // thing we do, since it will call done() on a_test. + this.do_interface_attribute_asserts(this.get_interface_object().prototype, + member, a_test); + }.bind(this))); + } + } else { + assert_equals(this.get_interface_object().prototype[member.name], undefined, + "getting property on prototype object must return undefined"); + // do_interface_attribute_asserts must be the last thing we do, + // since it will call done() on a_test. + this.do_interface_attribute_asserts(this.get_interface_object().prototype, member, a_test); + } + } + }.bind(this)); +}; + +IdlInterface.prototype.test_member_operation = function(member) +{ + if (!shouldRunSubTest(this.name)) { + return; + } + var a_test = subsetTestByKey(this.name, async_test, this.name + " interface: operation " + member); + a_test.step(function() + { + // This function tests WebIDL as of 2015-12-29. + // https://webidl.spec.whatwg.org/#es-operations + + if (!this.should_have_interface_object()) { + a_test.done(); + return; + } + + this.assert_interface_object_exists(); + + if (this.is_callback()) { + assert_false("prototype" in this.get_interface_object(), + this.name + ' should not have a "prototype" property'); + a_test.done(); + return; + } + + assert_own_property(this.get_interface_object(), "prototype", + 'interface "' + this.name + '" does not have own property "prototype"'); + + // "For each unique identifier of an exposed operation defined on the + // interface, there must exist a corresponding property, unless the + // effective overload set for that identifier and operation and with an + // argument count of 0 has no entries." + + // TODO: Consider [Exposed]. + + // "The location of the property is determined as follows:" + var memberHolderObject; + // "* If the operation is static, then the property exists on the + // interface object." + if (member.special === "static") { + assert_own_property(this.get_interface_object(), member.name, + "interface object missing static operation"); + memberHolderObject = this.get_interface_object(); + // "* Otherwise, [...] if the interface was declared with the [Global] + // extended attribute, then the property exists + // on every object that implements the interface." + } else if (this.is_global()) { + assert_own_property(self, member.name, + "global object missing non-static operation"); + memberHolderObject = self; + // "* Otherwise, the property exists solely on the interface’s + // interface prototype object." + } else { + assert_own_property(this.get_interface_object().prototype, member.name, + "interface prototype object missing non-static operation"); + memberHolderObject = this.get_interface_object().prototype; + } + this.do_member_unscopable_asserts(member); + this.do_member_operation_asserts(memberHolderObject, member, a_test); + }.bind(this)); +}; + +IdlInterface.prototype.do_member_unscopable_asserts = function(member) +{ + // Check that if the member is unscopable then it's in the + // @@unscopables object properly. + if (!member.isUnscopable) { + return; + } + + var unscopables = this.get_interface_object().prototype[Symbol.unscopables]; + var prop = member.name; + var propDesc = Object.getOwnPropertyDescriptor(unscopables, prop); + assert_equals(typeof propDesc, "object", + this.name + '.prototype[Symbol.unscopables].' + prop + ' must exist') + assert_false("get" in propDesc, + this.name + '.prototype[Symbol.unscopables].' + prop + ' must have no getter'); + assert_false("set" in propDesc, + this.name + '.prototype[Symbol.unscopables].' + prop + ' must have no setter'); + assert_true(propDesc.writable, + this.name + '.prototype[Symbol.unscopables].' + prop + ' must be writable'); + assert_true(propDesc.enumerable, + this.name + '.prototype[Symbol.unscopables].' + prop + ' must be enumerable'); + assert_true(propDesc.configurable, + this.name + '.prototype[Symbol.unscopables].' + prop + ' must be configurable'); + assert_equals(propDesc.value, true, + this.name + '.prototype[Symbol.unscopables].' + prop + ' must have the value `true`'); +}; + +IdlInterface.prototype.do_member_operation_asserts = function(memberHolderObject, member, a_test) +{ + var done = a_test.done.bind(a_test); + var operationUnforgeable = member.isUnforgeable; + var desc = Object.getOwnPropertyDescriptor(memberHolderObject, member.name); + // "The property has attributes { [[Writable]]: B, + // [[Enumerable]]: true, [[Configurable]]: B }, where B is false if the + // operation is unforgeable on the interface, and true otherwise". + assert_false("get" in desc, "property should not have a getter"); + assert_false("set" in desc, "property should not have a setter"); + assert_equals(desc.writable, !operationUnforgeable, + "property should be writable if and only if not unforgeable"); + assert_true(desc.enumerable, "property should be enumerable"); + assert_equals(desc.configurable, !operationUnforgeable, + "property should be configurable if and only if not unforgeable"); + // "The value of the property is a Function object whose + // behavior is as follows . . ." + assert_equals(typeof memberHolderObject[member.name], "function", + "property must be a function"); + + const ctors = this.members.filter(function(m) { + return m.type == "operation" && m.name == member.name; + }); + assert_equals( + memberHolderObject[member.name].length, + minOverloadLength(ctors), + "property has wrong .length"); + assert_equals( + memberHolderObject[member.name].name, + member.name, + "property has wrong .name"); + + // Make some suitable arguments + var args = member.arguments.map(function(arg) { + return create_suitable_object(arg.idlType); + }); + + // "Let O be a value determined as follows: + // ". . . + // "Otherwise, throw a TypeError." + // This should be hit if the operation is not static, there is + // no [ImplicitThis] attribute, and the this value is null. + // + // TODO: We currently ignore the [ImplicitThis] case. Except we manually + // check for globals, since otherwise we'll invoke window.close(). And we + // have to skip this test for anything that on the proto chain of "self", + // since that does in fact have implicit-this behavior. + if (member.special !== "static") { + var cb; + if (!this.is_global() && + memberHolderObject[member.name] != self[member.name]) + { + cb = awaitNCallbacks(2, done); + throwOrReject(a_test, member, memberHolderObject[member.name], null, args, + "calling operation with this = null didn't throw TypeError", cb); + } else { + cb = awaitNCallbacks(1, done); + } + + // ". . . If O is not null and is also not a platform object + // that implements interface I, throw a TypeError." + // + // TODO: Test a platform object that implements some other + // interface. (Have to be sure to get inheritance right.) + throwOrReject(a_test, member, memberHolderObject[member.name], {}, args, + "calling operation with this = {} didn't throw TypeError", cb); + } else { + done(); + } +} + +IdlInterface.prototype.test_to_json_operation = function(desc, memberHolderObject, member) { + var instanceName = memberHolderObject && memberHolderObject.constructor.name + || member.name + " object"; + if (member.has_extended_attribute("Default")) { + subsetTestByKey(this.name, test, function() { + var map = this.default_to_json_operation(); + var json = memberHolderObject.toJSON(); + map.forEach(function(type, k) { + assert_true(k in json, "property " + JSON.stringify(k) + " should be present in the output of " + this.name + ".prototype.toJSON()"); + var descriptor = Object.getOwnPropertyDescriptor(json, k); + assert_true(descriptor.writable, "property " + k + " should be writable"); + assert_true(descriptor.configurable, "property " + k + " should be configurable"); + assert_true(descriptor.enumerable, "property " + k + " should be enumerable"); + this.array.assert_type_is(json[k], type); + delete json[k]; + }, this); + }.bind(this), this.name + " interface: default toJSON operation on " + desc); + } else { + subsetTestByKey(this.name, test, function() { + assert_true(this.array.is_json_type(member.idlType), JSON.stringify(member.idlType) + " is not an appropriate return value for the toJSON operation of " + instanceName); + this.array.assert_type_is(memberHolderObject.toJSON(), member.idlType); + }.bind(this), this.name + " interface: toJSON operation on " + desc); + } +}; + +IdlInterface.prototype.test_member_iterable = function(member) +{ + subsetTestByKey(this.name, test, function() + { + var isPairIterator = member.idlType.length === 2; + var proto = this.get_interface_object().prototype; + var iteratorDesc = Object.getOwnPropertyDescriptor(proto, Symbol.iterator); + + assert_true(iteratorDesc.writable, "@@iterator property should be writable"); + assert_true(iteratorDesc.configurable, "@@iterator property should be configurable"); + assert_false(iteratorDesc.enumerable, "@@iterator property should not be enumerable"); + assert_equals(typeof iteratorDesc.value, "function", "@@iterator property should be a function"); + assert_equals(iteratorDesc.value.length, 0, "@@iterator function object length should be 0"); + assert_equals(iteratorDesc.value.name, isPairIterator ? "entries" : "values", "@@iterator function object should have the right name"); + + if (isPairIterator) { + assert_equals(proto["entries"], proto[Symbol.iterator], "entries method should be the same as @@iterator method"); + [ + ["entries", 0], + ["keys", 0], + ["values", 0], + ["forEach", 1] + ].forEach(([property, length]) => { + var desc = Object.getOwnPropertyDescriptor(proto, property); + assert_equals(typeof desc.value, "function", property + " property should be a function"); + assert_equals(desc.value.length, length, property + " function object should have the right length"); + assert_equals(desc.value.name, property, property + " function object should have the right name"); + }); + } else { + assert_equals(proto[Symbol.iterator], Array.prototype[Symbol.iterator], "@@iterator method should be the same as Array prototype's"); + ["entries", "keys", "values", "forEach", Symbol.iterator].forEach(property => { + var propertyName = property === Symbol.iterator ? "@@iterator" : property; + assert_equals(proto[property], Array.prototype[property], propertyName + " method should be the same as Array prototype's"); + }); + } + }.bind(this), this.name + " interface: iterable<" + member.idlType.map(function(t) { return t.idlType; }).join(", ") + ">"); +}; + +IdlInterface.prototype.test_member_maplike = function(member) { + subsetTestByKey(this.name, test, () => { + const proto = this.get_interface_object().prototype; + + const methods = [ + ["entries", 0], + ["keys", 0], + ["values", 0], + ["forEach", 1], + ["get", 1], + ["has", 1] + ]; + if (!member.readonly) { + methods.push( + ["set", 2], + ["delete", 1], + ["clear", 1] + ); + } + + for (const [name, length] of methods) { + const desc = Object.getOwnPropertyDescriptor(proto, name); + assert_equals(typeof desc.value, "function", `${name} should be a function`); + assert_equals(desc.enumerable, false, `${name} enumerable`); + assert_equals(desc.configurable, true, `${name} configurable`); + assert_equals(desc.writable, true, `${name} writable`); + assert_equals(desc.value.length, length, `${name} function object length should be ${length}`); + assert_equals(desc.value.name, name, `${name} function object should have the right name`); + } + + const iteratorDesc = Object.getOwnPropertyDescriptor(proto, Symbol.iterator); + assert_equals(iteratorDesc.value, proto.entries, `@@iterator should equal entries`); + assert_equals(iteratorDesc.enumerable, false, `@@iterator enumerable`); + assert_equals(iteratorDesc.configurable, true, `@@iterator configurable`); + assert_equals(iteratorDesc.writable, true, `@@iterator writable`); + + const sizeDesc = Object.getOwnPropertyDescriptor(proto, "size"); + assert_equals(typeof sizeDesc.get, "function", `size getter should be a function`); + assert_equals(sizeDesc.set, undefined, `size should not have a setter`); + assert_equals(sizeDesc.enumerable, false, `size enumerable`); + assert_equals(sizeDesc.configurable, true, `size configurable`); + assert_equals(sizeDesc.get.length, 0, `size getter length should have the right length`); + assert_equals(sizeDesc.get.name, "get size", `size getter have the right name`); + }, `${this.name} interface: maplike<${member.idlType.map(t => t.idlType).join(", ")}>`); +}; + +IdlInterface.prototype.test_member_setlike = function(member) { + subsetTestByKey(this.name, test, () => { + const proto = this.get_interface_object().prototype; + + const methods = [ + ["entries", 0], + ["keys", 0], + ["values", 0], + ["forEach", 1], + ["has", 1] + ]; + if (!member.readonly) { + methods.push( + ["add", 1], + ["delete", 1], + ["clear", 1] + ); + } + + for (const [name, length] of methods) { + const desc = Object.getOwnPropertyDescriptor(proto, name); + assert_equals(typeof desc.value, "function", `${name} should be a function`); + assert_equals(desc.enumerable, false, `${name} enumerable`); + assert_equals(desc.configurable, true, `${name} configurable`); + assert_equals(desc.writable, true, `${name} writable`); + assert_equals(desc.value.length, length, `${name} function object length should be ${length}`); + assert_equals(desc.value.name, name, `${name} function object should have the right name`); + } + + const iteratorDesc = Object.getOwnPropertyDescriptor(proto, Symbol.iterator); + assert_equals(iteratorDesc.value, proto.values, `@@iterator should equal values`); + assert_equals(iteratorDesc.enumerable, false, `@@iterator enumerable`); + assert_equals(iteratorDesc.configurable, true, `@@iterator configurable`); + assert_equals(iteratorDesc.writable, true, `@@iterator writable`); + + const sizeDesc = Object.getOwnPropertyDescriptor(proto, "size"); + assert_equals(typeof sizeDesc.get, "function", `size getter should be a function`); + assert_equals(sizeDesc.set, undefined, `size should not have a setter`); + assert_equals(sizeDesc.enumerable, false, `size enumerable`); + assert_equals(sizeDesc.configurable, true, `size configurable`); + assert_equals(sizeDesc.get.length, 0, `size getter length should have the right length`); + assert_equals(sizeDesc.get.name, "size", `size getter have the right name`); + }, `${this.name} interface: setlike<${member.idlType.map(t => t.idlType).join(", ")}>`); +}; + +IdlInterface.prototype.test_member_async_iterable = function(member) +{ + subsetTestByKey(this.name, test, function() + { + var isPairIterator = member.idlType.length === 2; + var proto = this.get_interface_object().prototype; + var iteratorDesc = Object.getOwnPropertyDescriptor(proto, Symbol.asyncIterator); + + assert_true(iteratorDesc.writable, "@@asyncIterator property should be writable"); + assert_true(iteratorDesc.configurable, "@@asyncIterator property should be configurable"); + assert_false(iteratorDesc.enumerable, "@@asyncIterator property should not be enumerable"); + assert_equals(typeof iteratorDesc.value, "function", "@@asyncIterator property should be a function"); + assert_equals(iteratorDesc.value.length, 0, "@@asyncIterator function object length should be 0"); + assert_equals(iteratorDesc.value.name, isPairIterator ? "entries" : "values", "@@asyncIterator function object should have the right name"); + + if (isPairIterator) { + assert_equals(proto["entries"], proto[Symbol.asyncIterator], "entries method should be the same as @@asyncIterator method"); + ["entries", "keys", "values"].forEach(property => { + var desc = Object.getOwnPropertyDescriptor(proto, property); + assert_equals(typeof desc.value, "function", property + " property should be a function"); + assert_equals(desc.value.length, 0, property + " function object length should be 0"); + assert_equals(desc.value.name, property, property + " function object should have the right name"); + }); + } else { + assert_equals(proto["values"], proto[Symbol.asyncIterator], "values method should be the same as @@asyncIterator method"); + assert_false("entries" in proto, "should not have an entries method"); + assert_false("keys" in proto, "should not have a keys method"); + } + }.bind(this), this.name + " interface: async iterable<" + member.idlType.map(function(t) { return t.idlType; }).join(", ") + ">"); +}; + +IdlInterface.prototype.test_member_stringifier = function(member) +{ + subsetTestByKey(this.name, test, function() + { + if (!this.should_have_interface_object()) { + return; + } + + this.assert_interface_object_exists(); + + if (this.is_callback()) { + assert_false("prototype" in this.get_interface_object(), + this.name + ' should not have a "prototype" property'); + return; + } + + assert_own_property(this.get_interface_object(), "prototype", + 'interface "' + this.name + '" does not have own property "prototype"'); + + // ". . . the property exists on the interface prototype object." + var interfacePrototypeObject = this.get_interface_object().prototype; + assert_own_property(interfacePrototypeObject, "toString", + "interface prototype object missing non-static operation"); + + var stringifierUnforgeable = member.isUnforgeable; + var desc = Object.getOwnPropertyDescriptor(interfacePrototypeObject, "toString"); + // "The property has attributes { [[Writable]]: B, + // [[Enumerable]]: true, [[Configurable]]: B }, where B is false if the + // stringifier is unforgeable on the interface, and true otherwise." + assert_false("get" in desc, "property should not have a getter"); + assert_false("set" in desc, "property should not have a setter"); + assert_equals(desc.writable, !stringifierUnforgeable, + "property should be writable if and only if not unforgeable"); + assert_true(desc.enumerable, "property should be enumerable"); + assert_equals(desc.configurable, !stringifierUnforgeable, + "property should be configurable if and only if not unforgeable"); + // "The value of the property is a Function object, which behaves as + // follows . . ." + assert_equals(typeof interfacePrototypeObject.toString, "function", + "property must be a function"); + // "The value of the Function object’s “length” property is the Number + // value 0." + assert_equals(interfacePrototypeObject.toString.length, 0, + "property has wrong .length"); + + // "Let O be the result of calling ToObject on the this value." + assert_throws_js(globalOf(interfacePrototypeObject.toString).TypeError, function() { + interfacePrototypeObject.toString.apply(null, []); + }, "calling stringifier with this = null didn't throw TypeError"); + + // "If O is not an object that implements the interface on which the + // stringifier was declared, then throw a TypeError." + // + // TODO: Test a platform object that implements some other + // interface. (Have to be sure to get inheritance right.) + assert_throws_js(globalOf(interfacePrototypeObject.toString).TypeError, function() { + interfacePrototypeObject.toString.apply({}, []); + }, "calling stringifier with this = {} didn't throw TypeError"); + }.bind(this), this.name + " interface: stringifier"); +}; + +IdlInterface.prototype.test_members = function() +{ + for (var i = 0; i < this.members.length; i++) + { + var member = this.members[i]; + if (member.untested) { + continue; + } + + if (!exposed_in(exposure_set(member, this.exposureSet))) { + subsetTestByKey(this.name, test, function() { + // It's not exposed, so we shouldn't find it anywhere. + assert_false(member.name in this.get_interface_object(), + "The interface object must not have a property " + + format_value(member.name)); + assert_false(member.name in this.get_interface_object().prototype, + "The prototype object must not have a property " + + format_value(member.name)); + }.bind(this), this.name + " interface: member " + member.name); + continue; + } + + switch (member.type) { + case "const": + this.test_member_const(member); + break; + + case "attribute": + // For unforgeable attributes, we do the checks in + // test_interface_of instead. + if (!member.isUnforgeable) + { + this.test_member_attribute(member); + } + if (member.special === "stringifier") { + this.test_member_stringifier(member); + } + break; + + case "operation": + // TODO: Need to correctly handle multiple operations with the same + // identifier. + // For unforgeable operations, we do the checks in + // test_interface_of instead. + if (member.name) { + if (!member.isUnforgeable) + { + this.test_member_operation(member); + } + } else if (member.special === "stringifier") { + this.test_member_stringifier(member); + } + break; + + case "iterable": + if (member.async) { + this.test_member_async_iterable(member); + } else { + this.test_member_iterable(member); + } + break; + case "maplike": + this.test_member_maplike(member); + break; + case "setlike": + this.test_member_setlike(member); + break; + default: + // TODO: check more member types. + break; + } + } +}; + +IdlInterface.prototype.test_object = function(desc) +{ + var obj, exception = null; + try + { + obj = eval(desc); + } + catch(e) + { + exception = e; + } + + var expected_typeof; + if (this.name == "HTMLAllCollection") + { + // Result of [[IsHTMLDDA]] slot + expected_typeof = "undefined"; + } + else + { + expected_typeof = "object"; + } + + this.test_primary_interface_of(desc, obj, exception, expected_typeof); + + var current_interface = this; + while (current_interface) + { + if (!(current_interface.name in this.array.members)) + { + throw new IdlHarnessError("Interface " + current_interface.name + " not found (inherited by " + this.name + ")"); + } + if (current_interface.prevent_multiple_testing && current_interface.already_tested) + { + return; + } + current_interface.test_interface_of(desc, obj, exception, expected_typeof); + current_interface = this.array.members[current_interface.base]; + } +}; + +IdlInterface.prototype.test_primary_interface_of = function(desc, obj, exception, expected_typeof) +{ + // Only the object itself, not its members, are tested here, so if the + // interface is untested, there is nothing to do. + if (this.untested) + { + return; + } + + // "The internal [[SetPrototypeOf]] method of every platform object that + // implements an interface with the [Global] extended + // attribute must execute the same algorithm as is defined for the + // [[SetPrototypeOf]] internal method of an immutable prototype exotic + // object." + // https://webidl.spec.whatwg.org/#platform-object-setprototypeof + if (this.is_global()) + { + this.test_immutable_prototype("global platform object", obj); + } + + + // We can't easily test that its prototype is correct if there's no + // interface object, or the object is from a different global environment + // (not instanceof Object). TODO: test in this case that its prototype at + // least looks correct, even if we can't test that it's actually correct. + if (this.should_have_interface_object() + && (typeof obj != expected_typeof || obj instanceof Object)) + { + subsetTestByKey(this.name, test, function() + { + assert_equals(exception, null, "Unexpected exception when evaluating object"); + assert_equals(typeof obj, expected_typeof, "wrong typeof object"); + this.assert_interface_object_exists(); + assert_own_property(this.get_interface_object(), "prototype", + 'interface "' + this.name + '" does not have own property "prototype"'); + + // "The value of the internal [[Prototype]] property of the + // platform object is the interface prototype object of the primary + // interface from the platform object’s associated global + // environment." + assert_equals(Object.getPrototypeOf(obj), + this.get_interface_object().prototype, + desc + "'s prototype is not " + this.name + ".prototype"); + }.bind(this), this.name + " must be primary interface of " + desc); + } + + // "The class string of a platform object that implements one or more + // interfaces must be the qualified name of the primary interface of the + // platform object." + subsetTestByKey(this.name, test, function() + { + assert_equals(exception, null, "Unexpected exception when evaluating object"); + assert_equals(typeof obj, expected_typeof, "wrong typeof object"); + assert_class_string(obj, this.get_qualified_name(), "class string of " + desc); + if (!this.has_stringifier()) + { + assert_equals(String(obj), "[object " + this.get_qualified_name() + "]", "String(" + desc + ")"); + } + }.bind(this), "Stringification of " + desc); +}; + +IdlInterface.prototype.test_interface_of = function(desc, obj, exception, expected_typeof) +{ + // TODO: Indexed and named properties, more checks on interface members + this.already_tested = true; + if (!shouldRunSubTest(this.name)) { + return; + } + + for (var i = 0; i < this.members.length; i++) + { + var member = this.members[i]; + if (member.untested) { + continue; + } + if (!exposed_in(exposure_set(member, this.exposureSet))) { + subsetTestByKey(this.name, test, function() { + assert_equals(exception, null, "Unexpected exception when evaluating object"); + assert_false(member.name in obj); + }.bind(this), this.name + " interface: " + desc + ' must not have property "' + member.name + '"'); + continue; + } + if (member.type == "attribute" && member.isUnforgeable) + { + var a_test = subsetTestByKey(this.name, async_test, this.name + " interface: " + desc + ' must have own property "' + member.name + '"'); + a_test.step(function() { + assert_equals(exception, null, "Unexpected exception when evaluating object"); + assert_equals(typeof obj, expected_typeof, "wrong typeof object"); + // Call do_interface_attribute_asserts last, since it will call a_test.done() + this.do_interface_attribute_asserts(obj, member, a_test); + }.bind(this)); + } + else if (member.type == "operation" && + member.name && + member.isUnforgeable) + { + var a_test = subsetTestByKey(this.name, async_test, this.name + " interface: " + desc + ' must have own property "' + member.name + '"'); + a_test.step(function() + { + assert_equals(exception, null, "Unexpected exception when evaluating object"); + assert_equals(typeof obj, expected_typeof, "wrong typeof object"); + assert_own_property(obj, member.name, + "Doesn't have the unforgeable operation property"); + this.do_member_operation_asserts(obj, member, a_test); + }.bind(this)); + } + else if ((member.type == "const" + || member.type == "attribute" + || member.type == "operation") + && member.name) + { + subsetTestByKey(this.name, test, function() + { + assert_equals(exception, null, "Unexpected exception when evaluating object"); + assert_equals(typeof obj, expected_typeof, "wrong typeof object"); + if (member.special !== "static") { + if (!this.is_global()) { + assert_inherits(obj, member.name); + } else { + assert_own_property(obj, member.name); + } + + if (member.type == "const") + { + assert_equals(obj[member.name], constValue(member.value)); + } + if (member.type == "attribute") + { + // Attributes are accessor properties, so they might + // legitimately throw an exception rather than returning + // anything. + var property, thrown = false; + try + { + property = obj[member.name]; + } + catch (e) + { + thrown = true; + } + if (!thrown) + { + if (this.name == "Document" && member.name == "all") + { + // Result of [[IsHTMLDDA]] slot + assert_equals(typeof property, "undefined"); + } + else + { + this.array.assert_type_is(property, member.idlType); + } + } + } + if (member.type == "operation") + { + assert_equals(typeof obj[member.name], "function"); + } + } + }.bind(this), this.name + " interface: " + desc + ' must inherit property "' + member + '" with the proper type'); + } + // TODO: This is wrong if there are multiple operations with the same + // identifier. + // TODO: Test passing arguments of the wrong type. + if (member.type == "operation" && member.name && member.arguments.length) + { + var description = + this.name + " interface: calling " + member + " on " + desc + + " with too few arguments must throw TypeError"; + var a_test = subsetTestByKey(this.name, async_test, description); + a_test.step(function() + { + assert_equals(exception, null, "Unexpected exception when evaluating object"); + assert_equals(typeof obj, expected_typeof, "wrong typeof object"); + var fn; + if (member.special !== "static") { + if (!this.is_global() && !member.isUnforgeable) { + assert_inherits(obj, member.name); + } else { + assert_own_property(obj, member.name); + } + fn = obj[member.name]; + } + else + { + assert_own_property(obj.constructor, member.name, "interface object must have static operation as own property"); + fn = obj.constructor[member.name]; + } + + var minLength = minOverloadLength(this.members.filter(function(m) { + return m.type == "operation" && m.name == member.name; + })); + var args = []; + var cb = awaitNCallbacks(minLength, a_test.done.bind(a_test)); + for (var i = 0; i < minLength; i++) { + throwOrReject(a_test, member, fn, obj, args, "Called with " + i + " arguments", cb); + + args.push(create_suitable_object(member.arguments[i].idlType)); + } + if (minLength === 0) { + cb(); + } + }.bind(this)); + } + + if (member.is_to_json_regular_operation()) { + this.test_to_json_operation(desc, obj, member); + } + } +}; + +IdlInterface.prototype.has_stringifier = function() +{ + if (this.name === "DOMException") { + // toString is inherited from Error, so don't assume we have the + // default stringifer + return true; + } + if (this.members.some(function(member) { return member.special === "stringifier"; })) { + return true; + } + if (this.base && + this.array.members[this.base].has_stringifier()) { + return true; + } + return false; +}; + +IdlInterface.prototype.do_interface_attribute_asserts = function(obj, member, a_test) +{ + // This function tests WebIDL as of 2015-01-27. + // TODO: Consider [Exposed]. + + // This is called by test_member_attribute() with the prototype as obj if + // it is not a global, and the global otherwise, and by test_interface_of() + // with the object as obj. + + var pendingPromises = []; + + // "The name of the property is the identifier of the attribute." + assert_own_property(obj, member.name); + + // "The property has attributes { [[Get]]: G, [[Set]]: S, [[Enumerable]]: + // true, [[Configurable]]: configurable }, where: + // "configurable is false if the attribute was declared with the + // [LegacyUnforgeable] extended attribute and true otherwise; + // "G is the attribute getter, defined below; and + // "S is the attribute setter, also defined below." + var desc = Object.getOwnPropertyDescriptor(obj, member.name); + assert_false("value" in desc, 'property descriptor should not have a "value" field'); + assert_false("writable" in desc, 'property descriptor should not have a "writable" field'); + assert_true(desc.enumerable, "property should be enumerable"); + if (member.isUnforgeable) + { + assert_false(desc.configurable, "[LegacyUnforgeable] property must not be configurable"); + } + else + { + assert_true(desc.configurable, "property must be configurable"); + } + + + // "The attribute getter is a Function object whose behavior when invoked + // is as follows:" + assert_equals(typeof desc.get, "function", "getter must be Function"); + + // "If the attribute is a regular attribute, then:" + if (member.special !== "static") { + // "If O is not a platform object that implements I, then: + // "If the attribute was specified with the [LegacyLenientThis] extended + // attribute, then return undefined. + // "Otherwise, throw a TypeError." + if (!member.has_extended_attribute("LegacyLenientThis")) { + if (member.idlType.generic !== "Promise") { + assert_throws_js(globalOf(desc.get).TypeError, function() { + desc.get.call({}); + }.bind(this), "calling getter on wrong object type must throw TypeError"); + } else { + pendingPromises.push( + promise_rejects_js(a_test, TypeError, desc.get.call({}), + "calling getter on wrong object type must reject the return promise with TypeError")); + } + } else { + assert_equals(desc.get.call({}), undefined, + "calling getter on wrong object type must return undefined"); + } + } + + // "The value of the Function object’s “length” property is the Number + // value 0." + assert_equals(desc.get.length, 0, "getter length must be 0"); + + // "Let name be the string "get " prepended to attribute’s identifier." + // "Perform ! SetFunctionName(F, name)." + assert_equals(desc.get.name, "get " + member.name, + "getter must have the name 'get " + member.name + "'"); + + + // TODO: Test calling setter on the interface prototype (should throw + // TypeError in most cases). + if (member.readonly + && !member.has_extended_attribute("LegacyLenientSetter") + && !member.has_extended_attribute("PutForwards") + && !member.has_extended_attribute("Replaceable")) + { + // "The attribute setter is undefined if the attribute is declared + // readonly and has neither a [PutForwards] nor a [Replaceable] + // extended attribute declared on it." + assert_equals(desc.set, undefined, "setter must be undefined for readonly attributes"); + } + else + { + // "Otherwise, it is a Function object whose behavior when + // invoked is as follows:" + assert_equals(typeof desc.set, "function", "setter must be function for PutForwards, Replaceable, or non-readonly attributes"); + + // "If the attribute is a regular attribute, then:" + if (member.special !== "static") { + // "If /validThis/ is false and the attribute was not specified + // with the [LegacyLenientThis] extended attribute, then throw a + // TypeError." + // "If the attribute is declared with a [Replaceable] extended + // attribute, then: ..." + // "If validThis is false, then return." + if (!member.has_extended_attribute("LegacyLenientThis")) { + assert_throws_js(globalOf(desc.set).TypeError, function() { + desc.set.call({}); + }.bind(this), "calling setter on wrong object type must throw TypeError"); + } else { + assert_equals(desc.set.call({}), undefined, + "calling setter on wrong object type must return undefined"); + } + } + + // "The value of the Function object’s “length” property is the Number + // value 1." + assert_equals(desc.set.length, 1, "setter length must be 1"); + + // "Let name be the string "set " prepended to id." + // "Perform ! SetFunctionName(F, name)." + assert_equals(desc.set.name, "set " + member.name, + "The attribute setter must have the name 'set " + member.name + "'"); + } + + Promise.all(pendingPromises).then(a_test.done.bind(a_test)); +} + +/// IdlInterfaceMember /// +function IdlInterfaceMember(obj) +{ + /** + * obj is an object produced by the WebIDLParser.js "ifMember" production. + * We just forward all properties to this object without modification, + * except for special extAttrs handling. + */ + for (var k in obj.toJSON()) + { + this[k] = obj[k]; + } + if (!("extAttrs" in this)) + { + this.extAttrs = []; + } + + this.isUnforgeable = this.has_extended_attribute("LegacyUnforgeable"); + this.isUnscopable = this.has_extended_attribute("Unscopable"); +} + +IdlInterfaceMember.prototype = Object.create(IdlObject.prototype); + +IdlInterfaceMember.prototype.toJSON = function() { + return this; +}; + +IdlInterfaceMember.prototype.is_to_json_regular_operation = function() { + return this.type == "operation" && this.special !== "static" && this.name == "toJSON"; +}; + +IdlInterfaceMember.prototype.toString = function() { + function formatType(type) { + var result; + if (type.generic) { + result = type.generic + "<" + type.idlType.map(formatType).join(", ") + ">"; + } else if (type.union) { + result = "(" + type.subtype.map(formatType).join(" or ") + ")"; + } else { + result = type.idlType; + } + if (type.nullable) { + result += "?" + } + return result; + } + + if (this.type === "operation") { + var args = this.arguments.map(function(m) { + return [ + m.optional ? "optional " : "", + formatType(m.idlType), + m.variadic ? "..." : "", + ].join(""); + }).join(", "); + return this.name + "(" + args + ")"; + } + + return this.name; +} + +/// Internal helper functions /// +function create_suitable_object(type) +{ + /** + * type is an object produced by the WebIDLParser.js "type" production. We + * return a JavaScript value that matches the type, if we can figure out + * how. + */ + if (type.nullable) + { + return null; + } + switch (type.idlType) + { + case "any": + case "boolean": + return true; + + case "byte": case "octet": case "short": case "unsigned short": + case "long": case "unsigned long": case "long long": + case "unsigned long long": case "float": case "double": + case "unrestricted float": case "unrestricted double": + return 7; + + case "DOMString": + case "ByteString": + case "USVString": + return "foo"; + + case "object": + return {a: "b"}; + + case "Node": + return document.createTextNode("abc"); + } + return null; +} + +/// IdlEnum /// +// Used for IdlArray.prototype.assert_type_is +function IdlEnum(obj) +{ + /** + * obj is an object produced by the WebIDLParser.js "dictionary" + * production. + */ + + /** Self-explanatory. */ + this.name = obj.name; + + /** An array of values produced by the "enum" production. */ + this.values = obj.values; + +} + +IdlEnum.prototype = Object.create(IdlObject.prototype); + +/// IdlCallback /// +// Used for IdlArray.prototype.assert_type_is +function IdlCallback(obj) +{ + /** + * obj is an object produced by the WebIDLParser.js "callback" + * production. + */ + + /** Self-explanatory. */ + this.name = obj.name; + + /** Arguments for the callback. */ + this.arguments = obj.arguments; +} + +IdlCallback.prototype = Object.create(IdlObject.prototype); + +/// IdlTypedef /// +// Used for IdlArray.prototype.assert_type_is +function IdlTypedef(obj) +{ + /** + * obj is an object produced by the WebIDLParser.js "typedef" + * production. + */ + + /** Self-explanatory. */ + this.name = obj.name; + + /** The idlType that we are supposed to be typedeffing to. */ + this.idlType = obj.idlType; + +} + +IdlTypedef.prototype = Object.create(IdlObject.prototype); + +/// IdlNamespace /// +function IdlNamespace(obj) +{ + this.name = obj.name; + this.extAttrs = obj.extAttrs; + this.untested = obj.untested; + /** A back-reference to our IdlArray. */ + this.array = obj.array; + + /** An array of IdlInterfaceMembers. */ + this.members = obj.members.map(m => new IdlInterfaceMember(m)); +} + +IdlNamespace.prototype = Object.create(IdlObject.prototype); + +IdlNamespace.prototype.do_member_operation_asserts = function (memberHolderObject, member, a_test) +{ + var desc = Object.getOwnPropertyDescriptor(memberHolderObject, member.name); + + assert_false("get" in desc, "property should not have a getter"); + assert_false("set" in desc, "property should not have a setter"); + assert_equals( + desc.writable, + !member.isUnforgeable, + "property should be writable if and only if not unforgeable"); + assert_true(desc.enumerable, "property should be enumerable"); + assert_equals( + desc.configurable, + !member.isUnforgeable, + "property should be configurable if and only if not unforgeable"); + + assert_equals( + typeof memberHolderObject[member.name], + "function", + "property must be a function"); + + assert_equals( + memberHolderObject[member.name].length, + minOverloadLength(this.members.filter(function(m) { + return m.type == "operation" && m.name == member.name; + })), + "operation has wrong .length"); + a_test.done(); +} + +IdlNamespace.prototype.test_member_operation = function(member) +{ + if (!shouldRunSubTest(this.name)) { + return; + } + var a_test = subsetTestByKey( + this.name, + async_test, + this.name + ' namespace: operation ' + member); + a_test.step(function() { + assert_own_property( + self[this.name], + member.name, + 'namespace object missing operation ' + format_value(member.name)); + + this.do_member_operation_asserts(self[this.name], member, a_test); + }.bind(this)); +}; + +IdlNamespace.prototype.test_member_attribute = function (member) +{ + if (!shouldRunSubTest(this.name)) { + return; + } + var a_test = subsetTestByKey( + this.name, + async_test, + this.name + ' namespace: attribute ' + member.name); + a_test.step(function() + { + assert_own_property( + self[this.name], + member.name, + this.name + ' does not have property ' + format_value(member.name)); + + var desc = Object.getOwnPropertyDescriptor(self[this.name], member.name); + assert_equals(desc.set, undefined, "setter must be undefined for namespace members"); + a_test.done(); + }.bind(this)); +}; + +IdlNamespace.prototype.test_self = function () +{ + /** + * TODO(lukebjerring): Assert: + * - "Note that unlike interfaces or dictionaries, namespaces do not create types." + */ + + subsetTestByKey(this.name, test, () => { + assert_true(this.extAttrs.every(o => o.name === "Exposed" || o.name === "SecureContext"), + "Only the [Exposed] and [SecureContext] extended attributes are applicable to namespaces"); + assert_true(this.has_extended_attribute("Exposed"), + "Namespaces must be annotated with the [Exposed] extended attribute"); + }, `${this.name} namespace: extended attributes`); + + const namespaceObject = self[this.name]; + + subsetTestByKey(this.name, test, () => { + const desc = Object.getOwnPropertyDescriptor(self, this.name); + assert_equals(desc.value, namespaceObject, `wrong value for ${this.name} namespace object`); + assert_true(desc.writable, "namespace object should be writable"); + assert_false(desc.enumerable, "namespace object should not be enumerable"); + assert_true(desc.configurable, "namespace object should be configurable"); + assert_false("get" in desc, "namespace object should not have a getter"); + assert_false("set" in desc, "namespace object should not have a setter"); + }, `${this.name} namespace: property descriptor`); + + subsetTestByKey(this.name, test, () => { + assert_true(Object.isExtensible(namespaceObject)); + }, `${this.name} namespace: [[Extensible]] is true`); + + subsetTestByKey(this.name, test, () => { + assert_true(namespaceObject instanceof Object); + + if (this.name === "console") { + // https://console.spec.whatwg.org/#console-namespace + const namespacePrototype = Object.getPrototypeOf(namespaceObject); + assert_equals(Reflect.ownKeys(namespacePrototype).length, 0); + assert_equals(Object.getPrototypeOf(namespacePrototype), Object.prototype); + } else { + assert_equals(Object.getPrototypeOf(namespaceObject), Object.prototype); + } + }, `${this.name} namespace: [[Prototype]] is Object.prototype`); + + subsetTestByKey(this.name, test, () => { + assert_equals(typeof namespaceObject, "object"); + }, `${this.name} namespace: typeof is "object"`); + + subsetTestByKey(this.name, test, () => { + assert_equals( + Object.getOwnPropertyDescriptor(namespaceObject, "length"), + undefined, + "length property must be undefined" + ); + }, `${this.name} namespace: has no length property`); + + subsetTestByKey(this.name, test, () => { + assert_equals( + Object.getOwnPropertyDescriptor(namespaceObject, "name"), + undefined, + "name property must be undefined" + ); + }, `${this.name} namespace: has no name property`); +}; + +IdlNamespace.prototype.test = function () +{ + if (!this.untested) { + this.test_self(); + } + + for (const v of Object.values(this.members)) { + switch (v.type) { + + case 'operation': + this.test_member_operation(v); + break; + + case 'attribute': + this.test_member_attribute(v); + break; + + default: + throw 'Invalid namespace member ' + v.name + ': ' + v.type + ' not supported'; + } + }; +}; + +}()); + +/** + * idl_test is a promise_test wrapper that handles the fetching of the IDL, + * avoiding repetitive boilerplate. + * + * @param {String[]} srcs Spec name(s) for source idl files (fetched from + * /interfaces/{name}.idl). + * @param {String[]} deps Spec name(s) for dependency idl files (fetched + * from /interfaces/{name}.idl). Order is important - dependencies from + * each source will only be included if they're already know to be a + * dependency (i.e. have already been seen). + * @param {Function} setup_func Function for extra setup of the idl_array, such + * as adding objects. Do not call idl_array.test() in the setup; it is + * called by this function (idl_test). + */ +function idl_test(srcs, deps, idl_setup_func) { + return promise_test(function (t) { + var idl_array = new IdlArray(); + var setup_error = null; + const validationIgnored = [ + "constructor-member", + "dict-arg-default", + "require-exposed" + ]; + return Promise.all( + srcs.concat(deps).map(fetch_spec)) + .then(function(results) { + const astArray = results.map(result => + WebIDL2.parse(result.idl, { sourceName: result.spec }) + ); + test(() => { + const validations = WebIDL2.validate(astArray) + .filter(v => !validationIgnored.includes(v.ruleName)); + if (validations.length) { + const message = validations.map(v => v.message).join("\n\n"); + throw new Error(message); + } + }, "idl_test validation"); + for (var i = 0; i < srcs.length; i++) { + idl_array.internal_add_idls(astArray[i]); + } + for (var i = srcs.length; i < srcs.length + deps.length; i++) { + idl_array.internal_add_dependency_idls(astArray[i]); + } + }) + .then(function() { + if (idl_setup_func) { + return idl_setup_func(idl_array, t); + } + }) + .catch(function(e) { setup_error = e || 'IDL setup failed.'; }) + .then(function () { + var error = setup_error; + try { + idl_array.test(); // Test what we can. + } catch (e) { + // If testing fails hard here, the original setup error + // is more likely to be the real cause. + error = error || e; + } + if (error) { + throw error; + } + }); + }, 'idl_test setup'); +} + +/** + * fetch_spec is a shorthand for a Promise that fetches the spec's content. + */ +function fetch_spec(spec) { + var url = '/interfaces/' + spec + '.idl'; + return fetch(url).then(function (r) { + if (!r.ok) { + throw new IdlHarnessError("Error fetching " + url + "."); + } + return r.text(); + }).then(idl => ({ spec, idl })); +} +// vim: set expandtab shiftwidth=4 tabstop=4 foldmarker=@{,@} foldmethod=marker: diff --git a/test/wpt/tests/resources/webidl2/lib/webidl2.js b/test/wpt/tests/resources/webidl2/lib/webidl2.js new file mode 100644 index 00000000000..7161def899c --- /dev/null +++ b/test/wpt/tests/resources/webidl2/lib/webidl2.js @@ -0,0 +1,3824 @@ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define([], factory); + else if(typeof exports === 'object') + exports["WebIDL2"] = factory(); + else + root["WebIDL2"] = factory(); +})(globalThis, () => { +return /******/ (() => { // webpackBootstrap +/******/ "use strict"; +/******/ var __webpack_modules__ = ([ +/* 0 */, +/* 1 */ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "parse": () => (/* binding */ parse) +/* harmony export */ }); +/* harmony import */ var _tokeniser_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2); +/* harmony import */ var _productions_enum_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(15); +/* harmony import */ var _productions_includes_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(16); +/* harmony import */ var _productions_extended_attributes_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(8); +/* harmony import */ var _productions_typedef_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(17); +/* harmony import */ var _productions_callback_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(18); +/* harmony import */ var _productions_interface_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(19); +/* harmony import */ var _productions_mixin_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(25); +/* harmony import */ var _productions_dictionary_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(26); +/* harmony import */ var _productions_namespace_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(28); +/* harmony import */ var _productions_callback_interface_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(29); +/* harmony import */ var _productions_helpers_js__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(4); +/* harmony import */ var _productions_token_js__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(10); + + + + + + + + + + + + + + +/** + * @param {Tokeniser} tokeniser + * @param {object} options + * @param {boolean} [options.concrete] + * @param {Function[]} [options.productions] + */ +function parseByTokens(tokeniser, options) { + const source = tokeniser.source; + + function error(str) { + tokeniser.error(str); + } + + function consume(...candidates) { + return tokeniser.consume(...candidates); + } + + function callback() { + const callback = consume("callback"); + if (!callback) return; + if (tokeniser.probe("interface")) { + return _productions_callback_interface_js__WEBPACK_IMPORTED_MODULE_10__.CallbackInterface.parse(tokeniser, callback); + } + return _productions_callback_js__WEBPACK_IMPORTED_MODULE_5__.CallbackFunction.parse(tokeniser, callback); + } + + function interface_(opts) { + const base = consume("interface"); + if (!base) return; + const ret = + _productions_mixin_js__WEBPACK_IMPORTED_MODULE_7__.Mixin.parse(tokeniser, base, opts) || + _productions_interface_js__WEBPACK_IMPORTED_MODULE_6__.Interface.parse(tokeniser, base, opts) || + error("Interface has no proper body"); + return ret; + } + + function partial() { + const partial = consume("partial"); + if (!partial) return; + return ( + _productions_dictionary_js__WEBPACK_IMPORTED_MODULE_8__.Dictionary.parse(tokeniser, { partial }) || + interface_({ partial }) || + _productions_namespace_js__WEBPACK_IMPORTED_MODULE_9__.Namespace.parse(tokeniser, { partial }) || + error("Partial doesn't apply to anything") + ); + } + + function definition() { + if (options.productions) { + for (const production of options.productions) { + const result = production(tokeniser); + if (result) { + return result; + } + } + } + + return ( + callback() || + interface_() || + partial() || + _productions_dictionary_js__WEBPACK_IMPORTED_MODULE_8__.Dictionary.parse(tokeniser) || + _productions_enum_js__WEBPACK_IMPORTED_MODULE_1__.Enum.parse(tokeniser) || + _productions_typedef_js__WEBPACK_IMPORTED_MODULE_4__.Typedef.parse(tokeniser) || + _productions_includes_js__WEBPACK_IMPORTED_MODULE_2__.Includes.parse(tokeniser) || + _productions_namespace_js__WEBPACK_IMPORTED_MODULE_9__.Namespace.parse(tokeniser) + ); + } + + function definitions() { + if (!source.length) return []; + const defs = []; + while (true) { + const ea = _productions_extended_attributes_js__WEBPACK_IMPORTED_MODULE_3__.ExtendedAttributes.parse(tokeniser); + const def = definition(); + if (!def) { + if (ea.length) error("Stray extended attributes"); + break; + } + (0,_productions_helpers_js__WEBPACK_IMPORTED_MODULE_11__.autoParenter)(def).extAttrs = ea; + defs.push(def); + } + const eof = _productions_token_js__WEBPACK_IMPORTED_MODULE_12__.Eof.parse(tokeniser); + if (options.concrete) { + defs.push(eof); + } + return defs; + } + const res = definitions(); + if (tokeniser.position < source.length) error("Unrecognised tokens"); + return res; +} + +/** + * @param {string} str + * @param {object} [options] + * @param {*} [options.sourceName] + * @param {boolean} [options.concrete] + * @param {Function[]} [options.productions] + * @return {import("./productions/base.js").Base[]} + */ +function parse(str, options = {}) { + const tokeniser = new _tokeniser_js__WEBPACK_IMPORTED_MODULE_0__.Tokeniser(str); + if (typeof options.sourceName !== "undefined") { + // @ts-ignore (See Tokeniser.source in supplement.d.ts) + tokeniser.source.name = options.sourceName; + } + return parseByTokens(tokeniser, options); +} + + +/***/ }), +/* 2 */ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "Tokeniser": () => (/* binding */ Tokeniser), +/* harmony export */ "WebIDLParseError": () => (/* binding */ WebIDLParseError), +/* harmony export */ "argumentNameKeywords": () => (/* binding */ argumentNameKeywords), +/* harmony export */ "stringTypes": () => (/* binding */ stringTypes), +/* harmony export */ "typeNameKeywords": () => (/* binding */ typeNameKeywords) +/* harmony export */ }); +/* harmony import */ var _error_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3); +/* harmony import */ var _productions_helpers_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); + + + +// These regular expressions use the sticky flag so they will only match at +// the current location (ie. the offset of lastIndex). +const tokenRe = { + // This expression uses a lookahead assertion to catch false matches + // against integers early. + decimal: + /-?(?=[0-9]*\.|[0-9]+[eE])(([0-9]+\.[0-9]*|[0-9]*\.[0-9]+)([Ee][-+]?[0-9]+)?|[0-9]+[Ee][-+]?[0-9]+)/y, + integer: /-?(0([Xx][0-9A-Fa-f]+|[0-7]*)|[1-9][0-9]*)/y, + identifier: /[_-]?[A-Za-z][0-9A-Z_a-z-]*/y, + string: /"[^"]*"/y, + whitespace: /[\t\n\r ]+/y, + comment: /\/\/.*|\/\*[\s\S]*?\*\//y, + other: /[^\t\n\r 0-9A-Za-z]/y, +}; + +const typeNameKeywords = [ + "ArrayBuffer", + "DataView", + "Int8Array", + "Int16Array", + "Int32Array", + "Uint8Array", + "Uint16Array", + "Uint32Array", + "Uint8ClampedArray", + "BigInt64Array", + "BigUint64Array", + "Float32Array", + "Float64Array", + "any", + "object", + "symbol", +]; + +const stringTypes = ["ByteString", "DOMString", "USVString"]; + +const argumentNameKeywords = [ + "async", + "attribute", + "callback", + "const", + "constructor", + "deleter", + "dictionary", + "enum", + "getter", + "includes", + "inherit", + "interface", + "iterable", + "maplike", + "namespace", + "partial", + "required", + "setlike", + "setter", + "static", + "stringifier", + "typedef", + "unrestricted", +]; + +const nonRegexTerminals = [ + "-Infinity", + "FrozenArray", + "Infinity", + "NaN", + "ObservableArray", + "Promise", + "bigint", + "boolean", + "byte", + "double", + "false", + "float", + "long", + "mixin", + "null", + "octet", + "optional", + "or", + "readonly", + "record", + "sequence", + "short", + "true", + "undefined", + "unsigned", + "void", +].concat(argumentNameKeywords, stringTypes, typeNameKeywords); + +const punctuations = [ + "(", + ")", + ",", + "...", + ":", + ";", + "<", + "=", + ">", + "?", + "*", + "[", + "]", + "{", + "}", +]; + +const reserved = [ + // "constructor" is now a keyword + "_constructor", + "toString", + "_toString", +]; + +/** + * @typedef {ArrayItemType>} Token + * @param {string} str + */ +function tokenise(str) { + const tokens = []; + let lastCharIndex = 0; + let trivia = ""; + let line = 1; + let index = 0; + while (lastCharIndex < str.length) { + const nextChar = str.charAt(lastCharIndex); + let result = -1; + + if (/[\t\n\r ]/.test(nextChar)) { + result = attemptTokenMatch("whitespace", { noFlushTrivia: true }); + } else if (nextChar === "/") { + result = attemptTokenMatch("comment", { noFlushTrivia: true }); + } + + if (result !== -1) { + const currentTrivia = tokens.pop().value; + line += (currentTrivia.match(/\n/g) || []).length; + trivia += currentTrivia; + index -= 1; + } else if (/[-0-9.A-Z_a-z]/.test(nextChar)) { + result = attemptTokenMatch("decimal"); + if (result === -1) { + result = attemptTokenMatch("integer"); + } + if (result === -1) { + result = attemptTokenMatch("identifier"); + const lastIndex = tokens.length - 1; + const token = tokens[lastIndex]; + if (result !== -1) { + if (reserved.includes(token.value)) { + const message = `${(0,_productions_helpers_js__WEBPACK_IMPORTED_MODULE_1__.unescape)( + token.value + )} is a reserved identifier and must not be used.`; + throw new WebIDLParseError( + (0,_error_js__WEBPACK_IMPORTED_MODULE_0__.syntaxError)(tokens, lastIndex, null, message) + ); + } else if (nonRegexTerminals.includes(token.value)) { + token.type = "inline"; + } + } + } + } else if (nextChar === '"') { + result = attemptTokenMatch("string"); + } + + for (const punctuation of punctuations) { + if (str.startsWith(punctuation, lastCharIndex)) { + tokens.push({ + type: "inline", + value: punctuation, + trivia, + line, + index, + }); + trivia = ""; + lastCharIndex += punctuation.length; + result = lastCharIndex; + break; + } + } + + // other as the last try + if (result === -1) { + result = attemptTokenMatch("other"); + } + if (result === -1) { + throw new Error("Token stream not progressing"); + } + lastCharIndex = result; + index += 1; + } + + // remaining trivia as eof + tokens.push({ + type: "eof", + value: "", + trivia, + line, + index, + }); + + return tokens; + + /** + * @param {keyof typeof tokenRe} type + * @param {object} options + * @param {boolean} [options.noFlushTrivia] + */ + function attemptTokenMatch(type, { noFlushTrivia } = {}) { + const re = tokenRe[type]; + re.lastIndex = lastCharIndex; + const result = re.exec(str); + if (result) { + tokens.push({ type, value: result[0], trivia, line, index }); + if (!noFlushTrivia) { + trivia = ""; + } + return re.lastIndex; + } + return -1; + } +} + +class Tokeniser { + /** + * @param {string} idl + */ + constructor(idl) { + this.source = tokenise(idl); + this.position = 0; + } + + /** + * @param {string} message + * @return {never} + */ + error(message) { + throw new WebIDLParseError( + (0,_error_js__WEBPACK_IMPORTED_MODULE_0__.syntaxError)(this.source, this.position, this.current, message) + ); + } + + /** + * @param {string} type + */ + probeKind(type) { + return ( + this.source.length > this.position && + this.source[this.position].type === type + ); + } + + /** + * @param {string} value + */ + probe(value) { + return ( + this.probeKind("inline") && this.source[this.position].value === value + ); + } + + /** + * @param {...string} candidates + */ + consumeKind(...candidates) { + for (const type of candidates) { + if (!this.probeKind(type)) continue; + const token = this.source[this.position]; + this.position++; + return token; + } + } + + /** + * @param {...string} candidates + */ + consume(...candidates) { + if (!this.probeKind("inline")) return; + const token = this.source[this.position]; + for (const value of candidates) { + if (token.value !== value) continue; + this.position++; + return token; + } + } + + /** + * @param {string} value + */ + consumeIdentifier(value) { + if (!this.probeKind("identifier")) { + return; + } + if (this.source[this.position].value !== value) { + return; + } + return this.consumeKind("identifier"); + } + + /** + * @param {number} position + */ + unconsume(position) { + this.position = position; + } +} + +class WebIDLParseError extends Error { + /** + * @param {object} options + * @param {string} options.message + * @param {string} options.bareMessage + * @param {string} options.context + * @param {number} options.line + * @param {*} options.sourceName + * @param {string} options.input + * @param {*[]} options.tokens + */ + constructor({ + message, + bareMessage, + context, + line, + sourceName, + input, + tokens, + }) { + super(message); + + this.name = "WebIDLParseError"; // not to be mangled + this.bareMessage = bareMessage; + this.context = context; + this.line = line; + this.sourceName = sourceName; + this.input = input; + this.tokens = tokens; + } +} + + +/***/ }), +/* 3 */ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "syntaxError": () => (/* binding */ syntaxError), +/* harmony export */ "validationError": () => (/* binding */ validationError) +/* harmony export */ }); +/** + * @param {string} text + */ +function lastLine(text) { + const splitted = text.split("\n"); + return splitted[splitted.length - 1]; +} + +function appendIfExist(base, target) { + let result = base; + if (target) { + result += ` ${target}`; + } + return result; +} + +function contextAsText(node) { + const hierarchy = [node]; + while (node && node.parent) { + const { parent } = node; + hierarchy.unshift(parent); + node = parent; + } + return hierarchy.map((n) => appendIfExist(n.type, n.name)).join(" -> "); +} + +/** + * @typedef {object} WebIDL2ErrorOptions + * @property {"error" | "warning"} [level] + * @property {Function} [autofix] + * @property {string} [ruleName] + * + * @typedef {ReturnType} WebIDLErrorData + * + * @param {string} message error message + * @param {*} position + * @param {*} current + * @param {*} message + * @param {"Syntax" | "Validation"} kind error type + * @param {WebIDL2ErrorOptions=} options + */ +function error( + source, + position, + current, + message, + kind, + { level = "error", autofix, ruleName } = {} +) { + /** + * @param {number} count + */ + function sliceTokens(count) { + return count > 0 + ? source.slice(position, position + count) + : source.slice(Math.max(position + count, 0), position); + } + + /** + * @param {import("./tokeniser.js").Token[]} inputs + * @param {object} [options] + * @param {boolean} [options.precedes] + * @returns + */ + function tokensToText(inputs, { precedes } = {}) { + const text = inputs.map((t) => t.trivia + t.value).join(""); + const nextToken = source[position]; + if (nextToken.type === "eof") { + return text; + } + if (precedes) { + return text + nextToken.trivia; + } + return text.slice(nextToken.trivia.length); + } + + const maxTokens = 5; // arbitrary but works well enough + const line = + source[position].type !== "eof" + ? source[position].line + : source.length > 1 + ? source[position - 1].line + : 1; + + const precedingLastLine = lastLine( + tokensToText(sliceTokens(-maxTokens), { precedes: true }) + ); + + const subsequentTokens = sliceTokens(maxTokens); + const subsequentText = tokensToText(subsequentTokens); + const subsequentFirstLine = subsequentText.split("\n")[0]; + + const spaced = " ".repeat(precedingLastLine.length) + "^"; + const sourceContext = precedingLastLine + subsequentFirstLine + "\n" + spaced; + + const contextType = kind === "Syntax" ? "since" : "inside"; + const inSourceName = source.name ? ` in ${source.name}` : ""; + const grammaticalContext = + current && current.name + ? `, ${contextType} \`${current.partial ? "partial " : ""}${contextAsText( + current + )}\`` + : ""; + const context = `${kind} error at line ${line}${inSourceName}${grammaticalContext}:\n${sourceContext}`; + return { + message: `${context} ${message}`, + bareMessage: message, + context, + line, + sourceName: source.name, + level, + ruleName, + autofix, + input: subsequentText, + tokens: subsequentTokens, + }; +} + +/** + * @param {string} message error message + */ +function syntaxError(source, position, current, message) { + return error(source, position, current, message, "Syntax"); +} + +/** + * @param {string} message error message + * @param {WebIDL2ErrorOptions} [options] + */ +function validationError( + token, + current, + ruleName, + message, + options = {} +) { + options.ruleName = ruleName; + return error( + current.source, + token.index, + current, + message, + "Validation", + options + ); +} + + +/***/ }), +/* 4 */ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "argument_list": () => (/* binding */ argument_list), +/* harmony export */ "autoParenter": () => (/* binding */ autoParenter), +/* harmony export */ "autofixAddExposedWindow": () => (/* binding */ autofixAddExposedWindow), +/* harmony export */ "const_data": () => (/* binding */ const_data), +/* harmony export */ "const_value": () => (/* binding */ const_value), +/* harmony export */ "findLastIndex": () => (/* binding */ findLastIndex), +/* harmony export */ "getFirstToken": () => (/* binding */ getFirstToken), +/* harmony export */ "getLastIndentation": () => (/* binding */ getLastIndentation), +/* harmony export */ "getMemberIndentation": () => (/* binding */ getMemberIndentation), +/* harmony export */ "list": () => (/* binding */ list), +/* harmony export */ "primitive_type": () => (/* binding */ primitive_type), +/* harmony export */ "return_type": () => (/* binding */ return_type), +/* harmony export */ "stringifier": () => (/* binding */ stringifier), +/* harmony export */ "type_with_extended_attributes": () => (/* binding */ type_with_extended_attributes), +/* harmony export */ "unescape": () => (/* binding */ unescape) +/* harmony export */ }); +/* harmony import */ var _type_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(5); +/* harmony import */ var _argument_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(11); +/* harmony import */ var _extended_attributes_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(8); +/* harmony import */ var _operation_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(13); +/* harmony import */ var _attribute_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(14); +/* harmony import */ var _tokeniser_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(2); + + + + + + + +/** + * @param {string} identifier + */ +function unescape(identifier) { + return identifier.startsWith("_") ? identifier.slice(1) : identifier; +} + +/** + * Parses comma-separated list + * @param {import("../tokeniser.js").Tokeniser} tokeniser + * @param {object} args + * @param {Function} args.parser parser function for each item + * @param {boolean} [args.allowDangler] whether to allow dangling comma + * @param {string} [args.listName] the name to be shown on error messages + */ +function list(tokeniser, { parser, allowDangler, listName = "list" }) { + const first = parser(tokeniser); + if (!first) { + return []; + } + first.tokens.separator = tokeniser.consume(","); + const items = [first]; + while (first.tokens.separator) { + const item = parser(tokeniser); + if (!item) { + if (!allowDangler) { + tokeniser.error(`Trailing comma in ${listName}`); + } + break; + } + item.tokens.separator = tokeniser.consume(","); + items.push(item); + if (!item.tokens.separator) break; + } + return items; +} + +/** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + */ +function const_value(tokeniser) { + return ( + tokeniser.consumeKind("decimal", "integer") || + tokeniser.consume("true", "false", "Infinity", "-Infinity", "NaN") + ); +} + +/** + * @param {object} token + * @param {string} token.type + * @param {string} token.value + */ +function const_data({ type, value }) { + switch (type) { + case "decimal": + case "integer": + return { type: "number", value }; + case "string": + return { type: "string", value: value.slice(1, -1) }; + } + + switch (value) { + case "true": + case "false": + return { type: "boolean", value: value === "true" }; + case "Infinity": + case "-Infinity": + return { type: "Infinity", negative: value.startsWith("-") }; + case "[": + return { type: "sequence", value: [] }; + case "{": + return { type: "dictionary" }; + default: + return { type: value }; + } +} + +/** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + */ +function primitive_type(tokeniser) { + function integer_type() { + const prefix = tokeniser.consume("unsigned"); + const base = tokeniser.consume("short", "long"); + if (base) { + const postfix = tokeniser.consume("long"); + return new _type_js__WEBPACK_IMPORTED_MODULE_0__.Type({ source, tokens: { prefix, base, postfix } }); + } + if (prefix) tokeniser.error("Failed to parse integer type"); + } + + function decimal_type() { + const prefix = tokeniser.consume("unrestricted"); + const base = tokeniser.consume("float", "double"); + if (base) { + return new _type_js__WEBPACK_IMPORTED_MODULE_0__.Type({ source, tokens: { prefix, base } }); + } + if (prefix) tokeniser.error("Failed to parse float type"); + } + + const { source } = tokeniser; + const num_type = integer_type() || decimal_type(); + if (num_type) return num_type; + const base = tokeniser.consume( + "bigint", + "boolean", + "byte", + "octet", + "undefined" + ); + if (base) { + return new _type_js__WEBPACK_IMPORTED_MODULE_0__.Type({ source, tokens: { base } }); + } +} + +/** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + */ +function argument_list(tokeniser) { + return list(tokeniser, { + parser: _argument_js__WEBPACK_IMPORTED_MODULE_1__.Argument.parse, + listName: "arguments list", + }); +} + +/** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + * @param {string=} typeName (TODO: See Type.type for more details) + */ +function type_with_extended_attributes(tokeniser, typeName) { + const extAttrs = _extended_attributes_js__WEBPACK_IMPORTED_MODULE_2__.ExtendedAttributes.parse(tokeniser); + const ret = _type_js__WEBPACK_IMPORTED_MODULE_0__.Type.parse(tokeniser, typeName); + if (ret) autoParenter(ret).extAttrs = extAttrs; + return ret; +} + +/** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + * @param {string=} typeName (TODO: See Type.type for more details) + */ +function return_type(tokeniser, typeName) { + const typ = _type_js__WEBPACK_IMPORTED_MODULE_0__.Type.parse(tokeniser, typeName || "return-type"); + if (typ) { + return typ; + } + const voidToken = tokeniser.consume("void"); + if (voidToken) { + const ret = new _type_js__WEBPACK_IMPORTED_MODULE_0__.Type({ + source: tokeniser.source, + tokens: { base: voidToken }, + }); + ret.type = "return-type"; + return ret; + } +} + +/** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + */ +function stringifier(tokeniser) { + const special = tokeniser.consume("stringifier"); + if (!special) return; + const member = + _attribute_js__WEBPACK_IMPORTED_MODULE_4__.Attribute.parse(tokeniser, { special }) || + _operation_js__WEBPACK_IMPORTED_MODULE_3__.Operation.parse(tokeniser, { special }) || + tokeniser.error("Unterminated stringifier"); + return member; +} + +/** + * @param {string} str + */ +function getLastIndentation(str) { + const lines = str.split("\n"); + // the first line visually binds to the preceding token + if (lines.length) { + const match = lines[lines.length - 1].match(/^\s+/); + if (match) { + return match[0]; + } + } + return ""; +} + +/** + * @param {string} parentTrivia + */ +function getMemberIndentation(parentTrivia) { + const indentation = getLastIndentation(parentTrivia); + const indentCh = indentation.includes("\t") ? "\t" : " "; + return indentation + indentCh; +} + +/** + * @param {import("./interface.js").Interface} def + */ +function autofixAddExposedWindow(def) { + return () => { + if (def.extAttrs.length) { + const tokeniser = new _tokeniser_js__WEBPACK_IMPORTED_MODULE_5__.Tokeniser("Exposed=Window,"); + const exposed = _extended_attributes_js__WEBPACK_IMPORTED_MODULE_2__.SimpleExtendedAttribute.parse(tokeniser); + exposed.tokens.separator = tokeniser.consume(","); + const existing = def.extAttrs[0]; + if (!/^\s/.test(existing.tokens.name.trivia)) { + existing.tokens.name.trivia = ` ${existing.tokens.name.trivia}`; + } + def.extAttrs.unshift(exposed); + } else { + autoParenter(def).extAttrs = _extended_attributes_js__WEBPACK_IMPORTED_MODULE_2__.ExtendedAttributes.parse( + new _tokeniser_js__WEBPACK_IMPORTED_MODULE_5__.Tokeniser("[Exposed=Window]") + ); + const trivia = def.tokens.base.trivia; + def.extAttrs.tokens.open.trivia = trivia; + def.tokens.base.trivia = `\n${getLastIndentation(trivia)}`; + } + }; +} + +/** + * Get the first syntax token for the given IDL object. + * @param {*} data + */ +function getFirstToken(data) { + if (data.extAttrs.length) { + return data.extAttrs.tokens.open; + } + if (data.type === "operation" && !data.special) { + return getFirstToken(data.idlType); + } + const tokens = Object.values(data.tokens).sort((x, y) => x.index - y.index); + return tokens[0]; +} + +/** + * @template T + * @param {T[]} array + * @param {(item: T) => boolean} predicate + */ +function findLastIndex(array, predicate) { + const index = array.slice().reverse().findIndex(predicate); + if (index === -1) { + return index; + } + return array.length - index - 1; +} + +/** + * Returns a proxy that auto-assign `parent` field. + * @template {Record} T + * @param {T} data + * @param {*} [parent] The object that will be assigned to `parent`. + * If absent, it will be `data` by default. + * @return {T} + */ +function autoParenter(data, parent) { + if (!parent) { + // Defaults to `data` unless specified otherwise. + parent = data; + } + if (!data) { + // This allows `autoParenter(undefined)` which again allows + // `autoParenter(parse())` where the function may return nothing. + return data; + } + const proxy = new Proxy(data, { + get(target, p) { + const value = target[p]; + if (Array.isArray(value) && p !== "source") { + // Wraps the array so that any added items will also automatically + // get their `parent` values. + return autoParenter(value, target); + } + return value; + }, + set(target, p, value) { + // @ts-ignore https://github.com/microsoft/TypeScript/issues/47357 + target[p] = value; + if (!value) { + return true; + } else if (Array.isArray(value)) { + // Assigning an array will add `parent` to its items. + for (const item of value) { + if (typeof item.parent !== "undefined") { + item.parent = parent; + } + } + } else if (typeof value.parent !== "undefined") { + value.parent = parent; + } + return true; + }, + }); + return proxy; +} + + +/***/ }), +/* 5 */ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "Type": () => (/* binding */ Type) +/* harmony export */ }); +/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6); +/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); +/* harmony import */ var _tokeniser_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(2); +/* harmony import */ var _error_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(3); +/* harmony import */ var _validators_helpers_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(7); +/* harmony import */ var _extended_attributes_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(8); + + + + + + + +/** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + * @param {string} typeName + */ +function generic_type(tokeniser, typeName) { + const base = tokeniser.consume( + "FrozenArray", + "ObservableArray", + "Promise", + "sequence", + "record" + ); + if (!base) { + return; + } + const ret = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.autoParenter)( + new Type({ source: tokeniser.source, tokens: { base } }) + ); + ret.tokens.open = + tokeniser.consume("<") || + tokeniser.error(`No opening bracket after ${base.value}`); + switch (base.value) { + case "Promise": { + if (tokeniser.probe("[")) + tokeniser.error("Promise type cannot have extended attribute"); + const subtype = + (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.return_type)(tokeniser, typeName) || + tokeniser.error("Missing Promise subtype"); + ret.subtype.push(subtype); + break; + } + case "sequence": + case "FrozenArray": + case "ObservableArray": { + const subtype = + (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.type_with_extended_attributes)(tokeniser, typeName) || + tokeniser.error(`Missing ${base.value} subtype`); + ret.subtype.push(subtype); + break; + } + case "record": { + if (tokeniser.probe("[")) + tokeniser.error("Record key cannot have extended attribute"); + const keyType = + tokeniser.consume(..._tokeniser_js__WEBPACK_IMPORTED_MODULE_2__.stringTypes) || + tokeniser.error(`Record key must be one of: ${_tokeniser_js__WEBPACK_IMPORTED_MODULE_2__.stringTypes.join(", ")}`); + const keyIdlType = new Type({ + source: tokeniser.source, + tokens: { base: keyType }, + }); + keyIdlType.tokens.separator = + tokeniser.consume(",") || + tokeniser.error("Missing comma after record key type"); + keyIdlType.type = typeName; + const valueType = + (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.type_with_extended_attributes)(tokeniser, typeName) || + tokeniser.error("Error parsing generic type record"); + ret.subtype.push(keyIdlType, valueType); + break; + } + } + if (!ret.idlType) tokeniser.error(`Error parsing generic type ${base.value}`); + ret.tokens.close = + tokeniser.consume(">") || + tokeniser.error(`Missing closing bracket after ${base.value}`); + return ret.this; +} + +/** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + */ +function type_suffix(tokeniser, obj) { + const nullable = tokeniser.consume("?"); + if (nullable) { + obj.tokens.nullable = nullable; + } + if (tokeniser.probe("?")) tokeniser.error("Can't nullable more than once"); +} + +/** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + * @param {string} typeName + */ +function single_type(tokeniser, typeName) { + let ret = generic_type(tokeniser, typeName) || (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.primitive_type)(tokeniser); + if (!ret) { + const base = + tokeniser.consumeKind("identifier") || + tokeniser.consume(..._tokeniser_js__WEBPACK_IMPORTED_MODULE_2__.stringTypes, ..._tokeniser_js__WEBPACK_IMPORTED_MODULE_2__.typeNameKeywords); + if (!base) { + return; + } + ret = new Type({ source: tokeniser.source, tokens: { base } }); + if (tokeniser.probe("<")) + tokeniser.error(`Unsupported generic type ${base.value}`); + } + if (ret.generic === "Promise" && tokeniser.probe("?")) { + tokeniser.error("Promise type cannot be nullable"); + } + ret.type = typeName || null; + type_suffix(tokeniser, ret); + if (ret.nullable && ret.idlType === "any") + tokeniser.error("Type `any` cannot be made nullable"); + return ret; +} + +/** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + * @param {string} type + */ +function union_type(tokeniser, type) { + const tokens = {}; + tokens.open = tokeniser.consume("("); + if (!tokens.open) return; + const ret = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.autoParenter)(new Type({ source: tokeniser.source, tokens })); + ret.type = type || null; + while (true) { + const typ = + (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.type_with_extended_attributes)(tokeniser) || + tokeniser.error("No type after open parenthesis or 'or' in union type"); + if (typ.idlType === "any") + tokeniser.error("Type `any` cannot be included in a union type"); + if (typ.generic === "Promise") + tokeniser.error("Type `Promise` cannot be included in a union type"); + ret.subtype.push(typ); + const or = tokeniser.consume("or"); + if (or) { + typ.tokens.separator = or; + } else break; + } + if (ret.idlType.length < 2) { + tokeniser.error( + "At least two types are expected in a union type but found less" + ); + } + tokens.close = + tokeniser.consume(")") || tokeniser.error("Unterminated union type"); + type_suffix(tokeniser, ret); + return ret.this; +} + +class Type extends _base_js__WEBPACK_IMPORTED_MODULE_0__.Base { + /** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + * @param {string} typeName + */ + static parse(tokeniser, typeName) { + return single_type(tokeniser, typeName) || union_type(tokeniser, typeName); + } + + constructor({ source, tokens }) { + super({ source, tokens }); + Object.defineProperty(this, "subtype", { value: [], writable: true }); + this.extAttrs = new _extended_attributes_js__WEBPACK_IMPORTED_MODULE_5__.ExtendedAttributes({ source, tokens: {} }); + } + + get generic() { + if (this.subtype.length && this.tokens.base) { + return this.tokens.base.value; + } + return ""; + } + get nullable() { + return Boolean(this.tokens.nullable); + } + get union() { + return Boolean(this.subtype.length) && !this.tokens.base; + } + get idlType() { + if (this.subtype.length) { + return this.subtype; + } + // Adding prefixes/postfixes for "unrestricted float", etc. + const name = [this.tokens.prefix, this.tokens.base, this.tokens.postfix] + .filter((t) => t) + .map((t) => t.value) + .join(" "); + return (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.unescape)(name); + } + + *validate(defs) { + yield* this.extAttrs.validate(defs); + + if (this.idlType === "void") { + const message = `\`void\` is now replaced by \`undefined\`. Refer to the \ +[relevant GitHub issue](https://github.com/whatwg/webidl/issues/60) \ +for more information.`; + yield (0,_error_js__WEBPACK_IMPORTED_MODULE_3__.validationError)(this.tokens.base, this, "replace-void", message, { + autofix: replaceVoid(this), + }); + } + + /* + * If a union is nullable, its subunions cannot include a dictionary + * If not, subunions may include dictionaries if each union is not nullable + */ + const typedef = !this.union && defs.unique.get(this.idlType); + const target = this.union + ? this + : typedef && typedef.type === "typedef" + ? typedef.idlType + : undefined; + if (target && this.nullable) { + // do not allow any dictionary + const { reference } = (0,_validators_helpers_js__WEBPACK_IMPORTED_MODULE_4__.idlTypeIncludesDictionary)(target, defs) || {}; + if (reference) { + const targetToken = (this.union ? reference : this).tokens.base; + const message = "Nullable union cannot include a dictionary type."; + yield (0,_error_js__WEBPACK_IMPORTED_MODULE_3__.validationError)( + targetToken, + this, + "no-nullable-union-dict", + message + ); + } + } else { + // allow some dictionary + for (const subtype of this.subtype) { + yield* subtype.validate(defs); + } + } + } + + /** @param {import("../writer.js").Writer} w */ + write(w) { + const type_body = () => { + if (this.union || this.generic) { + return w.ts.wrap([ + w.token(this.tokens.base, w.ts.generic), + w.token(this.tokens.open), + ...this.subtype.map((t) => t.write(w)), + w.token(this.tokens.close), + ]); + } + const firstToken = this.tokens.prefix || this.tokens.base; + const prefix = this.tokens.prefix + ? [this.tokens.prefix.value, w.ts.trivia(this.tokens.base.trivia)] + : []; + const ref = w.reference( + w.ts.wrap([ + ...prefix, + this.tokens.base.value, + w.token(this.tokens.postfix), + ]), + { + unescaped: /** @type {string} (because it's not union) */ ( + this.idlType + ), + context: this, + } + ); + return w.ts.wrap([w.ts.trivia(firstToken.trivia), ref]); + }; + return w.ts.wrap([ + this.extAttrs.write(w), + type_body(), + w.token(this.tokens.nullable), + w.token(this.tokens.separator), + ]); + } +} + +/** + * @param {Type} type + */ +function replaceVoid(type) { + return () => { + type.tokens.base.value = "undefined"; + }; +} + + +/***/ }), +/* 6 */ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "Base": () => (/* binding */ Base) +/* harmony export */ }); +class Base { + /** + * @param {object} initializer + * @param {Base["source"]} initializer.source + * @param {Base["tokens"]} initializer.tokens + */ + constructor({ source, tokens }) { + Object.defineProperties(this, { + source: { value: source }, + tokens: { value: tokens, writable: true }, + parent: { value: null, writable: true }, + this: { value: this }, // useful when escaping from proxy + }); + } + + toJSON() { + const json = { type: undefined, name: undefined, inheritance: undefined }; + let proto = this; + while (proto !== Object.prototype) { + const descMap = Object.getOwnPropertyDescriptors(proto); + for (const [key, value] of Object.entries(descMap)) { + if (value.enumerable || value.get) { + // @ts-ignore - allow indexing here + json[key] = this[key]; + } + } + proto = Object.getPrototypeOf(proto); + } + return json; + } +} + + +/***/ }), +/* 7 */ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "dictionaryIncludesRequiredField": () => (/* binding */ dictionaryIncludesRequiredField), +/* harmony export */ "idlTypeIncludesDictionary": () => (/* binding */ idlTypeIncludesDictionary) +/* harmony export */ }); +/** + * @typedef {import("../productions/dictionary.js").Dictionary} Dictionary + * + * @param {*} idlType + * @param {import("../validator.js").Definitions} defs + * @param {object} [options] + * @param {boolean} [options.useNullableInner] use when the input idlType is nullable and you want to use its inner type + * @return {{ reference: *, dictionary: Dictionary }} the type reference that ultimately includes dictionary. + */ +function idlTypeIncludesDictionary( + idlType, + defs, + { useNullableInner } = {} +) { + if (!idlType.union) { + const def = defs.unique.get(idlType.idlType); + if (!def) { + return; + } + if (def.type === "typedef") { + const { typedefIncludesDictionary } = defs.cache; + if (typedefIncludesDictionary.has(def)) { + // Note that this also halts when it met indeterminate state + // to prevent infinite recursion + return typedefIncludesDictionary.get(def); + } + defs.cache.typedefIncludesDictionary.set(def, undefined); // indeterminate state + const result = idlTypeIncludesDictionary(def.idlType, defs); + defs.cache.typedefIncludesDictionary.set(def, result); + if (result) { + return { + reference: idlType, + dictionary: result.dictionary, + }; + } + } + if (def.type === "dictionary" && (useNullableInner || !idlType.nullable)) { + return { + reference: idlType, + dictionary: def, + }; + } + } + for (const subtype of idlType.subtype) { + const result = idlTypeIncludesDictionary(subtype, defs); + if (result) { + if (subtype.union) { + return result; + } + return { + reference: subtype, + dictionary: result.dictionary, + }; + } + } +} + +/** + * @param {*} dict dictionary type + * @param {import("../validator.js").Definitions} defs + * @return {boolean} + */ +function dictionaryIncludesRequiredField(dict, defs) { + if (defs.cache.dictionaryIncludesRequiredField.has(dict)) { + return defs.cache.dictionaryIncludesRequiredField.get(dict); + } + // Set cached result to indeterminate to short-circuit circular definitions. + // The final result will be updated to true or false. + defs.cache.dictionaryIncludesRequiredField.set(dict, undefined); + let result = dict.members.some((field) => field.required); + if (!result && dict.inheritance) { + const superdict = defs.unique.get(dict.inheritance); + if (!superdict) { + // Assume required members in the supertype if it is unknown. + result = true; + } else if (dictionaryIncludesRequiredField(superdict, defs)) { + result = true; + } + } + defs.cache.dictionaryIncludesRequiredField.set(dict, result); + return result; +} + + +/***/ }), +/* 8 */ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "ExtendedAttributeParameters": () => (/* binding */ ExtendedAttributeParameters), +/* harmony export */ "ExtendedAttributes": () => (/* binding */ ExtendedAttributes), +/* harmony export */ "SimpleExtendedAttribute": () => (/* binding */ SimpleExtendedAttribute) +/* harmony export */ }); +/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6); +/* harmony import */ var _array_base_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(9); +/* harmony import */ var _token_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(10); +/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4); +/* harmony import */ var _error_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(3); + + + + + + +/** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + * @param {string} tokenName + */ +function tokens(tokeniser, tokenName) { + return (0,_helpers_js__WEBPACK_IMPORTED_MODULE_3__.list)(tokeniser, { + parser: _token_js__WEBPACK_IMPORTED_MODULE_2__.WrappedToken.parser(tokeniser, tokenName), + listName: tokenName + " list", + }); +} + +const extAttrValueSyntax = ["identifier", "decimal", "integer", "string"]; + +const shouldBeLegacyPrefixed = [ + "NoInterfaceObject", + "LenientSetter", + "LenientThis", + "TreatNonObjectAsNull", + "Unforgeable", +]; + +const renamedLegacies = new Map([ + .../** @type {[string, string][]} */ ( + shouldBeLegacyPrefixed.map((name) => [name, `Legacy${name}`]) + ), + ["NamedConstructor", "LegacyFactoryFunction"], + ["OverrideBuiltins", "LegacyOverrideBuiltIns"], + ["TreatNullAs", "LegacyNullToEmptyString"], +]); + +/** + * This will allow a set of extended attribute values to be parsed. + * @param {import("../tokeniser.js").Tokeniser} tokeniser + */ +function extAttrListItems(tokeniser) { + for (const syntax of extAttrValueSyntax) { + const toks = tokens(tokeniser, syntax); + if (toks.length) { + return toks; + } + } + tokeniser.error( + `Expected identifiers, strings, decimals, or integers but none found` + ); +} + +class ExtendedAttributeParameters extends _base_js__WEBPACK_IMPORTED_MODULE_0__.Base { + /** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + */ + static parse(tokeniser) { + const tokens = { assign: tokeniser.consume("=") }; + const ret = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_3__.autoParenter)( + new ExtendedAttributeParameters({ source: tokeniser.source, tokens }) + ); + ret.list = []; + if (tokens.assign) { + tokens.asterisk = tokeniser.consume("*"); + if (tokens.asterisk) { + return ret.this; + } + tokens.secondaryName = tokeniser.consumeKind(...extAttrValueSyntax); + } + tokens.open = tokeniser.consume("("); + if (tokens.open) { + ret.list = ret.rhsIsList + ? // [Exposed=(Window,Worker)] + extAttrListItems(tokeniser) + : // [LegacyFactoryFunction=Audio(DOMString src)] or [Constructor(DOMString str)] + (0,_helpers_js__WEBPACK_IMPORTED_MODULE_3__.argument_list)(tokeniser); + tokens.close = + tokeniser.consume(")") || + tokeniser.error("Unexpected token in extended attribute argument list"); + } else if (tokens.assign && !tokens.secondaryName) { + tokeniser.error("No right hand side to extended attribute assignment"); + } + return ret.this; + } + + get rhsIsList() { + return ( + this.tokens.assign && !this.tokens.asterisk && !this.tokens.secondaryName + ); + } + + get rhsType() { + if (this.rhsIsList) { + return this.list[0].tokens.value.type + "-list"; + } + if (this.tokens.asterisk) { + return "*"; + } + if (this.tokens.secondaryName) { + return this.tokens.secondaryName.type; + } + return null; + } + + /** @param {import("../writer.js").Writer} w */ + write(w) { + const { rhsType } = this; + return w.ts.wrap([ + w.token(this.tokens.assign), + w.token(this.tokens.asterisk), + w.reference_token(this.tokens.secondaryName, this.parent), + w.token(this.tokens.open), + ...this.list.map((p) => { + return rhsType === "identifier-list" + ? w.identifier(p, this.parent) + : p.write(w); + }), + w.token(this.tokens.close), + ]); + } +} + +class SimpleExtendedAttribute extends _base_js__WEBPACK_IMPORTED_MODULE_0__.Base { + /** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + */ + static parse(tokeniser) { + const name = tokeniser.consumeKind("identifier"); + if (name) { + return new SimpleExtendedAttribute({ + source: tokeniser.source, + tokens: { name }, + params: ExtendedAttributeParameters.parse(tokeniser), + }); + } + } + + constructor({ source, tokens, params }) { + super({ source, tokens }); + params.parent = this; + Object.defineProperty(this, "params", { value: params }); + } + + get type() { + return "extended-attribute"; + } + get name() { + return this.tokens.name.value; + } + get rhs() { + const { rhsType: type, tokens, list } = this.params; + if (!type) { + return null; + } + const value = this.params.rhsIsList + ? list + : this.params.tokens.secondaryName + ? (0,_helpers_js__WEBPACK_IMPORTED_MODULE_3__.unescape)(tokens.secondaryName.value) + : null; + return { type, value }; + } + get arguments() { + const { rhsIsList, list } = this.params; + if (!list || rhsIsList) { + return []; + } + return list; + } + + *validate(defs) { + const { name } = this; + if (name === "LegacyNoInterfaceObject") { + const message = `\`[LegacyNoInterfaceObject]\` extended attribute is an \ +undesirable feature that may be removed from Web IDL in the future. Refer to the \ +[relevant upstream PR](https://github.com/whatwg/webidl/pull/609) for more \ +information.`; + yield (0,_error_js__WEBPACK_IMPORTED_MODULE_4__.validationError)( + this.tokens.name, + this, + "no-nointerfaceobject", + message, + { level: "warning" } + ); + } else if (renamedLegacies.has(name)) { + const message = `\`[${name}]\` extended attribute is a legacy feature \ +that is now renamed to \`[${renamedLegacies.get(name)}]\`. Refer to the \ +[relevant upstream PR](https://github.com/whatwg/webidl/pull/870) for more \ +information.`; + yield (0,_error_js__WEBPACK_IMPORTED_MODULE_4__.validationError)(this.tokens.name, this, "renamed-legacy", message, { + level: "warning", + autofix: renameLegacyExtendedAttribute(this), + }); + } + for (const arg of this.arguments) { + yield* arg.validate(defs); + } + } + + /** @param {import("../writer.js").Writer} w */ + write(w) { + return w.ts.wrap([ + w.ts.trivia(this.tokens.name.trivia), + w.ts.extendedAttribute( + w.ts.wrap([ + w.ts.extendedAttributeReference(this.name), + this.params.write(w), + ]) + ), + w.token(this.tokens.separator), + ]); + } +} + +/** + * @param {SimpleExtendedAttribute} extAttr + */ +function renameLegacyExtendedAttribute(extAttr) { + return () => { + const { name } = extAttr; + extAttr.tokens.name.value = renamedLegacies.get(name); + if (name === "TreatNullAs") { + extAttr.params.tokens = {}; + } + }; +} + +// Note: we parse something simpler than the official syntax. It's all that ever +// seems to be used +class ExtendedAttributes extends _array_base_js__WEBPACK_IMPORTED_MODULE_1__.ArrayBase { + /** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + */ + static parse(tokeniser) { + const tokens = {}; + tokens.open = tokeniser.consume("["); + const ret = new ExtendedAttributes({ source: tokeniser.source, tokens }); + if (!tokens.open) return ret; + ret.push( + ...(0,_helpers_js__WEBPACK_IMPORTED_MODULE_3__.list)(tokeniser, { + parser: SimpleExtendedAttribute.parse, + listName: "extended attribute", + }) + ); + tokens.close = + tokeniser.consume("]") || + tokeniser.error( + "Expected a closing token for the extended attribute list" + ); + if (!ret.length) { + tokeniser.unconsume(tokens.close.index); + tokeniser.error("An extended attribute list must not be empty"); + } + if (tokeniser.probe("[")) { + tokeniser.error( + "Illegal double extended attribute lists, consider merging them" + ); + } + return ret; + } + + *validate(defs) { + for (const extAttr of this) { + yield* extAttr.validate(defs); + } + } + + /** @param {import("../writer.js").Writer} w */ + write(w) { + if (!this.length) return ""; + return w.ts.wrap([ + w.token(this.tokens.open), + ...this.map((ea) => ea.write(w)), + w.token(this.tokens.close), + ]); + } +} + + +/***/ }), +/* 9 */ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "ArrayBase": () => (/* binding */ ArrayBase) +/* harmony export */ }); +class ArrayBase extends Array { + constructor({ source, tokens }) { + super(); + Object.defineProperties(this, { + source: { value: source }, + tokens: { value: tokens }, + parent: { value: null, writable: true }, + }); + } +} + + +/***/ }), +/* 10 */ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "Eof": () => (/* binding */ Eof), +/* harmony export */ "WrappedToken": () => (/* binding */ WrappedToken) +/* harmony export */ }); +/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6); +/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); + + + +class WrappedToken extends _base_js__WEBPACK_IMPORTED_MODULE_0__.Base { + /** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + * @param {string} type + */ + static parser(tokeniser, type) { + return () => { + const value = tokeniser.consumeKind(type); + if (value) { + return new WrappedToken({ + source: tokeniser.source, + tokens: { value }, + }); + } + }; + } + + get value() { + return (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.unescape)(this.tokens.value.value); + } + + /** @param {import("../writer.js").Writer} w */ + write(w) { + return w.ts.wrap([ + w.token(this.tokens.value), + w.token(this.tokens.separator), + ]); + } +} + +class Eof extends WrappedToken { + /** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + */ + static parse(tokeniser) { + const value = tokeniser.consumeKind("eof"); + if (value) { + return new Eof({ source: tokeniser.source, tokens: { value } }); + } + } + + get type() { + return "eof"; + } +} + + +/***/ }), +/* 11 */ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "Argument": () => (/* binding */ Argument) +/* harmony export */ }); +/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6); +/* harmony import */ var _default_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(12); +/* harmony import */ var _extended_attributes_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(8); +/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4); +/* harmony import */ var _tokeniser_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(2); +/* harmony import */ var _error_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(3); +/* harmony import */ var _validators_helpers_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(7); + + + + + + + + +class Argument extends _base_js__WEBPACK_IMPORTED_MODULE_0__.Base { + /** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + */ + static parse(tokeniser) { + const start_position = tokeniser.position; + /** @type {Base["tokens"]} */ + const tokens = {}; + const ret = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_3__.autoParenter)( + new Argument({ source: tokeniser.source, tokens }) + ); + ret.extAttrs = _extended_attributes_js__WEBPACK_IMPORTED_MODULE_2__.ExtendedAttributes.parse(tokeniser); + tokens.optional = tokeniser.consume("optional"); + ret.idlType = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_3__.type_with_extended_attributes)(tokeniser, "argument-type"); + if (!ret.idlType) { + return tokeniser.unconsume(start_position); + } + if (!tokens.optional) { + tokens.variadic = tokeniser.consume("..."); + } + tokens.name = + tokeniser.consumeKind("identifier") || + tokeniser.consume(..._tokeniser_js__WEBPACK_IMPORTED_MODULE_4__.argumentNameKeywords); + if (!tokens.name) { + return tokeniser.unconsume(start_position); + } + ret.default = tokens.optional ? _default_js__WEBPACK_IMPORTED_MODULE_1__.Default.parse(tokeniser) : null; + return ret.this; + } + + get type() { + return "argument"; + } + get optional() { + return !!this.tokens.optional; + } + get variadic() { + return !!this.tokens.variadic; + } + get name() { + return (0,_helpers_js__WEBPACK_IMPORTED_MODULE_3__.unescape)(this.tokens.name.value); + } + + /** + * @param {import("../validator.js").Definitions} defs + */ + *validate(defs) { + yield* this.extAttrs.validate(defs); + yield* this.idlType.validate(defs); + const result = (0,_validators_helpers_js__WEBPACK_IMPORTED_MODULE_6__.idlTypeIncludesDictionary)(this.idlType, defs, { + useNullableInner: true, + }); + if (result) { + if (this.idlType.nullable) { + const message = `Dictionary arguments cannot be nullable.`; + yield (0,_error_js__WEBPACK_IMPORTED_MODULE_5__.validationError)( + this.tokens.name, + this, + "no-nullable-dict-arg", + message + ); + } else if (!this.optional) { + if ( + this.parent && + !(0,_validators_helpers_js__WEBPACK_IMPORTED_MODULE_6__.dictionaryIncludesRequiredField)(result.dictionary, defs) && + isLastRequiredArgument(this) + ) { + const message = `Dictionary argument must be optional if it has no required fields`; + yield (0,_error_js__WEBPACK_IMPORTED_MODULE_5__.validationError)( + this.tokens.name, + this, + "dict-arg-optional", + message, + { + autofix: autofixDictionaryArgumentOptionality(this), + } + ); + } + } else if (!this.default) { + const message = `Optional dictionary arguments must have a default value of \`{}\`.`; + yield (0,_error_js__WEBPACK_IMPORTED_MODULE_5__.validationError)( + this.tokens.name, + this, + "dict-arg-default", + message, + { + autofix: autofixOptionalDictionaryDefaultValue(this), + } + ); + } + } + } + + /** @param {import("../writer.js").Writer} w */ + write(w) { + return w.ts.wrap([ + this.extAttrs.write(w), + w.token(this.tokens.optional), + w.ts.type(this.idlType.write(w)), + w.token(this.tokens.variadic), + w.name_token(this.tokens.name, { data: this }), + this.default ? this.default.write(w) : "", + w.token(this.tokens.separator), + ]); + } +} + +/** + * @param {Argument} arg + */ +function isLastRequiredArgument(arg) { + const list = arg.parent.arguments || arg.parent.list; + const index = list.indexOf(arg); + const requiredExists = list.slice(index + 1).some((a) => !a.optional); + return !requiredExists; +} + +/** + * @param {Argument} arg + */ +function autofixDictionaryArgumentOptionality(arg) { + return () => { + const firstToken = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_3__.getFirstToken)(arg.idlType); + arg.tokens.optional = { + ...firstToken, + type: "optional", + value: "optional", + }; + firstToken.trivia = " "; + autofixOptionalDictionaryDefaultValue(arg)(); + }; +} + +/** + * @param {Argument} arg + */ +function autofixOptionalDictionaryDefaultValue(arg) { + return () => { + arg.default = _default_js__WEBPACK_IMPORTED_MODULE_1__.Default.parse(new _tokeniser_js__WEBPACK_IMPORTED_MODULE_4__.Tokeniser(" = {}")); + }; +} + + +/***/ }), +/* 12 */ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "Default": () => (/* binding */ Default) +/* harmony export */ }); +/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6); +/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); + + + +class Default extends _base_js__WEBPACK_IMPORTED_MODULE_0__.Base { + /** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + */ + static parse(tokeniser) { + const assign = tokeniser.consume("="); + if (!assign) { + return null; + } + const def = + (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.const_value)(tokeniser) || + tokeniser.consumeKind("string") || + tokeniser.consume("null", "[", "{") || + tokeniser.error("No value for default"); + const expression = [def]; + if (def.value === "[") { + const close = + tokeniser.consume("]") || + tokeniser.error("Default sequence value must be empty"); + expression.push(close); + } else if (def.value === "{") { + const close = + tokeniser.consume("}") || + tokeniser.error("Default dictionary value must be empty"); + expression.push(close); + } + return new Default({ + source: tokeniser.source, + tokens: { assign }, + expression, + }); + } + + constructor({ source, tokens, expression }) { + super({ source, tokens }); + expression.parent = this; + Object.defineProperty(this, "expression", { value: expression }); + } + + get type() { + return (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.const_data)(this.expression[0]).type; + } + get value() { + return (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.const_data)(this.expression[0]).value; + } + get negative() { + return (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.const_data)(this.expression[0]).negative; + } + + /** @param {import("../writer.js").Writer} w */ + write(w) { + return w.ts.wrap([ + w.token(this.tokens.assign), + ...this.expression.map((t) => w.token(t)), + ]); + } +} + + +/***/ }), +/* 13 */ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "Operation": () => (/* binding */ Operation) +/* harmony export */ }); +/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6); +/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); +/* harmony import */ var _error_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3); + + + + +class Operation extends _base_js__WEBPACK_IMPORTED_MODULE_0__.Base { + /** + * @typedef {import("../tokeniser.js").Token} Token + * + * @param {import("../tokeniser.js").Tokeniser} tokeniser + * @param {object} [options] + * @param {Token} [options.special] + * @param {Token} [options.regular] + */ + static parse(tokeniser, { special, regular } = {}) { + const tokens = { special }; + const ret = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.autoParenter)( + new Operation({ source: tokeniser.source, tokens }) + ); + if (special && special.value === "stringifier") { + tokens.termination = tokeniser.consume(";"); + if (tokens.termination) { + ret.arguments = []; + return ret; + } + } + if (!special && !regular) { + tokens.special = tokeniser.consume("getter", "setter", "deleter"); + } + ret.idlType = + (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.return_type)(tokeniser) || tokeniser.error("Missing return type"); + tokens.name = + tokeniser.consumeKind("identifier") || tokeniser.consume("includes"); + tokens.open = + tokeniser.consume("(") || tokeniser.error("Invalid operation"); + ret.arguments = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.argument_list)(tokeniser); + tokens.close = + tokeniser.consume(")") || tokeniser.error("Unterminated operation"); + tokens.termination = + tokeniser.consume(";") || + tokeniser.error("Unterminated operation, expected `;`"); + return ret.this; + } + + get type() { + return "operation"; + } + get name() { + const { name } = this.tokens; + if (!name) { + return ""; + } + return (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.unescape)(name.value); + } + get special() { + if (!this.tokens.special) { + return ""; + } + return this.tokens.special.value; + } + + *validate(defs) { + yield* this.extAttrs.validate(defs); + if (!this.name && ["", "static"].includes(this.special)) { + const message = `Regular or static operations must have both a return type and an identifier.`; + yield (0,_error_js__WEBPACK_IMPORTED_MODULE_2__.validationError)(this.tokens.open, this, "incomplete-op", message); + } + if (this.idlType) { + yield* this.idlType.validate(defs); + } + for (const argument of this.arguments) { + yield* argument.validate(defs); + } + } + + /** @param {import("../writer.js").Writer} w */ + write(w) { + const { parent } = this; + const body = this.idlType + ? [ + w.ts.type(this.idlType.write(w)), + w.name_token(this.tokens.name, { data: this, parent }), + w.token(this.tokens.open), + w.ts.wrap(this.arguments.map((arg) => arg.write(w))), + w.token(this.tokens.close), + ] + : []; + return w.ts.definition( + w.ts.wrap([ + this.extAttrs.write(w), + this.tokens.name + ? w.token(this.tokens.special) + : w.token(this.tokens.special, w.ts.nameless, { data: this, parent }), + ...body, + w.token(this.tokens.termination), + ]), + { data: this, parent } + ); + } +} + + +/***/ }), +/* 14 */ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "Attribute": () => (/* binding */ Attribute) +/* harmony export */ }); +/* harmony import */ var _error_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3); +/* harmony import */ var _validators_helpers_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(7); +/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6); +/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(4); + + + + + +class Attribute extends _base_js__WEBPACK_IMPORTED_MODULE_2__.Base { + /** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + * @param {object} [options] + * @param {import("../tokeniser.js").Token} [options.special] + * @param {boolean} [options.noInherit] + * @param {boolean} [options.readonly] + */ + static parse( + tokeniser, + { special, noInherit = false, readonly = false } = {} + ) { + const start_position = tokeniser.position; + const tokens = { special }; + const ret = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_3__.autoParenter)( + new Attribute({ source: tokeniser.source, tokens }) + ); + if (!special && !noInherit) { + tokens.special = tokeniser.consume("inherit"); + } + if (ret.special === "inherit" && tokeniser.probe("readonly")) { + tokeniser.error("Inherited attributes cannot be read-only"); + } + tokens.readonly = tokeniser.consume("readonly"); + if (readonly && !tokens.readonly && tokeniser.probe("attribute")) { + tokeniser.error("Attributes must be readonly in this context"); + } + tokens.base = tokeniser.consume("attribute"); + if (!tokens.base) { + tokeniser.unconsume(start_position); + return; + } + ret.idlType = + (0,_helpers_js__WEBPACK_IMPORTED_MODULE_3__.type_with_extended_attributes)(tokeniser, "attribute-type") || + tokeniser.error("Attribute lacks a type"); + tokens.name = + tokeniser.consumeKind("identifier") || + tokeniser.consume("async", "required") || + tokeniser.error("Attribute lacks a name"); + tokens.termination = + tokeniser.consume(";") || + tokeniser.error("Unterminated attribute, expected `;`"); + return ret.this; + } + + get type() { + return "attribute"; + } + get special() { + if (!this.tokens.special) { + return ""; + } + return this.tokens.special.value; + } + get readonly() { + return !!this.tokens.readonly; + } + get name() { + return (0,_helpers_js__WEBPACK_IMPORTED_MODULE_3__.unescape)(this.tokens.name.value); + } + + *validate(defs) { + yield* this.extAttrs.validate(defs); + yield* this.idlType.validate(defs); + + switch (this.idlType.generic) { + case "sequence": + case "record": { + const message = `Attributes cannot accept ${this.idlType.generic} types.`; + yield (0,_error_js__WEBPACK_IMPORTED_MODULE_0__.validationError)( + this.tokens.name, + this, + "attr-invalid-type", + message + ); + break; + } + default: { + const { reference } = + (0,_validators_helpers_js__WEBPACK_IMPORTED_MODULE_1__.idlTypeIncludesDictionary)(this.idlType, defs) || {}; + if (reference) { + const targetToken = (this.idlType.union ? reference : this.idlType) + .tokens.base; + const message = "Attributes cannot accept dictionary types."; + yield (0,_error_js__WEBPACK_IMPORTED_MODULE_0__.validationError)( + targetToken, + this, + "attr-invalid-type", + message + ); + } + } + } + } + + /** @param {import("../writer.js").Writer} w */ + write(w) { + const { parent } = this; + return w.ts.definition( + w.ts.wrap([ + this.extAttrs.write(w), + w.token(this.tokens.special), + w.token(this.tokens.readonly), + w.token(this.tokens.base), + w.ts.type(this.idlType.write(w)), + w.name_token(this.tokens.name, { data: this, parent }), + w.token(this.tokens.termination), + ]), + { data: this, parent } + ); + } +} + + +/***/ }), +/* 15 */ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "Enum": () => (/* binding */ Enum), +/* harmony export */ "EnumValue": () => (/* binding */ EnumValue) +/* harmony export */ }); +/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4); +/* harmony import */ var _token_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(10); +/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6); + + + + +class EnumValue extends _token_js__WEBPACK_IMPORTED_MODULE_1__.WrappedToken { + /** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + */ + static parse(tokeniser) { + const value = tokeniser.consumeKind("string"); + if (value) { + return new EnumValue({ source: tokeniser.source, tokens: { value } }); + } + } + + get type() { + return "enum-value"; + } + get value() { + return super.value.slice(1, -1); + } + + /** @param {import("../writer.js").Writer} w */ + write(w) { + const { parent } = this; + return w.ts.wrap([ + w.ts.trivia(this.tokens.value.trivia), + w.ts.definition( + w.ts.wrap(['"', w.ts.name(this.value, { data: this, parent }), '"']), + { data: this, parent } + ), + w.token(this.tokens.separator), + ]); + } +} + +class Enum extends _base_js__WEBPACK_IMPORTED_MODULE_2__.Base { + /** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + */ + static parse(tokeniser) { + /** @type {Base["tokens"]} */ + const tokens = {}; + tokens.base = tokeniser.consume("enum"); + if (!tokens.base) { + return; + } + tokens.name = + tokeniser.consumeKind("identifier") || + tokeniser.error("No name for enum"); + const ret = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.autoParenter)(new Enum({ source: tokeniser.source, tokens })); + tokeniser.current = ret.this; + tokens.open = tokeniser.consume("{") || tokeniser.error("Bodyless enum"); + ret.values = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.list)(tokeniser, { + parser: EnumValue.parse, + allowDangler: true, + listName: "enumeration", + }); + if (tokeniser.probeKind("string")) { + tokeniser.error("No comma between enum values"); + } + tokens.close = + tokeniser.consume("}") || tokeniser.error("Unexpected value in enum"); + if (!ret.values.length) { + tokeniser.error("No value in enum"); + } + tokens.termination = + tokeniser.consume(";") || tokeniser.error("No semicolon after enum"); + return ret.this; + } + + get type() { + return "enum"; + } + get name() { + return (0,_helpers_js__WEBPACK_IMPORTED_MODULE_0__.unescape)(this.tokens.name.value); + } + + /** @param {import("../writer.js").Writer} w */ + write(w) { + return w.ts.definition( + w.ts.wrap([ + this.extAttrs.write(w), + w.token(this.tokens.base), + w.name_token(this.tokens.name, { data: this }), + w.token(this.tokens.open), + w.ts.wrap(this.values.map((v) => v.write(w))), + w.token(this.tokens.close), + w.token(this.tokens.termination), + ]), + { data: this } + ); + } +} + + +/***/ }), +/* 16 */ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "Includes": () => (/* binding */ Includes) +/* harmony export */ }); +/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6); +/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); + + + +class Includes extends _base_js__WEBPACK_IMPORTED_MODULE_0__.Base { + /** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + */ + static parse(tokeniser) { + const target = tokeniser.consumeKind("identifier"); + if (!target) { + return; + } + const tokens = { target }; + tokens.includes = tokeniser.consume("includes"); + if (!tokens.includes) { + tokeniser.unconsume(target.index); + return; + } + tokens.mixin = + tokeniser.consumeKind("identifier") || + tokeniser.error("Incomplete includes statement"); + tokens.termination = + tokeniser.consume(";") || + tokeniser.error("No terminating ; for includes statement"); + return new Includes({ source: tokeniser.source, tokens }); + } + + get type() { + return "includes"; + } + get target() { + return (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.unescape)(this.tokens.target.value); + } + get includes() { + return (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.unescape)(this.tokens.mixin.value); + } + + /** @param {import("../writer.js").Writer} w */ + write(w) { + return w.ts.definition( + w.ts.wrap([ + this.extAttrs.write(w), + w.reference_token(this.tokens.target, this), + w.token(this.tokens.includes), + w.reference_token(this.tokens.mixin, this), + w.token(this.tokens.termination), + ]), + { data: this } + ); + } +} + + +/***/ }), +/* 17 */ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "Typedef": () => (/* binding */ Typedef) +/* harmony export */ }); +/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6); +/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); + + + +class Typedef extends _base_js__WEBPACK_IMPORTED_MODULE_0__.Base { + /** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + */ + static parse(tokeniser) { + /** @type {Base["tokens"]} */ + const tokens = {}; + const ret = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.autoParenter)(new Typedef({ source: tokeniser.source, tokens })); + tokens.base = tokeniser.consume("typedef"); + if (!tokens.base) { + return; + } + ret.idlType = + (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.type_with_extended_attributes)(tokeniser, "typedef-type") || + tokeniser.error("Typedef lacks a type"); + tokens.name = + tokeniser.consumeKind("identifier") || + tokeniser.error("Typedef lacks a name"); + tokeniser.current = ret.this; + tokens.termination = + tokeniser.consume(";") || + tokeniser.error("Unterminated typedef, expected `;`"); + return ret.this; + } + + get type() { + return "typedef"; + } + get name() { + return (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.unescape)(this.tokens.name.value); + } + + *validate(defs) { + yield* this.idlType.validate(defs); + } + + /** @param {import("../writer.js").Writer} w */ + write(w) { + return w.ts.definition( + w.ts.wrap([ + this.extAttrs.write(w), + w.token(this.tokens.base), + w.ts.type(this.idlType.write(w)), + w.name_token(this.tokens.name, { data: this }), + w.token(this.tokens.termination), + ]), + { data: this } + ); + } +} + + +/***/ }), +/* 18 */ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "CallbackFunction": () => (/* binding */ CallbackFunction) +/* harmony export */ }); +/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6); +/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); + + + +class CallbackFunction extends _base_js__WEBPACK_IMPORTED_MODULE_0__.Base { + /** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + */ + static parse(tokeniser, base) { + const tokens = { base }; + const ret = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.autoParenter)( + new CallbackFunction({ source: tokeniser.source, tokens }) + ); + tokens.name = + tokeniser.consumeKind("identifier") || + tokeniser.error("Callback lacks a name"); + tokeniser.current = ret.this; + tokens.assign = + tokeniser.consume("=") || tokeniser.error("Callback lacks an assignment"); + ret.idlType = + (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.return_type)(tokeniser) || tokeniser.error("Callback lacks a return type"); + tokens.open = + tokeniser.consume("(") || + tokeniser.error("Callback lacks parentheses for arguments"); + ret.arguments = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.argument_list)(tokeniser); + tokens.close = + tokeniser.consume(")") || tokeniser.error("Unterminated callback"); + tokens.termination = + tokeniser.consume(";") || + tokeniser.error("Unterminated callback, expected `;`"); + return ret.this; + } + + get type() { + return "callback"; + } + get name() { + return (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.unescape)(this.tokens.name.value); + } + + *validate(defs) { + yield* this.extAttrs.validate(defs); + yield* this.idlType.validate(defs); + } + + /** @param {import("../writer.js").Writer} w */ + write(w) { + return w.ts.definition( + w.ts.wrap([ + this.extAttrs.write(w), + w.token(this.tokens.base), + w.name_token(this.tokens.name, { data: this }), + w.token(this.tokens.assign), + w.ts.type(this.idlType.write(w)), + w.token(this.tokens.open), + ...this.arguments.map((arg) => arg.write(w)), + w.token(this.tokens.close), + w.token(this.tokens.termination), + ]), + { data: this } + ); + } +} + + +/***/ }), +/* 19 */ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "Interface": () => (/* binding */ Interface) +/* harmony export */ }); +/* harmony import */ var _container_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(20); +/* harmony import */ var _attribute_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(14); +/* harmony import */ var _operation_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(13); +/* harmony import */ var _constant_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(21); +/* harmony import */ var _iterable_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(22); +/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(4); +/* harmony import */ var _error_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(3); +/* harmony import */ var _validators_interface_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(23); +/* harmony import */ var _constructor_js__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(24); +/* harmony import */ var _tokeniser_js__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(2); +/* harmony import */ var _extended_attributes_js__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(8); + + + + + + + + + + + + +/** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + */ +function static_member(tokeniser) { + const special = tokeniser.consume("static"); + if (!special) return; + const member = + _attribute_js__WEBPACK_IMPORTED_MODULE_1__.Attribute.parse(tokeniser, { special }) || + _operation_js__WEBPACK_IMPORTED_MODULE_2__.Operation.parse(tokeniser, { special }) || + tokeniser.error("No body in static member"); + return member; +} + +class Interface extends _container_js__WEBPACK_IMPORTED_MODULE_0__.Container { + /** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + */ + static parse(tokeniser, base, { partial = null } = {}) { + const tokens = { partial, base }; + return _container_js__WEBPACK_IMPORTED_MODULE_0__.Container.parse( + tokeniser, + new Interface({ source: tokeniser.source, tokens }), + { + inheritable: !partial, + allowedMembers: [ + [_constant_js__WEBPACK_IMPORTED_MODULE_3__.Constant.parse], + [_constructor_js__WEBPACK_IMPORTED_MODULE_8__.Constructor.parse], + [static_member], + [_helpers_js__WEBPACK_IMPORTED_MODULE_5__.stringifier], + [_iterable_js__WEBPACK_IMPORTED_MODULE_4__.IterableLike.parse], + [_attribute_js__WEBPACK_IMPORTED_MODULE_1__.Attribute.parse], + [_operation_js__WEBPACK_IMPORTED_MODULE_2__.Operation.parse], + ], + } + ); + } + + get type() { + return "interface"; + } + + *validate(defs) { + yield* this.extAttrs.validate(defs); + if ( + !this.partial && + this.extAttrs.every((extAttr) => extAttr.name !== "Exposed") + ) { + const message = `Interfaces must have \`[Exposed]\` extended attribute. \ +To fix, add, for example, \`[Exposed=Window]\`. Please also consider carefully \ +if your interface should also be exposed in a Worker scope. Refer to the \ +[WebIDL spec section on Exposed](https://heycam.github.io/webidl/#Exposed) \ +for more information.`; + yield (0,_error_js__WEBPACK_IMPORTED_MODULE_6__.validationError)( + this.tokens.name, + this, + "require-exposed", + message, + { + autofix: (0,_helpers_js__WEBPACK_IMPORTED_MODULE_5__.autofixAddExposedWindow)(this), + } + ); + } + const oldConstructors = this.extAttrs.filter( + (extAttr) => extAttr.name === "Constructor" + ); + for (const constructor of oldConstructors) { + const message = `Constructors should now be represented as a \`constructor()\` operation on the interface \ +instead of \`[Constructor]\` extended attribute. Refer to the \ +[WebIDL spec section on constructor operations](https://heycam.github.io/webidl/#idl-constructors) \ +for more information.`; + yield (0,_error_js__WEBPACK_IMPORTED_MODULE_6__.validationError)( + constructor.tokens.name, + this, + "constructor-member", + message, + { + autofix: autofixConstructor(this, constructor), + } + ); + } + + const isGlobal = this.extAttrs.some((extAttr) => extAttr.name === "Global"); + if (isGlobal) { + const factoryFunctions = this.extAttrs.filter( + (extAttr) => extAttr.name === "LegacyFactoryFunction" + ); + for (const named of factoryFunctions) { + const message = `Interfaces marked as \`[Global]\` cannot have factory functions.`; + yield (0,_error_js__WEBPACK_IMPORTED_MODULE_6__.validationError)( + named.tokens.name, + this, + "no-constructible-global", + message + ); + } + + const constructors = this.members.filter( + (member) => member.type === "constructor" + ); + for (const named of constructors) { + const message = `Interfaces marked as \`[Global]\` cannot have constructors.`; + yield (0,_error_js__WEBPACK_IMPORTED_MODULE_6__.validationError)( + named.tokens.base, + this, + "no-constructible-global", + message + ); + } + } + + yield* super.validate(defs); + if (!this.partial) { + yield* (0,_validators_interface_js__WEBPACK_IMPORTED_MODULE_7__.checkInterfaceMemberDuplication)(defs, this); + } + } +} + +function autofixConstructor(interfaceDef, constructorExtAttr) { + interfaceDef = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_5__.autoParenter)(interfaceDef); + return () => { + const indentation = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_5__.getLastIndentation)( + interfaceDef.extAttrs.tokens.open.trivia + ); + const memberIndent = interfaceDef.members.length + ? (0,_helpers_js__WEBPACK_IMPORTED_MODULE_5__.getLastIndentation)((0,_helpers_js__WEBPACK_IMPORTED_MODULE_5__.getFirstToken)(interfaceDef.members[0]).trivia) + : (0,_helpers_js__WEBPACK_IMPORTED_MODULE_5__.getMemberIndentation)(indentation); + const constructorOp = _constructor_js__WEBPACK_IMPORTED_MODULE_8__.Constructor.parse( + new _tokeniser_js__WEBPACK_IMPORTED_MODULE_9__.Tokeniser(`\n${memberIndent}constructor();`) + ); + constructorOp.extAttrs = new _extended_attributes_js__WEBPACK_IMPORTED_MODULE_10__.ExtendedAttributes({ + source: interfaceDef.source, + tokens: {}, + }); + (0,_helpers_js__WEBPACK_IMPORTED_MODULE_5__.autoParenter)(constructorOp).arguments = constructorExtAttr.arguments; + + const existingIndex = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_5__.findLastIndex)( + interfaceDef.members, + (m) => m.type === "constructor" + ); + interfaceDef.members.splice(existingIndex + 1, 0, constructorOp); + + const { close } = interfaceDef.tokens; + if (!close.trivia.includes("\n")) { + close.trivia += `\n${indentation}`; + } + + const { extAttrs } = interfaceDef; + const index = extAttrs.indexOf(constructorExtAttr); + const removed = extAttrs.splice(index, 1); + if (!extAttrs.length) { + extAttrs.tokens.open = extAttrs.tokens.close = undefined; + } else if (extAttrs.length === index) { + extAttrs[index - 1].tokens.separator = undefined; + } else if (!extAttrs[index].tokens.name.trivia.trim()) { + extAttrs[index].tokens.name.trivia = removed[0].tokens.name.trivia; + } + }; +} + + +/***/ }), +/* 20 */ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "Container": () => (/* binding */ Container) +/* harmony export */ }); +/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6); +/* harmony import */ var _extended_attributes_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(8); +/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4); + + + + +/** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + */ +function inheritance(tokeniser) { + const colon = tokeniser.consume(":"); + if (!colon) { + return {}; + } + const inheritance = + tokeniser.consumeKind("identifier") || + tokeniser.error("Inheritance lacks a type"); + return { colon, inheritance }; +} + +class Container extends _base_js__WEBPACK_IMPORTED_MODULE_0__.Base { + /** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + * @param {*} instance TODO: This should be {T extends Container}, but see https://github.com/microsoft/TypeScript/issues/4628 + * @param {*} args + */ + static parse(tokeniser, instance, { inheritable, allowedMembers }) { + const { tokens, type } = instance; + tokens.name = + tokeniser.consumeKind("identifier") || + tokeniser.error(`Missing name in ${type}`); + tokeniser.current = instance; + instance = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_2__.autoParenter)(instance); + if (inheritable) { + Object.assign(tokens, inheritance(tokeniser)); + } + tokens.open = tokeniser.consume("{") || tokeniser.error(`Bodyless ${type}`); + instance.members = []; + while (true) { + tokens.close = tokeniser.consume("}"); + if (tokens.close) { + tokens.termination = + tokeniser.consume(";") || + tokeniser.error(`Missing semicolon after ${type}`); + return instance.this; + } + const ea = _extended_attributes_js__WEBPACK_IMPORTED_MODULE_1__.ExtendedAttributes.parse(tokeniser); + let mem; + for (const [parser, ...args] of allowedMembers) { + mem = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_2__.autoParenter)(parser(tokeniser, ...args)); + if (mem) { + break; + } + } + if (!mem) { + tokeniser.error("Unknown member"); + } + mem.extAttrs = ea; + instance.members.push(mem.this); + } + } + + get partial() { + return !!this.tokens.partial; + } + get name() { + return (0,_helpers_js__WEBPACK_IMPORTED_MODULE_2__.unescape)(this.tokens.name.value); + } + get inheritance() { + if (!this.tokens.inheritance) { + return null; + } + return (0,_helpers_js__WEBPACK_IMPORTED_MODULE_2__.unescape)(this.tokens.inheritance.value); + } + + *validate(defs) { + for (const member of this.members) { + if (member.validate) { + yield* member.validate(defs); + } + } + } + + /** @param {import("../writer.js").Writer} w */ + write(w) { + const inheritance = () => { + if (!this.tokens.inheritance) { + return ""; + } + return w.ts.wrap([ + w.token(this.tokens.colon), + w.ts.trivia(this.tokens.inheritance.trivia), + w.ts.inheritance( + w.reference(this.tokens.inheritance.value, { context: this }) + ), + ]); + }; + + return w.ts.definition( + w.ts.wrap([ + this.extAttrs.write(w), + w.token(this.tokens.callback), + w.token(this.tokens.partial), + w.token(this.tokens.base), + w.token(this.tokens.mixin), + w.name_token(this.tokens.name, { data: this }), + inheritance(), + w.token(this.tokens.open), + w.ts.wrap(this.members.map((m) => m.write(w))), + w.token(this.tokens.close), + w.token(this.tokens.termination), + ]), + { data: this } + ); + } +} + + +/***/ }), +/* 21 */ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "Constant": () => (/* binding */ Constant) +/* harmony export */ }); +/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6); +/* harmony import */ var _type_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5); +/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4); + + + + +class Constant extends _base_js__WEBPACK_IMPORTED_MODULE_0__.Base { + /** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + */ + static parse(tokeniser) { + /** @type {Base["tokens"]} */ + const tokens = {}; + tokens.base = tokeniser.consume("const"); + if (!tokens.base) { + return; + } + let idlType = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_2__.primitive_type)(tokeniser); + if (!idlType) { + const base = + tokeniser.consumeKind("identifier") || + tokeniser.error("Const lacks a type"); + idlType = new _type_js__WEBPACK_IMPORTED_MODULE_1__.Type({ source: tokeniser.source, tokens: { base } }); + } + if (tokeniser.probe("?")) { + tokeniser.error("Unexpected nullable constant type"); + } + idlType.type = "const-type"; + tokens.name = + tokeniser.consumeKind("identifier") || + tokeniser.error("Const lacks a name"); + tokens.assign = + tokeniser.consume("=") || tokeniser.error("Const lacks value assignment"); + tokens.value = + (0,_helpers_js__WEBPACK_IMPORTED_MODULE_2__.const_value)(tokeniser) || tokeniser.error("Const lacks a value"); + tokens.termination = + tokeniser.consume(";") || + tokeniser.error("Unterminated const, expected `;`"); + const ret = new Constant({ source: tokeniser.source, tokens }); + (0,_helpers_js__WEBPACK_IMPORTED_MODULE_2__.autoParenter)(ret).idlType = idlType; + return ret; + } + + get type() { + return "const"; + } + get name() { + return (0,_helpers_js__WEBPACK_IMPORTED_MODULE_2__.unescape)(this.tokens.name.value); + } + get value() { + return (0,_helpers_js__WEBPACK_IMPORTED_MODULE_2__.const_data)(this.tokens.value); + } + + /** @param {import("../writer.js").Writer} w */ + write(w) { + const { parent } = this; + return w.ts.definition( + w.ts.wrap([ + this.extAttrs.write(w), + w.token(this.tokens.base), + w.ts.type(this.idlType.write(w)), + w.name_token(this.tokens.name, { data: this, parent }), + w.token(this.tokens.assign), + w.token(this.tokens.value), + w.token(this.tokens.termination), + ]), + { data: this, parent } + ); + } +} + + +/***/ }), +/* 22 */ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "IterableLike": () => (/* binding */ IterableLike) +/* harmony export */ }); +/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6); +/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); + + + +class IterableLike extends _base_js__WEBPACK_IMPORTED_MODULE_0__.Base { + /** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + */ + static parse(tokeniser) { + const start_position = tokeniser.position; + const ret = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.autoParenter)( + new IterableLike({ source: tokeniser.source, tokens: {} }) + ); + const { tokens } = ret; + tokens.readonly = tokeniser.consume("readonly"); + if (!tokens.readonly) { + tokens.async = tokeniser.consume("async"); + } + tokens.base = tokens.readonly + ? tokeniser.consume("maplike", "setlike") + : tokens.async + ? tokeniser.consume("iterable") + : tokeniser.consume("iterable", "maplike", "setlike"); + if (!tokens.base) { + tokeniser.unconsume(start_position); + return; + } + + const { type } = ret; + const secondTypeRequired = type === "maplike"; + const secondTypeAllowed = secondTypeRequired || type === "iterable"; + const argumentAllowed = ret.async && type === "iterable"; + + tokens.open = + tokeniser.consume("<") || + tokeniser.error(`Missing less-than sign \`<\` in ${type} declaration`); + const first = + (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.type_with_extended_attributes)(tokeniser) || + tokeniser.error(`Missing a type argument in ${type} declaration`); + ret.idlType = [first]; + ret.arguments = []; + + if (secondTypeAllowed) { + first.tokens.separator = tokeniser.consume(","); + if (first.tokens.separator) { + ret.idlType.push((0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.type_with_extended_attributes)(tokeniser)); + } else if (secondTypeRequired) { + tokeniser.error(`Missing second type argument in ${type} declaration`); + } + } + + tokens.close = + tokeniser.consume(">") || + tokeniser.error(`Missing greater-than sign \`>\` in ${type} declaration`); + + if (tokeniser.probe("(")) { + if (argumentAllowed) { + tokens.argsOpen = tokeniser.consume("("); + ret.arguments.push(...(0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.argument_list)(tokeniser)); + tokens.argsClose = + tokeniser.consume(")") || + tokeniser.error("Unterminated async iterable argument list"); + } else { + tokeniser.error(`Arguments are only allowed for \`async iterable\``); + } + } + + tokens.termination = + tokeniser.consume(";") || + tokeniser.error(`Missing semicolon after ${type} declaration`); + + return ret.this; + } + + get type() { + return this.tokens.base.value; + } + get readonly() { + return !!this.tokens.readonly; + } + get async() { + return !!this.tokens.async; + } + + *validate(defs) { + for (const type of this.idlType) { + yield* type.validate(defs); + } + for (const argument of this.arguments) { + yield* argument.validate(defs); + } + } + + /** @param {import("../writer.js").Writer} w */ + write(w) { + return w.ts.definition( + w.ts.wrap([ + this.extAttrs.write(w), + w.token(this.tokens.readonly), + w.token(this.tokens.async), + w.token(this.tokens.base, w.ts.generic), + w.token(this.tokens.open), + w.ts.wrap(this.idlType.map((t) => t.write(w))), + w.token(this.tokens.close), + w.token(this.tokens.argsOpen), + w.ts.wrap(this.arguments.map((arg) => arg.write(w))), + w.token(this.tokens.argsClose), + w.token(this.tokens.termination), + ]), + { data: this, parent: this.parent } + ); + } +} + + +/***/ }), +/* 23 */ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "checkInterfaceMemberDuplication": () => (/* binding */ checkInterfaceMemberDuplication) +/* harmony export */ }); +/* harmony import */ var _error_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3); + + +/** + * @param {import("../validator.js").Definitions} defs + * @param {import("../productions/container.js").Container} i + */ +function* checkInterfaceMemberDuplication(defs, i) { + const opNames = groupOperationNames(i); + const partials = defs.partials.get(i.name) || []; + const mixins = defs.mixinMap.get(i.name) || []; + for (const ext of [...partials, ...mixins]) { + const additions = getOperations(ext); + const statics = additions.filter((a) => a.special === "static"); + const nonstatics = additions.filter((a) => a.special !== "static"); + yield* checkAdditions(statics, opNames.statics, ext, i); + yield* checkAdditions(nonstatics, opNames.nonstatics, ext, i); + statics.forEach((op) => opNames.statics.add(op.name)); + nonstatics.forEach((op) => opNames.nonstatics.add(op.name)); + } + + /** + * @param {import("../productions/operation.js").Operation[]} additions + * @param {Set} existings + * @param {import("../productions/container.js").Container} ext + * @param {import("../productions/container.js").Container} base + */ + function* checkAdditions(additions, existings, ext, base) { + for (const addition of additions) { + const { name } = addition; + if (name && existings.has(name)) { + const isStatic = addition.special === "static" ? "static " : ""; + const message = `The ${isStatic}operation "${name}" has already been defined for the base interface "${base.name}" either in itself or in a mixin`; + yield (0,_error_js__WEBPACK_IMPORTED_MODULE_0__.validationError)( + addition.tokens.name, + ext, + "no-cross-overload", + message + ); + } + } + } + + /** + * @param {import("../productions/container.js").Container} i + * @returns {import("../productions/operation.js").Operation[]} + */ + function getOperations(i) { + return i.members.filter(({ type }) => type === "operation"); + } + + /** + * @param {import("../productions/container.js").Container} i + */ + function groupOperationNames(i) { + const ops = getOperations(i); + return { + statics: new Set( + ops.filter((op) => op.special === "static").map((op) => op.name) + ), + nonstatics: new Set( + ops.filter((op) => op.special !== "static").map((op) => op.name) + ), + }; + } +} + + +/***/ }), +/* 24 */ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "Constructor": () => (/* binding */ Constructor) +/* harmony export */ }); +/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6); +/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); + + + +class Constructor extends _base_js__WEBPACK_IMPORTED_MODULE_0__.Base { + /** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + */ + static parse(tokeniser) { + const base = tokeniser.consume("constructor"); + if (!base) { + return; + } + /** @type {Base["tokens"]} */ + const tokens = { base }; + tokens.open = + tokeniser.consume("(") || + tokeniser.error("No argument list in constructor"); + const args = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.argument_list)(tokeniser); + tokens.close = + tokeniser.consume(")") || tokeniser.error("Unterminated constructor"); + tokens.termination = + tokeniser.consume(";") || + tokeniser.error("No semicolon after constructor"); + const ret = new Constructor({ source: tokeniser.source, tokens }); + (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.autoParenter)(ret).arguments = args; + return ret; + } + + get type() { + return "constructor"; + } + + *validate(defs) { + for (const argument of this.arguments) { + yield* argument.validate(defs); + } + } + + /** @param {import("../writer.js").Writer} w */ + write(w) { + const { parent } = this; + return w.ts.definition( + w.ts.wrap([ + this.extAttrs.write(w), + w.token(this.tokens.base, w.ts.nameless, { data: this, parent }), + w.token(this.tokens.open), + w.ts.wrap(this.arguments.map((arg) => arg.write(w))), + w.token(this.tokens.close), + w.token(this.tokens.termination), + ]), + { data: this, parent } + ); + } +} + + +/***/ }), +/* 25 */ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "Mixin": () => (/* binding */ Mixin) +/* harmony export */ }); +/* harmony import */ var _container_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(20); +/* harmony import */ var _constant_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(21); +/* harmony import */ var _attribute_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(14); +/* harmony import */ var _operation_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(13); +/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(4); + + + + + + +class Mixin extends _container_js__WEBPACK_IMPORTED_MODULE_0__.Container { + /** + * @typedef {import("../tokeniser.js").Token} Token + * + * @param {import("../tokeniser.js").Tokeniser} tokeniser + * @param {Token} base + * @param {object} [options] + * @param {Token} [options.partial] + */ + static parse(tokeniser, base, { partial } = {}) { + const tokens = { partial, base }; + tokens.mixin = tokeniser.consume("mixin"); + if (!tokens.mixin) { + return; + } + return _container_js__WEBPACK_IMPORTED_MODULE_0__.Container.parse( + tokeniser, + new Mixin({ source: tokeniser.source, tokens }), + { + allowedMembers: [ + [_constant_js__WEBPACK_IMPORTED_MODULE_1__.Constant.parse], + [_helpers_js__WEBPACK_IMPORTED_MODULE_4__.stringifier], + [_attribute_js__WEBPACK_IMPORTED_MODULE_2__.Attribute.parse, { noInherit: true }], + [_operation_js__WEBPACK_IMPORTED_MODULE_3__.Operation.parse, { regular: true }], + ], + } + ); + } + + get type() { + return "interface mixin"; + } +} + + +/***/ }), +/* 26 */ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "Dictionary": () => (/* binding */ Dictionary) +/* harmony export */ }); +/* harmony import */ var _container_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(20); +/* harmony import */ var _field_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(27); + + + +class Dictionary extends _container_js__WEBPACK_IMPORTED_MODULE_0__.Container { + /** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + * @param {object} [options] + * @param {import("../tokeniser.js").Token} [options.partial] + */ + static parse(tokeniser, { partial } = {}) { + const tokens = { partial }; + tokens.base = tokeniser.consume("dictionary"); + if (!tokens.base) { + return; + } + return _container_js__WEBPACK_IMPORTED_MODULE_0__.Container.parse( + tokeniser, + new Dictionary({ source: tokeniser.source, tokens }), + { + inheritable: !partial, + allowedMembers: [[_field_js__WEBPACK_IMPORTED_MODULE_1__.Field.parse]], + } + ); + } + + get type() { + return "dictionary"; + } +} + + +/***/ }), +/* 27 */ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "Field": () => (/* binding */ Field) +/* harmony export */ }); +/* harmony import */ var _base_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6); +/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); +/* harmony import */ var _extended_attributes_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(8); +/* harmony import */ var _default_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(12); + + + + + +class Field extends _base_js__WEBPACK_IMPORTED_MODULE_0__.Base { + /** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + */ + static parse(tokeniser) { + /** @type {Base["tokens"]} */ + const tokens = {}; + const ret = (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.autoParenter)(new Field({ source: tokeniser.source, tokens })); + ret.extAttrs = _extended_attributes_js__WEBPACK_IMPORTED_MODULE_2__.ExtendedAttributes.parse(tokeniser); + tokens.required = tokeniser.consume("required"); + ret.idlType = + (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.type_with_extended_attributes)(tokeniser, "dictionary-type") || + tokeniser.error("Dictionary member lacks a type"); + tokens.name = + tokeniser.consumeKind("identifier") || + tokeniser.error("Dictionary member lacks a name"); + ret.default = _default_js__WEBPACK_IMPORTED_MODULE_3__.Default.parse(tokeniser); + if (tokens.required && ret.default) + tokeniser.error("Required member must not have a default"); + tokens.termination = + tokeniser.consume(";") || + tokeniser.error("Unterminated dictionary member, expected `;`"); + return ret.this; + } + + get type() { + return "field"; + } + get name() { + return (0,_helpers_js__WEBPACK_IMPORTED_MODULE_1__.unescape)(this.tokens.name.value); + } + get required() { + return !!this.tokens.required; + } + + *validate(defs) { + yield* this.idlType.validate(defs); + } + + /** @param {import("../writer.js").Writer} w */ + write(w) { + const { parent } = this; + return w.ts.definition( + w.ts.wrap([ + this.extAttrs.write(w), + w.token(this.tokens.required), + w.ts.type(this.idlType.write(w)), + w.name_token(this.tokens.name, { data: this, parent }), + this.default ? this.default.write(w) : "", + w.token(this.tokens.termination), + ]), + { data: this, parent } + ); + } +} + + +/***/ }), +/* 28 */ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "Namespace": () => (/* binding */ Namespace) +/* harmony export */ }); +/* harmony import */ var _container_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(20); +/* harmony import */ var _attribute_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(14); +/* harmony import */ var _operation_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(13); +/* harmony import */ var _error_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(3); +/* harmony import */ var _helpers_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(4); +/* harmony import */ var _constant_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(21); + + + + + + + +class Namespace extends _container_js__WEBPACK_IMPORTED_MODULE_0__.Container { + /** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + * @param {object} [options] + * @param {import("../tokeniser.js").Token} [options.partial] + */ + static parse(tokeniser, { partial } = {}) { + const tokens = { partial }; + tokens.base = tokeniser.consume("namespace"); + if (!tokens.base) { + return; + } + return _container_js__WEBPACK_IMPORTED_MODULE_0__.Container.parse( + tokeniser, + new Namespace({ source: tokeniser.source, tokens }), + { + allowedMembers: [ + [_attribute_js__WEBPACK_IMPORTED_MODULE_1__.Attribute.parse, { noInherit: true, readonly: true }], + [_constant_js__WEBPACK_IMPORTED_MODULE_5__.Constant.parse], + [_operation_js__WEBPACK_IMPORTED_MODULE_2__.Operation.parse, { regular: true }], + ], + } + ); + } + + get type() { + return "namespace"; + } + + *validate(defs) { + if ( + !this.partial && + this.extAttrs.every((extAttr) => extAttr.name !== "Exposed") + ) { + const message = `Namespaces must have [Exposed] extended attribute. \ +To fix, add, for example, [Exposed=Window]. Please also consider carefully \ +if your namespace should also be exposed in a Worker scope. Refer to the \ +[WebIDL spec section on Exposed](https://heycam.github.io/webidl/#Exposed) \ +for more information.`; + yield (0,_error_js__WEBPACK_IMPORTED_MODULE_3__.validationError)( + this.tokens.name, + this, + "require-exposed", + message, + { + autofix: (0,_helpers_js__WEBPACK_IMPORTED_MODULE_4__.autofixAddExposedWindow)(this), + } + ); + } + yield* super.validate(defs); + } +} + + +/***/ }), +/* 29 */ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "CallbackInterface": () => (/* binding */ CallbackInterface) +/* harmony export */ }); +/* harmony import */ var _container_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(20); +/* harmony import */ var _operation_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(13); +/* harmony import */ var _constant_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(21); + + + + +class CallbackInterface extends _container_js__WEBPACK_IMPORTED_MODULE_0__.Container { + /** + * @param {import("../tokeniser.js").Tokeniser} tokeniser + */ + static parse(tokeniser, callback, { partial = null } = {}) { + const tokens = { callback }; + tokens.base = tokeniser.consume("interface"); + if (!tokens.base) { + return; + } + return _container_js__WEBPACK_IMPORTED_MODULE_0__.Container.parse( + tokeniser, + new CallbackInterface({ source: tokeniser.source, tokens }), + { + inheritable: !partial, + allowedMembers: [ + [_constant_js__WEBPACK_IMPORTED_MODULE_2__.Constant.parse], + [_operation_js__WEBPACK_IMPORTED_MODULE_1__.Operation.parse, { regular: true }], + ], + } + ); + } + + get type() { + return "callback interface"; + } +} + + +/***/ }), +/* 30 */ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "Writer": () => (/* binding */ Writer), +/* harmony export */ "write": () => (/* binding */ write) +/* harmony export */ }); +function noop(arg) { + return arg; +} + +const templates = { + wrap: (items) => items.join(""), + trivia: noop, + name: noop, + reference: noop, + type: noop, + generic: noop, + nameless: noop, + inheritance: noop, + definition: noop, + extendedAttribute: noop, + extendedAttributeReference: noop, +}; + +class Writer { + constructor(ts) { + this.ts = Object.assign({}, templates, ts); + } + + /** + * @param {string} raw + * @param {object} options + * @param {string} [options.unescaped] + * @param {import("./productions/base.js").Base} [options.context] + * @returns + */ + reference(raw, { unescaped, context }) { + if (!unescaped) { + unescaped = raw.startsWith("_") ? raw.slice(1) : raw; + } + return this.ts.reference(raw, unescaped, context); + } + + /** + * @param {import("./tokeniser.js").Token} t + * @param {Function} wrapper + * @param {...any} args + * @returns + */ + token(t, wrapper = noop, ...args) { + if (!t) { + return ""; + } + const value = wrapper(t.value, ...args); + return this.ts.wrap([this.ts.trivia(t.trivia), value]); + } + + reference_token(t, context) { + return this.token(t, this.reference.bind(this), { context }); + } + + name_token(t, arg) { + return this.token(t, this.ts.name, arg); + } + + identifier(id, context) { + return this.ts.wrap([ + this.reference_token(id.tokens.value, context), + this.token(id.tokens.separator), + ]); + } +} + +function write(ast, { templates: ts = templates } = {}) { + ts = Object.assign({}, templates, ts); + + const w = new Writer(ts); + + return ts.wrap(ast.map((it) => it.write(w))); +} + + +/***/ }), +/* 31 */ +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "validate": () => (/* binding */ validate) +/* harmony export */ }); +/* harmony import */ var _error_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3); + + +function getMixinMap(all, unique) { + const map = new Map(); + const includes = all.filter((def) => def.type === "includes"); + for (const include of includes) { + const mixin = unique.get(include.includes); + if (!mixin) { + continue; + } + const array = map.get(include.target); + if (array) { + array.push(mixin); + } else { + map.set(include.target, [mixin]); + } + } + return map; +} + +/** + * @typedef {ReturnType} Definitions + */ +function groupDefinitions(all) { + const unique = new Map(); + const duplicates = new Set(); + const partials = new Map(); + for (const def of all) { + if (def.partial) { + const array = partials.get(def.name); + if (array) { + array.push(def); + } else { + partials.set(def.name, [def]); + } + continue; + } + if (!def.name) { + continue; + } + if (!unique.has(def.name)) { + unique.set(def.name, def); + } else { + duplicates.add(def); + } + } + return { + all, + unique, + partials, + duplicates, + mixinMap: getMixinMap(all, unique), + cache: { + typedefIncludesDictionary: new WeakMap(), + dictionaryIncludesRequiredField: new WeakMap(), + }, + }; +} + +function* checkDuplicatedNames({ unique, duplicates }) { + for (const dup of duplicates) { + const { name } = dup; + const message = `The name "${name}" of type "${ + unique.get(name).type + }" was already seen`; + yield (0,_error_js__WEBPACK_IMPORTED_MODULE_0__.validationError)(dup.tokens.name, dup, "no-duplicate", message); + } +} + +function* validateIterable(ast) { + const defs = groupDefinitions(ast); + for (const def of defs.all) { + if (def.validate) { + yield* def.validate(defs); + } + } + yield* checkDuplicatedNames(defs); +} + +// Remove this once all of our support targets expose `.flat()` by default +function flatten(array) { + if (array.flat) { + return array.flat(); + } + return [].concat(...array); +} + +/** + * @param {import("./productions/base.js").Base[]} ast + * @return {import("./error.js").WebIDLErrorData[]} validation errors + */ +function validate(ast) { + return [...validateIterable(flatten(ast))]; +} + + +/***/ }) +/******/ ]); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__); +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __webpack_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); +/******/ } +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ (() => { +/******/ // define __esModule on exports +/******/ __webpack_require__.r = (exports) => { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ })(); +/******/ +/************************************************************************/ +var __webpack_exports__ = {}; +// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. +(() => { +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ "WebIDLParseError": () => (/* reexport safe */ _lib_tokeniser_js__WEBPACK_IMPORTED_MODULE_3__.WebIDLParseError), +/* harmony export */ "parse": () => (/* reexport safe */ _lib_webidl2_js__WEBPACK_IMPORTED_MODULE_0__.parse), +/* harmony export */ "validate": () => (/* reexport safe */ _lib_validator_js__WEBPACK_IMPORTED_MODULE_2__.validate), +/* harmony export */ "write": () => (/* reexport safe */ _lib_writer_js__WEBPACK_IMPORTED_MODULE_1__.write) +/* harmony export */ }); +/* harmony import */ var _lib_webidl2_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); +/* harmony import */ var _lib_writer_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(30); +/* harmony import */ var _lib_validator_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(31); +/* harmony import */ var _lib_tokeniser_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(2); + + + + + +})(); + +/******/ return __webpack_exports__; +/******/ })() +; +}); +//# sourceMappingURL=webidl2.js.map \ No newline at end of file