diff options
Diffstat (limited to 'alarm/node_modules/jsdom/lib/jsdom/living/nodes/Document-impl.js')
-rw-r--r-- | alarm/node_modules/jsdom/lib/jsdom/living/nodes/Document-impl.js | 946 |
1 files changed, 946 insertions, 0 deletions
diff --git a/alarm/node_modules/jsdom/lib/jsdom/living/nodes/Document-impl.js b/alarm/node_modules/jsdom/lib/jsdom/living/nodes/Document-impl.js new file mode 100644 index 0000000..bfa812a --- /dev/null +++ b/alarm/node_modules/jsdom/lib/jsdom/living/nodes/Document-impl.js @@ -0,0 +1,946 @@ +"use strict"; + +const { CookieJar } = require("tough-cookie"); + +const NodeImpl = require("./Node-impl").implementation; +const idlUtils = require("../generated/utils"); +const NODE_TYPE = require("../node-type"); +const { hasWeakRefs, mixin, memoizeQuery } = require("../../utils"); +const { firstChildWithLocalName, firstChildWithLocalNames, firstDescendantWithLocalName } = + require("../helpers/traversal"); +const whatwgURL = require("whatwg-url"); +const StyleSheetList = require("../generated/StyleSheetList.js"); +const { domSymbolTree } = require("../helpers/internal-constants"); +const eventAccessors = require("../helpers/create-event-accessor"); +const { asciiLowercase, stripAndCollapseASCIIWhitespace } = require("../helpers/strings"); +const { childTextContent } = require("../helpers/text"); +const { HTML_NS, SVG_NS } = require("../helpers/namespaces"); +const DOMException = require("domexception/webidl2js-wrapper"); +const { parseIntoDocument } = require("../../browser/parser"); +const History = require("../generated/History"); +const Location = require("../generated/Location"); +const HTMLCollection = require("../generated/HTMLCollection"); +const NodeList = require("../generated/NodeList"); +const validateName = require("../helpers/validate-names").name; +const { validateAndExtract } = require("../helpers/validate-names"); +const { fireAnEvent } = require("../helpers/events"); +const { shadowIncludingInclusiveDescendantsIterator } = require("../helpers/shadow-dom"); +const { enqueueCECallbackReaction } = require("../helpers/custom-elements"); +const { createElement, internalCreateElementNSSteps } = require("../helpers/create-element"); +const IterableWeakSet = require("../helpers/iterable-weak-set"); + +const DocumentOrShadowRootImpl = require("./DocumentOrShadowRoot-impl").implementation; +const GlobalEventHandlersImpl = require("./GlobalEventHandlers-impl").implementation; +const NonElementParentNodeImpl = require("./NonElementParentNode-impl").implementation; +const ParentNodeImpl = require("./ParentNode-impl").implementation; + +const { clone, listOfElementsWithQualifiedName, listOfElementsWithNamespaceAndLocalName, + listOfElementsWithClassNames } = require("../node"); +const generatedAttr = require("../generated/Attr"); +const Comment = require("../generated/Comment"); +const ProcessingInstruction = require("../generated/ProcessingInstruction"); +const CDATASection = require("../generated/CDATASection"); +const Text = require("../generated/Text"); +const DocumentFragment = require("../generated/DocumentFragment"); +const DOMImplementation = require("../generated/DOMImplementation"); +const TreeWalker = require("../generated/TreeWalker"); +const NodeIterator = require("../generated/NodeIterator"); +const ShadowRoot = require("../generated/ShadowRoot"); +const Range = require("../generated/Range"); +const documents = require("../documents.js"); + +const CustomEvent = require("../generated/CustomEvent"); +const ErrorEvent = require("../generated/ErrorEvent"); +const Event = require("../generated/Event"); +const FocusEvent = require("../generated/FocusEvent"); +const HashChangeEvent = require("../generated/HashChangeEvent"); +const KeyboardEvent = require("../generated/KeyboardEvent"); +const MessageEvent = require("../generated/MessageEvent"); +const MouseEvent = require("../generated/MouseEvent"); +const PopStateEvent = require("../generated/PopStateEvent"); +const ProgressEvent = require("../generated/ProgressEvent"); +const TouchEvent = require("../generated/TouchEvent"); +const UIEvent = require("../generated/UIEvent"); + +const RequestManager = require("../../browser/resources/request-manager"); +const AsyncResourceQueue = require("../../browser/resources/async-resource-queue"); +const ResourceQueue = require("../../browser/resources/resource-queue"); +const PerDocumentResourceLoader = require("../../browser/resources/per-document-resource-loader"); + +function clearChildNodes(node) { + for (let child = domSymbolTree.firstChild(node); child; child = domSymbolTree.firstChild(node)) { + node.removeChild(child); + } +} + +function pad(number) { + if (number < 10) { + return "0" + number; + } + return number; +} + +function toLastModifiedString(date) { + return pad(date.getMonth() + 1) + + "/" + pad(date.getDate()) + + "/" + date.getFullYear() + + " " + pad(date.getHours()) + + ":" + pad(date.getMinutes()) + + ":" + pad(date.getSeconds()); +} + +const eventInterfaceTable = { + customevent: CustomEvent, + errorevent: ErrorEvent, + event: Event, + events: Event, + focusevent: FocusEvent, + hashchangeevent: HashChangeEvent, + htmlevents: Event, + keyboardevent: KeyboardEvent, + messageevent: MessageEvent, + mouseevent: MouseEvent, + mouseevents: MouseEvent, + popstateevent: PopStateEvent, + progressevent: ProgressEvent, + svgevents: Event, + touchevent: TouchEvent, + uievent: UIEvent, + uievents: UIEvent +}; + +class DocumentImpl extends NodeImpl { + constructor(globalObject, args, privateData) { + super(globalObject, args, privateData); + + this._initGlobalEvents(); + + this._ownerDocument = this; + this.nodeType = NODE_TYPE.DOCUMENT_NODE; + if (!privateData.options) { + privateData.options = {}; + } + if (!privateData.options.parsingMode) { + privateData.options.parsingMode = "xml"; + } + if (!privateData.options.encoding) { + privateData.options.encoding = "UTF-8"; + } + if (!privateData.options.contentType) { + privateData.options.contentType = privateData.options.parsingMode === "xml" ? "application/xml" : "text/html"; + } + + this._parsingMode = privateData.options.parsingMode; + + this._implementation = DOMImplementation.createImpl(this._globalObject, [], { + ownerDocument: this + }); + + this._defaultView = privateData.options.defaultView || null; + this._global = privateData.options.global; + this._ids = Object.create(null); + this._attached = true; + this._currentScript = null; + this._pageShowingFlag = false; + this._cookieJar = privateData.options.cookieJar; + this._parseOptions = privateData.options.parseOptions || {}; + this._scriptingDisabled = privateData.options.scriptingDisabled; + if (this._cookieJar === undefined) { + this._cookieJar = new CookieJar(null, { looseMode: true }); + } + + if (this._scriptingDisabled) { + this._parseOptions.scriptingEnabled = false; + } + + this.contentType = privateData.options.contentType; + this._encoding = privateData.options.encoding; + + const urlOption = privateData.options.url === undefined ? "about:blank" : privateData.options.url; + const parsed = whatwgURL.parseURL(urlOption); + if (parsed === null) { + throw new TypeError(`Could not parse "${urlOption}" as a URL`); + } + + this._URL = parsed; + this._origin = urlOption === "about:blank" && privateData.options.parentOrigin ? + privateData.options.parentOrigin : + whatwgURL.serializeURLOrigin(this._URL); + + this._location = Location.createImpl(this._globalObject, [], { relevantDocument: this }); + this._history = History.createImpl(this._globalObject, [], { + window: this._defaultView, + document: this, + actAsIfLocationReloadCalled: () => this._location.reload() + }); + + if (hasWeakRefs) { + this._workingNodeIterators = new IterableWeakSet(); + } else { + this._workingNodeIterators = []; + } + + this._referrer = privateData.options.referrer || ""; + this._lastModified = toLastModifiedString(privateData.options.lastModified || new Date()); + this._asyncQueue = new AsyncResourceQueue(); + this._queue = new ResourceQueue({ asyncQueue: this._asyncQueue, paused: false }); + this._deferQueue = new ResourceQueue({ paused: true }); + this._requestManager = new RequestManager(); + this._currentDocumentReadiness = privateData.options.readyState || "loading"; + + this._lastFocusedElement = null; + + this._resourceLoader = new PerDocumentResourceLoader(this); + + // Each Document in a browsing context can also have a latest entry. This is the entry for that Document + // to which the browsing context's session history was most recently traversed. When a Document is created, + // it initially has no latest entry. + this._latestEntry = null; + + // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#throw-on-dynamic-markup-insertion-counter + this._throwOnDynamicMarkupInsertionCounter = 0; + } + + _getTheParent(event) { + if (event.type === "load" || !this._defaultView) { + return null; + } + + return idlUtils.implForWrapper(this._defaultView); + } + + get compatMode() { + return this._parsingMode === "xml" || this.doctype ? "CSS1Compat" : "BackCompat"; + } + get charset() { + return this._encoding; + } + get characterSet() { + return this._encoding; + } + get inputEncoding() { + return this._encoding; + } + get doctype() { + for (const childNode of domSymbolTree.childrenIterator(this)) { + if (childNode.nodeType === NODE_TYPE.DOCUMENT_TYPE_NODE) { + return childNode; + } + } + return null; + } + get URL() { + return whatwgURL.serializeURL(this._URL); + } + get documentURI() { + return whatwgURL.serializeURL(this._URL); + } + get location() { + return this._defaultView ? this._location : null; + } + + // https://dom.spec.whatwg.org/#dom-document-documentelement + get documentElement() { + for (const childNode of domSymbolTree.childrenIterator(this)) { + if (childNode.nodeType === NODE_TYPE.ELEMENT_NODE) { + return childNode; + } + } + + return null; + } + + get implementation() { + return this._implementation; + } + set implementation(implementation) { + this._implementation = implementation; + } + + get defaultView() { + return this._defaultView; + } + + get currentScript() { + return this._currentScript; + } + + get readyState() { + return this._currentDocumentReadiness; + } + + set readyState(state) { + this._currentDocumentReadiness = state; + fireAnEvent("readystatechange", this); + } + + hasFocus() { + return Boolean(this._lastFocusedElement); + } + + _descendantRemoved(parent, child) { + if (child.tagName === "STYLE") { + this.styleSheets._remove(child.sheet); + } + + super._descendantRemoved(parent, child); + } + + write(...args) { + let text = ""; + for (let i = 0; i < args.length; ++i) { + text += args[i]; + } + + if (this._parsingMode === "xml") { + throw DOMException.create(this._globalObject, [ + "Cannot use document.write on XML documents", + "InvalidStateError" + ]); + } + + if (this._throwOnDynamicMarkupInsertionCounter > 0) { + throw DOMException.create(this._globalObject, [ + "Cannot use document.write while a custom element upgrades", + "InvalidStateError" + ]); + } + + if (this._writeAfterElement) { + // If called from an script element directly (during the first tick), + // the new elements are inserted right after that element. + const tempDiv = this.createElement("div"); + tempDiv.innerHTML = text; + + let child = tempDiv.firstChild; + let previous = this._writeAfterElement; + const parent = this._writeAfterElement.parentNode; + + while (child) { + const node = child; + child = child.nextSibling; + + node._isMovingDueToDocumentWrite = true; // hack for script execution + parent.insertBefore(node, previous.nextSibling); + node._isMovingDueToDocumentWrite = false; + + previous = node; + } + } else if (this.readyState === "loading") { + // During page loading, document.write appends to the current element + // Find the last child that has been added to the document. + if (this.lastChild) { + let node = this; + while (node.lastChild && node.lastChild.nodeType === NODE_TYPE.ELEMENT_NODE) { + node = node.lastChild; + } + node.innerHTML = text; + } else { + clearChildNodes(this); + parseIntoDocument(text, this); + } + } else if (text) { + clearChildNodes(this); + parseIntoDocument(text, this); + } + } + + writeln(...args) { + this.write(...args, "\n"); + } + + // This is implemented separately for Document (which has a _ids cache) and DocumentFragment (which does not). + getElementById(id) { + if (!this._ids[id]) { + return null; + } + + // Let's find the first element with where it's root is the document. + const matchElement = this._ids[id].find(candidate => { + let root = candidate; + while (domSymbolTree.parent(root)) { + root = domSymbolTree.parent(root); + } + + return root === this; + }); + + return matchElement || null; + } + + get referrer() { + return this._referrer || ""; + } + get lastModified() { + return this._lastModified; + } + get images() { + return this.getElementsByTagName("IMG"); + } + get embeds() { + return this.getElementsByTagName("EMBED"); + } + get plugins() { + return this.embeds; + } + get links() { + return HTMLCollection.createImpl(this._globalObject, [], { + element: this, + query: () => domSymbolTree.treeToArray(this, { + filter: node => (node._localName === "a" || node._localName === "area") && + node.hasAttributeNS(null, "href") && + node._namespaceURI === HTML_NS + }) + }); + } + get forms() { + return this.getElementsByTagName("FORM"); + } + get scripts() { + return this.getElementsByTagName("SCRIPT"); + } + get anchors() { + return HTMLCollection.createImpl(this._globalObject, [], { + element: this, + query: () => domSymbolTree.treeToArray(this, { + filter: node => node._localName === "a" && + node.hasAttributeNS(null, "name") && + node._namespaceURI === HTML_NS + }) + }); + } + + // The applets attribute must return an + // HTMLCollection rooted at the Document node, + // whose filter matches nothing. + // (It exists for historical reasons.) + get applets() { + return HTMLCollection.createImpl(this._globalObject, [], { + element: this, + query: () => [] + }); + } + + open() { + let child = domSymbolTree.firstChild(this); + while (child) { + this.removeChild(child); + child = domSymbolTree.firstChild(this); + } + this._modified(); + return this; + } + close(noQueue) { + // In some cases like when creating an empty iframe, I want to emit the + // events right away to avoid problems if later I asign the property src. + if (noQueue) { + this.readyState = "complete"; + + fireAnEvent("DOMContentLoaded", this, undefined, { bubbles: true }); + fireAnEvent("load", this); + + return; + } + this._queue.resume(); + + const dummyPromise = Promise.resolve(); + + const onDOMContentLoad = () => { + const doc = this; + function dispatchEvent() { + // https://html.spec.whatwg.org/#the-end + doc.readyState = "interactive"; + fireAnEvent("DOMContentLoaded", doc, undefined, { bubbles: true }); + } + + return new Promise(resolve => { + if (!this._deferQueue.tail) { + dispatchEvent(); + resolve(); + return; + } + + this._deferQueue.setListener(() => { + dispatchEvent(); + resolve(); + }); + + this._deferQueue.resume(); + }); + }; + + const onLoad = () => { + const doc = this; + function dispatchEvent() { + doc.readyState = "complete"; + fireAnEvent("load", doc); + } + + return new Promise(resolve => { + if (this._asyncQueue.count() === 0) { + dispatchEvent(); + resolve(); + return; + } + + this._asyncQueue.setListener(() => { + dispatchEvent(); + resolve(); + }); + }); + }; + + this._queue.push(dummyPromise, onDOMContentLoad, null); + // Set the readyState to 'complete' once all resources are loaded. + // As a side-effect the document's load-event will be dispatched. + this._queue.push(dummyPromise, onLoad, null, true); + } + + getElementsByName(elementName) { + return NodeList.createImpl(this._globalObject, [], { + element: this, + query: () => domSymbolTree.treeToArray(this, { + filter: node => node.getAttributeNS && node.getAttributeNS(null, "name") === elementName + }) + }); + } + + get title() { + const { documentElement } = this; + let value = ""; + + if (documentElement && documentElement._localName === "svg") { + const svgTitleElement = firstChildWithLocalName(documentElement, "title", SVG_NS); + + if (svgTitleElement) { + value = childTextContent(svgTitleElement); + } + } else { + const titleElement = firstDescendantWithLocalName(this, "title"); + + if (titleElement) { + value = childTextContent(titleElement); + } + } + + value = stripAndCollapseASCIIWhitespace(value); + + return value; + } + + set title(value) { + const { documentElement } = this; + let element; + + if (documentElement && documentElement._localName === "svg") { + element = firstChildWithLocalName(documentElement, "title", SVG_NS); + + if (!element) { + element = this.createElementNS(SVG_NS, "title"); + + this._insert(element, documentElement.firstChild); + } + + element.textContent = value; + } else if (documentElement && documentElement._namespaceURI === HTML_NS) { + const titleElement = firstDescendantWithLocalName(this, "title"); + const headElement = this.head; + + if (titleElement === null && headElement === null) { + return; + } + + if (titleElement !== null) { + element = titleElement; + } else { + element = this.createElement("title"); + headElement._append(element); + } + + element.textContent = value; + } + } + + get dir() { + return this.documentElement ? this.documentElement.dir : ""; + } + set dir(value) { + if (this.documentElement) { + this.documentElement.dir = value; + } + } + + get head() { + return this.documentElement ? firstChildWithLocalName(this.documentElement, "head") : null; + } + + get body() { + const { documentElement } = this; + if (!documentElement || documentElement._localName !== "html" || + documentElement._namespaceURI !== HTML_NS) { + return null; + } + + return firstChildWithLocalNames(this.documentElement, new Set(["body", "frameset"])); + } + + set body(value) { + if (value === null || + value._namespaceURI !== HTML_NS || + (value._localName !== "body" && value._localName !== "frameset")) { + throw DOMException.create(this._globalObject, [ + "Cannot set the body to null or a non-body/frameset element", + "HierarchyRequestError" + ]); + } + + const bodyElement = this.body; + if (value === bodyElement) { + return; + } + + if (bodyElement !== null) { + bodyElement.parentNode._replace(value, bodyElement); + return; + } + + const { documentElement } = this; + if (documentElement === null) { + throw DOMException.create(this._globalObject, [ + "Cannot set the body when there is no document element", + "HierarchyRequestError" + ]); + } + + documentElement._append(value); + } + + _runPreRemovingSteps(oldNode) { + // https://html.spec.whatwg.org/#focus-fixup-rule + if (oldNode === this.activeElement) { + this._lastFocusedElement = this.body; + } + for (const activeNodeIterator of this._workingNodeIterators) { + activeNodeIterator._preRemovingSteps(oldNode); + } + } + + createEvent(type) { + const typeLower = type.toLowerCase(); + const eventWrapper = eventInterfaceTable[typeLower] || null; + + if (!eventWrapper) { + throw DOMException.create(this._globalObject, [ + "The provided event type (\"" + type + "\") is invalid", + "NotSupportedError" + ]); + } + + const impl = eventWrapper.createImpl(this._globalObject, [""]); + impl._initializedFlag = false; + return impl; + } + + createRange() { + return Range.createImpl(this._globalObject, [], { + start: { node: this, offset: 0 }, + end: { node: this, offset: 0 } + }); + } + + createProcessingInstruction(target, data) { + validateName(this._globalObject, target); + + if (data.includes("?>")) { + throw DOMException.create(this._globalObject, [ + "Processing instruction data cannot contain the string \"?>\"", + "InvalidCharacterError" + ]); + } + + return ProcessingInstruction.createImpl(this._globalObject, [], { + ownerDocument: this, + target, + data + }); + } + + // https://dom.spec.whatwg.org/#dom-document-createcdatasection + createCDATASection(data) { + if (this._parsingMode === "html") { + throw DOMException.create(this._globalObject, [ + "Cannot create CDATA sections in HTML documents", + "NotSupportedError" + ]); + } + + if (data.includes("]]>")) { + throw DOMException.create(this._globalObject, [ + "CDATA section data cannot contain the string \"]]>\"", + "InvalidCharacterError" + ]); + } + + return CDATASection.createImpl(this._globalObject, [], { + ownerDocument: this, + data + }); + } + + createTextNode(data) { + return Text.createImpl(this._globalObject, [], { + ownerDocument: this, + data + }); + } + + createComment(data) { + return Comment.createImpl(this._globalObject, [], { + ownerDocument: this, + data + }); + } + + // https://dom.spec.whatwg.org/#dom-document-createelement + createElement(localName, options) { + validateName(this._globalObject, localName); + + if (this._parsingMode === "html") { + localName = asciiLowercase(localName); + } + + let isValue = null; + if (options && options.is !== undefined) { + isValue = options.is; + } + + const namespace = this._parsingMode === "html" || this.contentType === "application/xhtml+xml" ? HTML_NS : null; + + return createElement(this, localName, namespace, null, isValue, true); + } + + // https://dom.spec.whatwg.org/#dom-document-createelementns + createElementNS(namespace, qualifiedName, options) { + return internalCreateElementNSSteps(this, namespace, qualifiedName, options); + } + + createDocumentFragment() { + return DocumentFragment.createImpl(this._globalObject, [], { ownerDocument: this }); + } + + createAttribute(localName) { + validateName(this._globalObject, localName); + + if (this._parsingMode === "html") { + localName = asciiLowercase(localName); + } + + return this._createAttribute({ localName }); + } + + createAttributeNS(namespace, name) { + if (namespace === undefined) { + namespace = null; + } + namespace = namespace !== null ? String(namespace) : namespace; + + const extracted = validateAndExtract(this._globalObject, namespace, name); + return this._createAttribute({ + namespace: extracted.namespace, + namespacePrefix: extracted.prefix, + localName: extracted.localName + }); + } + + // Using this helper function rather than directly calling generatedAttr.createImpl may be preferred in some files, + // to avoid introducing a potentially cyclic dependency on generated/Attr.js. + _createAttribute({ + localName, + value, + namespace, + namespacePrefix + }) { + return generatedAttr.createImpl(this._globalObject, [], { + localName, + value, + namespace, + namespacePrefix, + ownerDocument: this + }); + } + + createTreeWalker(root, whatToShow, filter) { + return TreeWalker.createImpl(this._globalObject, [], { root, whatToShow, filter }); + } + + createNodeIterator(root, whatToShow, filter) { + const nodeIterator = NodeIterator.createImpl(this._globalObject, [], { root, whatToShow, filter }); + + if (hasWeakRefs) { + this._workingNodeIterators.add(nodeIterator); + } else { + this._workingNodeIterators.push(nodeIterator); + while (this._workingNodeIterators.length > 10) { + const toInactivate = this._workingNodeIterators.shift(); + toInactivate._working = false; + } + } + + return nodeIterator; + } + + importNode(node, deep) { + if (node.nodeType === NODE_TYPE.DOCUMENT_NODE) { + throw DOMException.create(this._globalObject, [ + "Cannot import a document node", + "NotSupportedError" + ]); + } else if (ShadowRoot.isImpl(node)) { + throw DOMException.create(this._globalObject, [ + "Cannot adopt a shadow root", + "NotSupportedError" + ]); + } + + return clone(node, this, deep); + } + + // https://dom.spec.whatwg.org/#dom-document-adoptnode + adoptNode(node) { + if (node.nodeType === NODE_TYPE.DOCUMENT_NODE) { + throw DOMException.create(this._globalObject, [ + "Cannot adopt a document node", + "NotSupportedError" + ]); + } else if (ShadowRoot.isImpl(node)) { + throw DOMException.create(this._globalObject, [ + "Cannot adopt a shadow root", + "HierarchyRequestError" + ]); + } + + this._adoptNode(node); + + return node; + } + + // https://dom.spec.whatwg.org/#concept-node-adopt + _adoptNode(node) { + const newDocument = this; + const oldDocument = node._ownerDocument; + + const parent = domSymbolTree.parent(node); + if (parent) { + parent._remove(node); + } + + if (oldDocument !== newDocument) { + for (const inclusiveDescendant of shadowIncludingInclusiveDescendantsIterator(node)) { + inclusiveDescendant._ownerDocument = newDocument; + } + + for (const inclusiveDescendant of shadowIncludingInclusiveDescendantsIterator(node)) { + if (inclusiveDescendant._ceState === "custom") { + enqueueCECallbackReaction(inclusiveDescendant, "adoptedCallback", [ + idlUtils.wrapperForImpl(oldDocument), + idlUtils.wrapperForImpl(newDocument) + ]); + } + } + + for (const inclusiveDescendant of shadowIncludingInclusiveDescendantsIterator(node)) { + if (inclusiveDescendant._adoptingSteps) { + inclusiveDescendant._adoptingSteps(oldDocument); + } + } + } + } + + get cookie() { + return this._cookieJar.getCookieStringSync(this.URL, { http: false }); + } + set cookie(cookieStr) { + cookieStr = String(cookieStr); + this._cookieJar.setCookieSync(cookieStr, this.URL, { + http: false, + ignoreError: true + }); + } + + // The clear(), captureEvents(), and releaseEvents() methods must do nothing + clear() {} + + captureEvents() {} + + releaseEvents() {} + + get styleSheets() { + if (!this._styleSheets) { + this._styleSheets = StyleSheetList.createImpl(this._globalObject); + } + + // TODO: each style and link element should register its sheet on creation + // and remove it on removal. + return this._styleSheets; + } + + get hidden() { + if (this._defaultView && this._defaultView._pretendToBeVisual) { + return false; + } + + return true; + } + + get visibilityState() { + if (this._defaultView && this._defaultView._pretendToBeVisual) { + return "visible"; + } + + return "prerender"; + } + + // https://w3c.github.io/selection-api/#extensions-to-document-interface + getSelection() { + return this._defaultView ? this._defaultView._selection : null; + } + + // Needed to ensure that the resulting document has the correct prototype chain: + // https://dom.spec.whatwg.org/#concept-node-clone says "that implements the same interfaces as node". + _cloneDocument() { + const copy = documents.createImpl( + this._globalObject, + { + contentType: this.contentType, + encoding: this._encoding, + parsingMode: this._parsingMode + } + ); + + copy._URL = this._URL; + copy._origin = this._origin; + return copy; + } +} + +eventAccessors.createEventAccessor(DocumentImpl.prototype, "readystatechange"); +mixin(DocumentImpl.prototype, DocumentOrShadowRootImpl.prototype); +mixin(DocumentImpl.prototype, GlobalEventHandlersImpl.prototype); +mixin(DocumentImpl.prototype, NonElementParentNodeImpl.prototype); +mixin(DocumentImpl.prototype, ParentNodeImpl.prototype); + +DocumentImpl.prototype.getElementsByTagName = memoizeQuery(function (qualifiedName) { + return listOfElementsWithQualifiedName(qualifiedName, this); +}); + +DocumentImpl.prototype.getElementsByTagNameNS = memoizeQuery(function (namespace, localName) { + return listOfElementsWithNamespaceAndLocalName(namespace, localName, this); +}); + +DocumentImpl.prototype.getElementsByClassName = memoizeQuery(function getElementsByClassName(classNames) { + return listOfElementsWithClassNames(classNames, this); +}); + +module.exports = { + implementation: DocumentImpl +}; |