Add node modules and new code for release (#39)

Co-authored-by: tbarnes94 <tbarnes94@users.noreply.github.com>
This commit is contained in:
github-actions[bot] 2022-01-05 11:26:06 -05:00 committed by GitHub
parent a10d84bc2e
commit 7ad2aa66bb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7655 changed files with 1763577 additions and 14 deletions

793
node_modules/jsdom/lib/jsdom/browser/Window.js generated vendored Normal file
View file

@ -0,0 +1,793 @@
"use strict";
const vm = require("vm");
const webIDLConversions = require("webidl-conversions");
const { CSSStyleDeclaration } = require("cssstyle");
const { Performance: RawPerformance } = require("w3c-hr-time");
const notImplemented = require("./not-implemented");
const { installInterfaces } = require("../living/interfaces");
const { define, mixin } = require("../utils");
const Element = require("../living/generated/Element");
const EventTarget = require("../living/generated/EventTarget");
const PageTransitionEvent = require("../living/generated/PageTransitionEvent");
const namedPropertiesWindow = require("../living/named-properties-window");
const postMessage = require("../living/post-message");
const DOMException = require("domexception/webidl2js-wrapper");
const { btoa, atob } = require("abab");
const idlUtils = require("../living/generated/utils");
const WebSocketImpl = require("../living/websockets/WebSocket-impl").implementation;
const BarProp = require("../living/generated/BarProp");
const documents = require("../living/documents.js");
const External = require("../living/generated/External");
const Navigator = require("../living/generated/Navigator");
const Performance = require("../living/generated/Performance");
const Screen = require("../living/generated/Screen");
const Storage = require("../living/generated/Storage");
const Selection = require("../living/generated/Selection");
const reportException = require("../living/helpers/runtime-script-errors");
const { fireAnEvent } = require("../living/helpers/events");
const SessionHistory = require("../living/window/SessionHistory");
const { forEachMatchingSheetRuleOfElement, getResolvedValue, propertiesWithResolvedValueImplemented,
SHADOW_DOM_PSEUDO_REGEXP } = require("../living/helpers/style-rules.js");
const CustomElementRegistry = require("../living/generated/CustomElementRegistry");
const jsGlobals = require("./js-globals.json");
const GlobalEventHandlersImpl = require("../living/nodes/GlobalEventHandlers-impl").implementation;
const WindowEventHandlersImpl = require("../living/nodes/WindowEventHandlers-impl").implementation;
exports.createWindow = function (options) {
return new Window(options);
};
const jsGlobalEntriesToInstall = Object.entries(jsGlobals).filter(([name]) => name in global);
// https://html.spec.whatwg.org/#the-window-object
function setupWindow(windowInstance, { runScripts }) {
if (runScripts === "outside-only" || runScripts === "dangerously") {
contextifyWindow(windowInstance);
// Without this, these globals will only appear to scripts running inside the context using vm.runScript; they will
// not appear to scripts running from the outside, including to JSDOM implementation code.
for (const [globalName, globalPropDesc] of jsGlobalEntriesToInstall) {
const propDesc = { ...globalPropDesc, value: vm.runInContext(globalName, windowInstance) };
Object.defineProperty(windowInstance, globalName, propDesc);
}
} else {
// Without contextifying the window, none of the globals will exist. So, let's at least alias them from the Node.js
// context. See https://github.com/jsdom/jsdom/issues/2727 for more background and discussion.
for (const [globalName, globalPropDesc] of jsGlobalEntriesToInstall) {
const propDesc = { ...globalPropDesc, value: global[globalName] };
Object.defineProperty(windowInstance, globalName, propDesc);
}
}
installInterfaces(windowInstance, ["Window"]);
const EventTargetConstructor = windowInstance.EventTarget;
// eslint-disable-next-line func-name-matching, func-style, no-shadow
const windowConstructor = function Window() {
throw new TypeError("Illegal constructor");
};
Object.setPrototypeOf(windowConstructor, EventTargetConstructor);
Object.defineProperty(windowInstance, "Window", {
configurable: true,
writable: true,
value: windowConstructor
});
const windowPrototype = Object.create(EventTargetConstructor.prototype);
Object.defineProperties(windowPrototype, {
constructor: {
value: windowConstructor,
writable: true,
configurable: true
},
[Symbol.toStringTag]: {
value: "Window",
configurable: true
}
});
windowConstructor.prototype = windowPrototype;
Object.setPrototypeOf(windowInstance, windowPrototype);
EventTarget.setup(windowInstance, windowInstance);
mixin(windowInstance, WindowEventHandlersImpl.prototype);
mixin(windowInstance, GlobalEventHandlersImpl.prototype);
windowInstance._initGlobalEvents();
windowInstance._globalObject = windowInstance;
}
// NOTE: per https://heycam.github.io/webidl/#Global, all properties on the Window object must be own-properties.
// That is why we assign everything inside of the constructor, instead of using a shared prototype.
// You can verify this in e.g. Firefox or Internet Explorer, which do a good job with Web IDL compliance.
function Window(options) {
setupWindow(this, { runScripts: options.runScripts });
const rawPerformance = new RawPerformance();
const windowInitialized = rawPerformance.now();
const window = this;
///// PRIVATE DATA PROPERTIES
this._resourceLoader = options.resourceLoader;
// vm initialization is deferred until script processing is activated
this._globalProxy = this;
Object.defineProperty(idlUtils.implForWrapper(this), idlUtils.wrapperSymbol, { get: () => this._globalProxy });
// List options explicitly to be clear which are passed through
this._document = documents.createWrapper(window, {
parsingMode: options.parsingMode,
contentType: options.contentType,
encoding: options.encoding,
cookieJar: options.cookieJar,
url: options.url,
lastModified: options.lastModified,
referrer: options.referrer,
concurrentNodeIterators: options.concurrentNodeIterators,
parseOptions: options.parseOptions,
defaultView: this._globalProxy,
global: this
}, { alwaysUseDocumentClass: true });
if (vm.isContext(window)) {
const documentImpl = idlUtils.implForWrapper(window._document);
documentImpl._defaultView = window._globalProxy = vm.runInContext("this", window);
}
const documentOrigin = idlUtils.implForWrapper(this._document)._origin;
this._origin = documentOrigin;
// https://html.spec.whatwg.org/#session-history
this._sessionHistory = new SessionHistory({
document: idlUtils.implForWrapper(this._document),
url: idlUtils.implForWrapper(this._document)._URL,
stateObject: null
}, this);
this._virtualConsole = options.virtualConsole;
this._runScripts = options.runScripts;
// Set up the window as if it's a top level window.
// If it's not, then references will be corrected by frame/iframe code.
this._parent = this._top = this._globalProxy;
this._frameElement = null;
// This implements window.frames.length, since window.frames returns a
// self reference to the window object. This value is incremented in the
// HTMLFrameElement implementation.
this._length = 0;
this._pretendToBeVisual = options.pretendToBeVisual;
this._storageQuota = options.storageQuota;
// Some properties (such as localStorage and sessionStorage) share data
// between windows in the same origin. This object is intended
// to contain such data.
if (options.commonForOrigin && options.commonForOrigin[documentOrigin]) {
this._commonForOrigin = options.commonForOrigin;
} else {
this._commonForOrigin = {
[documentOrigin]: {
localStorageArea: new Map(),
sessionStorageArea: new Map(),
windowsInSameOrigin: [this]
}
};
}
this._currentOriginData = this._commonForOrigin[documentOrigin];
///// WEB STORAGE
this._localStorage = Storage.create(window, [], {
associatedWindow: this,
storageArea: this._currentOriginData.localStorageArea,
type: "localStorage",
url: this._document.documentURI,
storageQuota: this._storageQuota
});
this._sessionStorage = Storage.create(window, [], {
associatedWindow: this,
storageArea: this._currentOriginData.sessionStorageArea,
type: "sessionStorage",
url: this._document.documentURI,
storageQuota: this._storageQuota
});
///// SELECTION
// https://w3c.github.io/selection-api/#dfn-selection
this._selection = Selection.createImpl(window);
// https://w3c.github.io/selection-api/#dom-window
this.getSelection = function () {
return window._selection;
};
///// GETTERS
const locationbar = BarProp.create(window);
const menubar = BarProp.create(window);
const personalbar = BarProp.create(window);
const scrollbars = BarProp.create(window);
const statusbar = BarProp.create(window);
const toolbar = BarProp.create(window);
const external = External.create(window);
const navigator = Navigator.create(window, [], { userAgent: this._resourceLoader._userAgent });
const performance = Performance.create(window, [], { rawPerformance });
const screen = Screen.create(window);
const customElementRegistry = CustomElementRegistry.create(window);
define(this, {
get length() {
return window._length;
},
get window() {
return window._globalProxy;
},
get frameElement() {
return idlUtils.wrapperForImpl(window._frameElement);
},
get frames() {
return window._globalProxy;
},
get self() {
return window._globalProxy;
},
get parent() {
return window._parent;
},
get top() {
return window._top;
},
get document() {
return window._document;
},
get external() {
return external;
},
get location() {
return idlUtils.wrapperForImpl(idlUtils.implForWrapper(window._document)._location);
},
get history() {
return idlUtils.wrapperForImpl(idlUtils.implForWrapper(window._document)._history);
},
get navigator() {
return navigator;
},
get locationbar() {
return locationbar;
},
get menubar() {
return menubar;
},
get personalbar() {
return personalbar;
},
get scrollbars() {
return scrollbars;
},
get statusbar() {
return statusbar;
},
get toolbar() {
return toolbar;
},
get performance() {
return performance;
},
get screen() {
return screen;
},
get origin() {
return window._origin;
},
// The origin IDL attribute is defined with [Replaceable].
set origin(value) {
Object.defineProperty(this, "origin", {
value,
writable: true,
enumerable: true,
configurable: true
});
},
get localStorage() {
if (idlUtils.implForWrapper(this._document)._origin === "null") {
throw DOMException.create(window, [
"localStorage is not available for opaque origins",
"SecurityError"
]);
}
return this._localStorage;
},
get sessionStorage() {
if (idlUtils.implForWrapper(this._document)._origin === "null") {
throw DOMException.create(window, [
"sessionStorage is not available for opaque origins",
"SecurityError"
]);
}
return this._sessionStorage;
},
get customElements() {
return customElementRegistry;
}
});
namedPropertiesWindow.initializeWindow(this, this._globalProxy);
///// METHODS
// https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers
// In the spec the list of active timers is a set of IDs. We make it a map of IDs to Node.js timer objects, so that
// we can call Node.js-side clearTimeout() when clearing, and thus allow process shutdown faster.
const listOfActiveTimers = new Map();
let latestTimerId = 0;
this.setTimeout = function (handler, timeout = 0, ...args) {
if (typeof handler !== "function") {
handler = webIDLConversions.DOMString(handler);
}
timeout = webIDLConversions.long(timeout);
return timerInitializationSteps(handler, timeout, args, { methodContext: window, repeat: false });
};
this.setInterval = function (handler, timeout = 0, ...args) {
if (typeof handler !== "function") {
handler = webIDLConversions.DOMString(handler);
}
timeout = webIDLConversions.long(timeout);
return timerInitializationSteps(handler, timeout, args, { methodContext: window, repeat: true });
};
this.clearTimeout = function (handle = 0) {
handle = webIDLConversions.long(handle);
const nodejsTimer = listOfActiveTimers.get(handle);
if (nodejsTimer) {
clearTimeout(nodejsTimer);
listOfActiveTimers.delete(handle);
}
};
this.clearInterval = function (handle = 0) {
handle = webIDLConversions.long(handle);
const nodejsTimer = listOfActiveTimers.get(handle);
if (nodejsTimer) {
// We use setTimeout() in timerInitializationSteps even for this.setInterval().
clearTimeout(nodejsTimer);
listOfActiveTimers.delete(handle);
}
};
function timerInitializationSteps(handler, timeout, args, { methodContext, repeat, previousHandle }) {
// This appears to be unspecced, but matches browser behavior for close()ed windows.
if (!methodContext._document) {
return 0;
}
// TODO: implement timer nesting level behavior.
const methodContextProxy = methodContext._globalProxy;
const handle = previousHandle !== undefined ? previousHandle : ++latestTimerId;
function task() {
if (!listOfActiveTimers.has(handle)) {
return;
}
try {
if (typeof handler === "function") {
handler.apply(methodContextProxy, args);
} else if (window._runScripts === "dangerously") {
vm.runInContext(handler, window, { filename: window.location.href, displayErrors: false });
}
} catch (e) {
reportException(window, e, window.location.href);
}
if (listOfActiveTimers.has(handle)) {
if (repeat) {
timerInitializationSteps(handler, timeout, args, { methodContext, repeat: true, previousHandle: handle });
} else {
listOfActiveTimers.delete(handle);
}
}
}
if (timeout < 0) {
timeout = 0;
}
const nodejsTimer = setTimeout(task, timeout);
listOfActiveTimers.set(handle, nodejsTimer);
return handle;
}
// https://html.spec.whatwg.org/multipage/imagebitmap-and-animations.html#animation-frames
let animationFrameCallbackId = 0;
const mapOfAnimationFrameCallbacks = new Map();
let animationFrameNodejsInterval = null;
// Unlike the spec, where an animation frame happens every 60 Hz regardless, we optimize so that if there are no
// requestAnimationFrame() calls outstanding, we don't fire the timer. This helps us track that.
let numberOfOngoingAnimationFrameCallbacks = 0;
if (this._pretendToBeVisual) {
this.requestAnimationFrame = function (callback) {
callback = webIDLConversions.Function(callback);
const handle = ++animationFrameCallbackId;
mapOfAnimationFrameCallbacks.set(handle, callback);
++numberOfOngoingAnimationFrameCallbacks;
if (numberOfOngoingAnimationFrameCallbacks === 1) {
animationFrameNodejsInterval = setInterval(() => {
runAnimationFrameCallbacks(rawPerformance.now() - windowInitialized);
}, 1000 / 60);
}
return handle;
};
this.cancelAnimationFrame = function (handle) {
handle = webIDLConversions["unsigned long"](handle);
removeAnimationFrameCallback(handle);
};
function runAnimationFrameCallbacks(now) {
// Converting to an array is important to get a sync snapshot and thus match spec semantics.
const callbackHandles = [...mapOfAnimationFrameCallbacks.keys()];
for (const handle of callbackHandles) {
// This has() can be false if a callback calls cancelAnimationFrame().
if (mapOfAnimationFrameCallbacks.has(handle)) {
const callback = mapOfAnimationFrameCallbacks.get(handle);
removeAnimationFrameCallback(handle);
try {
callback(now);
} catch (e) {
reportException(window, e, window.location.href);
}
}
}
}
function removeAnimationFrameCallback(handle) {
if (mapOfAnimationFrameCallbacks.has(handle)) {
--numberOfOngoingAnimationFrameCallbacks;
if (numberOfOngoingAnimationFrameCallbacks === 0) {
clearInterval(animationFrameNodejsInterval);
}
}
mapOfAnimationFrameCallbacks.delete(handle);
}
}
function stopAllTimers() {
for (const nodejsTimer of listOfActiveTimers.values()) {
clearTimeout(nodejsTimer);
}
listOfActiveTimers.clear();
clearInterval(animationFrameNodejsInterval);
}
function Option(text, value, defaultSelected, selected) {
if (text === undefined) {
text = "";
}
text = webIDLConversions.DOMString(text);
if (value !== undefined) {
value = webIDLConversions.DOMString(value);
}
defaultSelected = webIDLConversions.boolean(defaultSelected);
selected = webIDLConversions.boolean(selected);
const option = window._document.createElement("option");
const impl = idlUtils.implForWrapper(option);
if (text !== "") {
impl.text = text;
}
if (value !== undefined) {
impl.setAttributeNS(null, "value", value);
}
if (defaultSelected) {
impl.setAttributeNS(null, "selected", "");
}
impl._selectedness = selected;
return option;
}
Object.defineProperty(Option, "prototype", {
value: this.HTMLOptionElement.prototype,
configurable: false,
enumerable: false,
writable: false
});
Object.defineProperty(window, "Option", {
value: Option,
configurable: true,
enumerable: false,
writable: true
});
function Image() {
const img = window._document.createElement("img");
const impl = idlUtils.implForWrapper(img);
if (arguments.length > 0) {
impl.setAttributeNS(null, "width", String(arguments[0]));
}
if (arguments.length > 1) {
impl.setAttributeNS(null, "height", String(arguments[1]));
}
return img;
}
Object.defineProperty(Image, "prototype", {
value: this.HTMLImageElement.prototype,
configurable: false,
enumerable: false,
writable: false
});
Object.defineProperty(window, "Image", {
value: Image,
configurable: true,
enumerable: false,
writable: true
});
function Audio(src) {
const audio = window._document.createElement("audio");
const impl = idlUtils.implForWrapper(audio);
impl.setAttributeNS(null, "preload", "auto");
if (src !== undefined) {
impl.setAttributeNS(null, "src", String(src));
}
return audio;
}
Object.defineProperty(Audio, "prototype", {
value: this.HTMLAudioElement.prototype,
configurable: false,
enumerable: false,
writable: false
});
Object.defineProperty(window, "Audio", {
value: Audio,
configurable: true,
enumerable: false,
writable: true
});
this.postMessage = postMessage(window);
this.atob = function (str) {
const result = atob(str);
if (result === null) {
throw DOMException.create(window, [
"The string to be decoded contains invalid characters.",
"InvalidCharacterError"
]);
}
return result;
};
this.btoa = function (str) {
const result = btoa(str);
if (result === null) {
throw DOMException.create(window, [
"The string to be encoded contains invalid characters.",
"InvalidCharacterError"
]);
}
return result;
};
this.stop = function () {
const manager = idlUtils.implForWrapper(this._document)._requestManager;
if (manager) {
manager.close();
}
};
this.close = function () {
// Recursively close child frame windows, then ourselves (depth-first).
for (let i = 0; i < this.length; ++i) {
this[i].close();
}
// Clear out all listeners. Any in-flight or upcoming events should not get delivered.
idlUtils.implForWrapper(this)._eventListeners = Object.create(null);
if (this._document) {
if (this._document.body) {
this._document.body.innerHTML = "";
}
if (this._document.close) {
// It's especially important to clear out the listeners here because document.close() causes a "load" event to
// fire.
idlUtils.implForWrapper(this._document)._eventListeners = Object.create(null);
this._document.close();
}
const doc = idlUtils.implForWrapper(this._document);
if (doc._requestManager) {
doc._requestManager.close();
}
delete this._document;
}
stopAllTimers();
WebSocketImpl.cleanUpWindow(this);
};
this.getComputedStyle = function (elt) {
elt = Element.convert(elt);
let pseudoElt = arguments[1];
if (pseudoElt !== undefined && pseudoElt !== null) {
pseudoElt = webIDLConversions.DOMString(pseudoElt);
}
if (pseudoElt !== undefined && pseudoElt !== null && pseudoElt !== "") {
// TODO: Parse pseudoElt
if (SHADOW_DOM_PSEUDO_REGEXP.test(pseudoElt)) {
throw new TypeError("Tried to get the computed style of a Shadow DOM pseudo-element.");
}
notImplemented("window.computedStyle(elt, pseudoElt)", this);
}
const declaration = new CSSStyleDeclaration();
const { forEach } = Array.prototype;
const { style } = elt;
forEachMatchingSheetRuleOfElement(elt, rule => {
forEach.call(rule.style, property => {
declaration.setProperty(
property,
rule.style.getPropertyValue(property),
rule.style.getPropertyPriority(property)
);
});
});
// https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle
const declarations = Object.keys(propertiesWithResolvedValueImplemented);
forEach.call(declarations, property => {
declaration.setProperty(property, getResolvedValue(elt, property));
});
forEach.call(style, property => {
declaration.setProperty(property, style.getPropertyValue(property), style.getPropertyPriority(property));
});
return declaration;
};
this.getSelection = function () {
return window._document.getSelection();
};
// The captureEvents() and releaseEvents() methods must do nothing
this.captureEvents = function () {};
this.releaseEvents = function () {};
///// PUBLIC DATA PROPERTIES (TODO: should be getters)
function wrapConsoleMethod(method) {
return (...args) => {
window._virtualConsole.emit(method, ...args);
};
}
this.console = {
assert: wrapConsoleMethod("assert"),
clear: wrapConsoleMethod("clear"),
count: wrapConsoleMethod("count"),
countReset: wrapConsoleMethod("countReset"),
debug: wrapConsoleMethod("debug"),
dir: wrapConsoleMethod("dir"),
dirxml: wrapConsoleMethod("dirxml"),
error: wrapConsoleMethod("error"),
group: wrapConsoleMethod("group"),
groupCollapsed: wrapConsoleMethod("groupCollapsed"),
groupEnd: wrapConsoleMethod("groupEnd"),
info: wrapConsoleMethod("info"),
log: wrapConsoleMethod("log"),
table: wrapConsoleMethod("table"),
time: wrapConsoleMethod("time"),
timeLog: wrapConsoleMethod("timeLog"),
timeEnd: wrapConsoleMethod("timeEnd"),
trace: wrapConsoleMethod("trace"),
warn: wrapConsoleMethod("warn")
};
function notImplementedMethod(name) {
return function () {
notImplemented(name, window);
};
}
define(this, {
name: "",
status: "",
devicePixelRatio: 1,
innerWidth: 1024,
innerHeight: 768,
outerWidth: 1024,
outerHeight: 768,
pageXOffset: 0,
pageYOffset: 0,
screenX: 0,
screenLeft: 0,
screenY: 0,
screenTop: 0,
scrollX: 0,
scrollY: 0,
alert: notImplementedMethod("window.alert"),
blur: notImplementedMethod("window.blur"),
confirm: notImplementedMethod("window.confirm"),
focus: notImplementedMethod("window.focus"),
moveBy: notImplementedMethod("window.moveBy"),
moveTo: notImplementedMethod("window.moveTo"),
open: notImplementedMethod("window.open"),
print: notImplementedMethod("window.print"),
prompt: notImplementedMethod("window.prompt"),
resizeBy: notImplementedMethod("window.resizeBy"),
resizeTo: notImplementedMethod("window.resizeTo"),
scroll: notImplementedMethod("window.scroll"),
scrollBy: notImplementedMethod("window.scrollBy"),
scrollTo: notImplementedMethod("window.scrollTo")
});
///// INITIALIZATION
process.nextTick(() => {
if (!window.document) {
return; // window might've been closed already
}
if (window.document.readyState === "complete") {
fireAnEvent("load", window, undefined, {}, window.document);
} else {
window.document.addEventListener("load", () => {
fireAnEvent("load", window, undefined, {}, window.document);
if (!idlUtils.implForWrapper(window._document)._pageShowingFlag) {
idlUtils.implForWrapper(window._document)._pageShowingFlag = true;
fireAnEvent("pageshow", window, PageTransitionEvent, { persisted: false }, window.document);
}
});
}
});
}
function contextifyWindow(window) {
if (vm.isContext(window)) {
return;
}
vm.createContext(window);
}

View file

@ -0,0 +1,785 @@
// Ideally, we would use
// https://html.spec.whatwg.org/multipage/rendering.html#the-css-user-agent-style-sheet-and-presentational-hints
// but for now, just use the version from blink. This file is copied from
// https://chromium.googlesource.com/chromium/blink/+/96aa3a280ab7d67178c8f122a04949ce5f8579e0/Source/core/css/html.css
// (removed a line which had octal literals inside since octal literals are not allowed in template strings)
// We use a .js file because otherwise we can't browserify this. (brfs is unusable due to lack of ES2015 support)
module.exports = `
/*
* The default style sheet used to render HTML.
*
* Copyright (C) 2000 Lars Knoll (knoll@kde.org)
* Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
@namespace "http://www.w3.org/1999/xhtml";
html {
display: block
}
:root {
scroll-blocks-on: start-touch wheel-event
}
/* children of the <head> element all have display:none */
head {
display: none
}
meta {
display: none
}
title {
display: none
}
link {
display: none
}
style {
display: none
}
script {
display: none
}
/* generic block-level elements */
body {
display: block;
margin: 8px
}
p {
display: block;
-webkit-margin-before: 1__qem;
-webkit-margin-after: 1__qem;
-webkit-margin-start: 0;
-webkit-margin-end: 0;
}
div {
display: block
}
layer {
display: block
}
article, aside, footer, header, hgroup, main, nav, section {
display: block
}
marquee {
display: inline-block;
}
address {
display: block
}
blockquote {
display: block;
-webkit-margin-before: 1__qem;
-webkit-margin-after: 1em;
-webkit-margin-start: 40px;
-webkit-margin-end: 40px;
}
figcaption {
display: block
}
figure {
display: block;
-webkit-margin-before: 1em;
-webkit-margin-after: 1em;
-webkit-margin-start: 40px;
-webkit-margin-end: 40px;
}
q {
display: inline
}
/* nwmatcher does not support ::before and ::after, so we can't render q
correctly: https://html.spec.whatwg.org/multipage/rendering.html#phrasing-content-3
TODO: add q::before and q::after selectors
*/
center {
display: block;
/* special centering to be able to emulate the html4/netscape behaviour */
text-align: -webkit-center
}
hr {
display: block;
-webkit-margin-before: 0.5em;
-webkit-margin-after: 0.5em;
-webkit-margin-start: auto;
-webkit-margin-end: auto;
border-style: inset;
border-width: 1px;
box-sizing: border-box
}
map {
display: inline
}
video {
object-fit: contain;
}
/* heading elements */
h1 {
display: block;
font-size: 2em;
-webkit-margin-before: 0.67__qem;
-webkit-margin-after: 0.67em;
-webkit-margin-start: 0;
-webkit-margin-end: 0;
font-weight: bold
}
article h1,
aside h1,
nav h1,
section h1 {
font-size: 1.5em;
-webkit-margin-before: 0.83__qem;
-webkit-margin-after: 0.83em;
}
article article h1,
article aside h1,
article nav h1,
article section h1,
aside article h1,
aside aside h1,
aside nav h1,
aside section h1,
nav article h1,
nav aside h1,
nav nav h1,
nav section h1,
section article h1,
section aside h1,
section nav h1,
section section h1 {
font-size: 1.17em;
-webkit-margin-before: 1__qem;
-webkit-margin-after: 1em;
}
/* Remaining selectors are deleted because nwmatcher does not support
:matches() and expanding the selectors manually would be far too verbose.
Also see https://html.spec.whatwg.org/multipage/rendering.html#sections-and-headings
TODO: rewrite to use :matches() when nwmatcher supports it.
*/
h2 {
display: block;
font-size: 1.5em;
-webkit-margin-before: 0.83__qem;
-webkit-margin-after: 0.83em;
-webkit-margin-start: 0;
-webkit-margin-end: 0;
font-weight: bold
}
h3 {
display: block;
font-size: 1.17em;
-webkit-margin-before: 1__qem;
-webkit-margin-after: 1em;
-webkit-margin-start: 0;
-webkit-margin-end: 0;
font-weight: bold
}
h4 {
display: block;
-webkit-margin-before: 1.33__qem;
-webkit-margin-after: 1.33em;
-webkit-margin-start: 0;
-webkit-margin-end: 0;
font-weight: bold
}
h5 {
display: block;
font-size: .83em;
-webkit-margin-before: 1.67__qem;
-webkit-margin-after: 1.67em;
-webkit-margin-start: 0;
-webkit-margin-end: 0;
font-weight: bold
}
h6 {
display: block;
font-size: .67em;
-webkit-margin-before: 2.33__qem;
-webkit-margin-after: 2.33em;
-webkit-margin-start: 0;
-webkit-margin-end: 0;
font-weight: bold
}
/* tables */
table {
display: table;
border-collapse: separate;
border-spacing: 2px;
border-color: gray
}
thead {
display: table-header-group;
vertical-align: middle;
border-color: inherit
}
tbody {
display: table-row-group;
vertical-align: middle;
border-color: inherit
}
tfoot {
display: table-footer-group;
vertical-align: middle;
border-color: inherit
}
/* for tables without table section elements (can happen with XHTML or dynamically created tables) */
table > tr {
vertical-align: middle;
}
col {
display: table-column
}
colgroup {
display: table-column-group
}
tr {
display: table-row;
vertical-align: inherit;
border-color: inherit
}
td, th {
display: table-cell;
vertical-align: inherit
}
th {
font-weight: bold
}
caption {
display: table-caption;
text-align: -webkit-center
}
/* lists */
ul, menu, dir {
display: block;
list-style-type: disc;
-webkit-margin-before: 1__qem;
-webkit-margin-after: 1em;
-webkit-margin-start: 0;
-webkit-margin-end: 0;
-webkit-padding-start: 40px
}
ol {
display: block;
list-style-type: decimal;
-webkit-margin-before: 1__qem;
-webkit-margin-after: 1em;
-webkit-margin-start: 0;
-webkit-margin-end: 0;
-webkit-padding-start: 40px
}
li {
display: list-item;
text-align: -webkit-match-parent;
}
ul ul, ol ul {
list-style-type: circle
}
ol ol ul, ol ul ul, ul ol ul, ul ul ul {
list-style-type: square
}
dd {
display: block;
-webkit-margin-start: 40px
}
dl {
display: block;
-webkit-margin-before: 1__qem;
-webkit-margin-after: 1em;
-webkit-margin-start: 0;
-webkit-margin-end: 0;
}
dt {
display: block
}
ol ul, ul ol, ul ul, ol ol {
-webkit-margin-before: 0;
-webkit-margin-after: 0
}
/* form elements */
form {
display: block;
margin-top: 0__qem;
}
label {
cursor: default;
}
legend {
display: block;
-webkit-padding-start: 2px;
-webkit-padding-end: 2px;
border: none
}
fieldset {
display: block;
-webkit-margin-start: 2px;
-webkit-margin-end: 2px;
-webkit-padding-before: 0.35em;
-webkit-padding-start: 0.75em;
-webkit-padding-end: 0.75em;
-webkit-padding-after: 0.625em;
border: 2px groove ThreeDFace;
min-width: -webkit-min-content;
}
button {
-webkit-appearance: button;
}
/* Form controls don't go vertical. */
input, textarea, select, button, meter, progress {
-webkit-writing-mode: horizontal-tb !important;
}
input, textarea, select, button {
margin: 0__qem;
font: -webkit-small-control;
text-rendering: auto; /* FIXME: Remove when tabs work with optimizeLegibility. */
color: initial;
letter-spacing: normal;
word-spacing: normal;
line-height: normal;
text-transform: none;
text-indent: 0;
text-shadow: none;
display: inline-block;
text-align: start;
}
/* TODO: Add " i" to attribute matchers to support case-insensitive matching */
input[type="hidden"] {
display: none
}
input {
-webkit-appearance: textfield;
padding: 1px;
background-color: white;
border: 2px inset;
-webkit-rtl-ordering: logical;
-webkit-user-select: text;
cursor: auto;
}
input[type="search"] {
-webkit-appearance: searchfield;
box-sizing: border-box;
}
select {
border-radius: 5px;
}
textarea {
-webkit-appearance: textarea;
background-color: white;
border: 1px solid;
-webkit-rtl-ordering: logical;
-webkit-user-select: text;
flex-direction: column;
resize: auto;
cursor: auto;
padding: 2px;
white-space: pre-wrap;
word-wrap: break-word;
}
input[type="password"] {
-webkit-text-security: disc !important;
}
input[type="hidden"], input[type="image"], input[type="file"] {
-webkit-appearance: initial;
padding: initial;
background-color: initial;
border: initial;
}
input[type="file"] {
align-items: baseline;
color: inherit;
text-align: start !important;
}
input[type="radio"], input[type="checkbox"] {
margin: 3px 0.5ex;
padding: initial;
background-color: initial;
border: initial;
}
input[type="button"], input[type="submit"], input[type="reset"] {
-webkit-appearance: push-button;
-webkit-user-select: none;
white-space: pre
}
input[type="button"], input[type="submit"], input[type="reset"], button {
align-items: flex-start;
text-align: center;
cursor: default;
color: ButtonText;
padding: 2px 6px 3px 6px;
border: 2px outset ButtonFace;
background-color: ButtonFace;
box-sizing: border-box
}
input[type="range"] {
-webkit-appearance: slider-horizontal;
padding: initial;
border: initial;
margin: 2px;
color: #909090;
}
input[type="button"]:disabled, input[type="submit"]:disabled, input[type="reset"]:disabled,
button:disabled, select:disabled, optgroup:disabled, option:disabled,
select[disabled]>option {
color: GrayText
}
input[type="button"]:active, input[type="submit"]:active, input[type="reset"]:active, button:active {
border-style: inset
}
input[type="button"]:active:disabled, input[type="submit"]:active:disabled, input[type="reset"]:active:disabled, button:active:disabled {
border-style: outset
}
datalist {
display: none
}
area {
display: inline;
cursor: pointer;
}
param {
display: none
}
input[type="checkbox"] {
-webkit-appearance: checkbox;
box-sizing: border-box;
}
input[type="radio"] {
-webkit-appearance: radio;
box-sizing: border-box;
}
input[type="color"] {
-webkit-appearance: square-button;
width: 44px;
height: 23px;
background-color: ButtonFace;
/* Same as native_theme_base. */
border: 1px #a9a9a9 solid;
padding: 1px 2px;
}
input[type="color"][list] {
-webkit-appearance: menulist;
width: 88px;
height: 23px
}
select {
-webkit-appearance: menulist;
box-sizing: border-box;
align-items: center;
border: 1px solid;
white-space: pre;
-webkit-rtl-ordering: logical;
color: black;
background-color: white;
cursor: default;
}
optgroup {
font-weight: bolder;
display: block;
}
option {
font-weight: normal;
display: block;
padding: 0 2px 1px 2px;
white-space: pre;
min-height: 1.2em;
}
output {
display: inline;
}
/* meter */
meter {
-webkit-appearance: meter;
box-sizing: border-box;
display: inline-block;
height: 1em;
width: 5em;
vertical-align: -0.2em;
}
/* progress */
progress {
-webkit-appearance: progress-bar;
box-sizing: border-box;
display: inline-block;
height: 1em;
width: 10em;
vertical-align: -0.2em;
}
/* inline elements */
u, ins {
text-decoration: underline
}
strong, b {
font-weight: bold
}
i, cite, em, var, address, dfn {
font-style: italic
}
tt, code, kbd, samp {
font-family: monospace
}
pre, xmp, plaintext, listing {
display: block;
font-family: monospace;
white-space: pre;
margin: 1__qem 0
}
mark {
background-color: yellow;
color: black
}
big {
font-size: larger
}
small {
font-size: smaller
}
s, strike, del {
text-decoration: line-through
}
sub {
vertical-align: sub;
font-size: smaller
}
sup {
vertical-align: super;
font-size: smaller
}
nobr {
white-space: nowrap
}
/* states */
:focus {
outline: auto 5px -webkit-focus-ring-color
}
/* Read-only text fields do not show a focus ring but do still receive focus */
html:focus, body:focus, input[readonly]:focus {
outline: none
}
embed:focus, iframe:focus, object:focus {
outline: none
}
input:focus, textarea:focus, select:focus {
outline-offset: -2px
}
input[type="button"]:focus,
input[type="checkbox"]:focus,
input[type="file"]:focus,
input[type="hidden"]:focus,
input[type="image"]:focus,
input[type="radio"]:focus,
input[type="reset"]:focus,
input[type="search"]:focus,
input[type="submit"]:focus {
outline-offset: 0
}
/* HTML5 ruby elements */
ruby, rt {
text-indent: 0; /* blocks used for ruby rendering should not trigger this */
}
rt {
line-height: normal;
-webkit-text-emphasis: none;
}
ruby > rt {
display: block;
font-size: 50%;
text-align: start;
}
ruby > rp {
display: none;
}
/* other elements */
noframes {
display: none
}
frameset, frame {
display: block
}
frameset {
border-color: inherit
}
iframe {
border: 2px inset
}
details {
display: block
}
summary {
display: block
}
template {
display: none
}
bdi, output {
unicode-bidi: -webkit-isolate;
}
bdo {
unicode-bidi: bidi-override;
}
textarea[dir=auto] {
unicode-bidi: -webkit-plaintext;
}
dialog:not([open]) {
display: none
}
dialog {
position: absolute;
left: 0;
right: 0;
width: -webkit-fit-content;
height: -webkit-fit-content;
margin: auto;
border: solid;
padding: 1em;
background: white;
color: black
}
/* noscript is handled internally, as it depends on settings. */
`;

292
node_modules/jsdom/lib/jsdom/browser/js-globals.json generated vendored Normal file
View file

@ -0,0 +1,292 @@
{
"Object": {
"writable": true,
"enumerable": false,
"configurable": true
},
"Function": {
"writable": true,
"enumerable": false,
"configurable": true
},
"Array": {
"writable": true,
"enumerable": false,
"configurable": true
},
"Number": {
"writable": true,
"enumerable": false,
"configurable": true
},
"parseFloat": {
"writable": true,
"enumerable": false,
"configurable": true
},
"parseInt": {
"writable": true,
"enumerable": false,
"configurable": true
},
"Infinity": {
"writable": false,
"enumerable": false,
"configurable": false
},
"NaN": {
"writable": false,
"enumerable": false,
"configurable": false
},
"undefined": {
"writable": false,
"enumerable": false,
"configurable": false
},
"Boolean": {
"writable": true,
"enumerable": false,
"configurable": true
},
"String": {
"writable": true,
"enumerable": false,
"configurable": true
},
"Symbol": {
"writable": true,
"enumerable": false,
"configurable": true
},
"Date": {
"writable": true,
"enumerable": false,
"configurable": true
},
"Promise": {
"writable": true,
"enumerable": false,
"configurable": true
},
"RegExp": {
"writable": true,
"enumerable": false,
"configurable": true
},
"Error": {
"writable": true,
"enumerable": false,
"configurable": true
},
"EvalError": {
"writable": true,
"enumerable": false,
"configurable": true
},
"RangeError": {
"writable": true,
"enumerable": false,
"configurable": true
},
"ReferenceError": {
"writable": true,
"enumerable": false,
"configurable": true
},
"SyntaxError": {
"writable": true,
"enumerable": false,
"configurable": true
},
"TypeError": {
"writable": true,
"enumerable": false,
"configurable": true
},
"URIError": {
"writable": true,
"enumerable": false,
"configurable": true
},
"globalThis": {
"writable": true,
"enumerable": false,
"configurable": true
},
"JSON": {
"writable": true,
"enumerable": false,
"configurable": true
},
"Math": {
"writable": true,
"enumerable": false,
"configurable": true
},
"Intl": {
"writable": true,
"enumerable": false,
"configurable": true
},
"ArrayBuffer": {
"writable": true,
"enumerable": false,
"configurable": true
},
"Uint8Array": {
"writable": true,
"enumerable": false,
"configurable": true
},
"Int8Array": {
"writable": true,
"enumerable": false,
"configurable": true
},
"Uint16Array": {
"writable": true,
"enumerable": false,
"configurable": true
},
"Int16Array": {
"writable": true,
"enumerable": false,
"configurable": true
},
"Uint32Array": {
"writable": true,
"enumerable": false,
"configurable": true
},
"Int32Array": {
"writable": true,
"enumerable": false,
"configurable": true
},
"Float32Array": {
"writable": true,
"enumerable": false,
"configurable": true
},
"Float64Array": {
"writable": true,
"enumerable": false,
"configurable": true
},
"Uint8ClampedArray": {
"writable": true,
"enumerable": false,
"configurable": true
},
"BigUint64Array": {
"writable": true,
"enumerable": false,
"configurable": true
},
"BigInt64Array": {
"writable": true,
"enumerable": false,
"configurable": true
},
"DataView": {
"writable": true,
"enumerable": false,
"configurable": true
},
"Map": {
"writable": true,
"enumerable": false,
"configurable": true
},
"BigInt": {
"writable": true,
"enumerable": false,
"configurable": true
},
"Set": {
"writable": true,
"enumerable": false,
"configurable": true
},
"WeakMap": {
"writable": true,
"enumerable": false,
"configurable": true
},
"WeakSet": {
"writable": true,
"enumerable": false,
"configurable": true
},
"Proxy": {
"writable": true,
"enumerable": false,
"configurable": true
},
"Reflect": {
"writable": true,
"enumerable": false,
"configurable": true
},
"decodeURI": {
"writable": true,
"enumerable": false,
"configurable": true
},
"decodeURIComponent": {
"writable": true,
"enumerable": false,
"configurable": true
},
"encodeURI": {
"writable": true,
"enumerable": false,
"configurable": true
},
"encodeURIComponent": {
"writable": true,
"enumerable": false,
"configurable": true
},
"escape": {
"writable": true,
"enumerable": false,
"configurable": true
},
"unescape": {
"writable": true,
"enumerable": false,
"configurable": true
},
"eval": {
"writable": true,
"enumerable": false,
"configurable": true
},
"isFinite": {
"writable": true,
"enumerable": false,
"configurable": true
},
"isNaN": {
"writable": true,
"enumerable": false,
"configurable": true
},
"SharedArrayBuffer": {
"writable": true,
"enumerable": false,
"configurable": true
},
"Atomics": {
"writable": true,
"enumerable": false,
"configurable": true
},
"WebAssembly": {
"writable": true,
"enumerable": false,
"configurable": true
}
}

View file

@ -0,0 +1,13 @@
"use strict";
module.exports = function (nameForErrorMessage, window) {
if (!window) {
// Do nothing for window-less documents.
return;
}
const error = new Error(`Not implemented: ${nameForErrorMessage}`);
error.type = "not implemented";
window._virtualConsole.emit("jsdomError", error);
};

222
node_modules/jsdom/lib/jsdom/browser/parser/html.js generated vendored Normal file
View file

@ -0,0 +1,222 @@
"use strict";
const parse5 = require("parse5");
const { createElement } = require("../../living/helpers/create-element");
const DocumentType = require("../../living/generated/DocumentType");
const DocumentFragment = require("../../living/generated/DocumentFragment");
const Text = require("../../living/generated/Text");
const Comment = require("../../living/generated/Comment");
const attributes = require("../../living/attributes");
const nodeTypes = require("../../living/node-type");
const serializationAdapter = require("../../living/domparsing/parse5-adapter-serialization");
const {
customElementReactionsStack, invokeCEReactions, lookupCEDefinition
} = require("../../living/helpers/custom-elements");
// Horrible monkey-patch to implement https://github.com/inikulin/parse5/issues/237 and
// https://github.com/inikulin/parse5/issues/285.
const OpenElementStack = require("parse5/lib/parser/open-element-stack");
const openElementStackOriginalPush = OpenElementStack.prototype.push;
OpenElementStack.prototype.push = function (...args) {
openElementStackOriginalPush.apply(this, args);
this.treeAdapter._currentElement = this.current;
const after = this.items[this.stackTop];
if (after._pushedOnStackOfOpenElements) {
after._pushedOnStackOfOpenElements();
}
};
const openElementStackOriginalPop = OpenElementStack.prototype.pop;
OpenElementStack.prototype.pop = function (...args) {
const before = this.items[this.stackTop];
openElementStackOriginalPop.apply(this, args);
this.treeAdapter._currentElement = this.current;
if (before._poppedOffStackOfOpenElements) {
before._poppedOffStackOfOpenElements();
}
};
class JSDOMParse5Adapter {
constructor(documentImpl, options = {}) {
this._documentImpl = documentImpl;
this._globalObject = documentImpl._globalObject;
this._fragment = options.fragment || false;
// Since the createElement hook doesn't provide the parent element, we keep track of this using _currentElement:
// https://github.com/inikulin/parse5/issues/285. See above horrible monkey-patch for how this is maintained.
this._currentElement = undefined;
}
_ownerDocument() {
const { _currentElement } = this;
// The _currentElement is undefined when parsing elements at the root of the document.
if (_currentElement) {
return _currentElement.localName === "template" ?
_currentElement.content._ownerDocument :
_currentElement._ownerDocument;
}
return this._documentImpl;
}
createDocument() {
// parse5's model assumes that parse(html) will call into here to create the new Document, then return it. However,
// jsdom's model assumes we can create a Window (and through that create an empty Document), do some other setup
// stuff, and then parse, stuffing nodes into that Document as we go. So to adapt between these two models, we just
// return the already-created Document when asked by parse5 to "create" a Document.
return this._documentImpl;
}
createDocumentFragment() {
const ownerDocument = this._ownerDocument();
return DocumentFragment.createImpl(this._globalObject, [], { ownerDocument });
}
// https://html.spec.whatwg.org/#create-an-element-for-the-token
createElement(localName, namespace, attrs) {
const ownerDocument = this._ownerDocument();
const isAttribute = attrs.find(attr => attr.name === "is");
const isValue = isAttribute ? isAttribute.value : null;
const definition = lookupCEDefinition(ownerDocument, namespace, localName);
let willExecuteScript = false;
if (definition !== null && !this._fragment) {
willExecuteScript = true;
}
if (willExecuteScript) {
ownerDocument._throwOnDynamicMarkupInsertionCounter++;
customElementReactionsStack.push([]);
}
const element = createElement(ownerDocument, localName, namespace, null, isValue, willExecuteScript);
this.adoptAttributes(element, attrs);
if (willExecuteScript) {
const queue = customElementReactionsStack.pop();
invokeCEReactions(queue);
ownerDocument._throwOnDynamicMarkupInsertionCounter--;
}
if ("_parserInserted" in element) {
element._parserInserted = true;
}
return element;
}
createCommentNode(data) {
const ownerDocument = this._ownerDocument();
return Comment.createImpl(this._globalObject, [], { data, ownerDocument });
}
appendChild(parentNode, newNode) {
parentNode._append(newNode);
}
insertBefore(parentNode, newNode, referenceNode) {
parentNode._insert(newNode, referenceNode);
}
setTemplateContent(templateElement, contentFragment) {
// This code makes the glue between jsdom and parse5 HTMLTemplateElement parsing:
//
// * jsdom during the construction of the HTMLTemplateElement (for example when create via
// `document.createElement("template")`), creates a DocumentFragment and set it into _templateContents.
// * parse5 when parsing a <template> tag creates an HTMLTemplateElement (`createElement` adapter hook) and also
// create a DocumentFragment (`createDocumentFragment` adapter hook).
//
// At this point we now have to replace the one created in jsdom with one created by parse5.
const { _ownerDocument, _host } = templateElement._templateContents;
contentFragment._ownerDocument = _ownerDocument;
contentFragment._host = _host;
templateElement._templateContents = contentFragment;
}
setDocumentType(document, name, publicId, systemId) {
const ownerDocument = this._ownerDocument();
const documentType = DocumentType.createImpl(this._globalObject, [], { name, publicId, systemId, ownerDocument });
document._append(documentType);
}
setDocumentMode(document, mode) {
// TODO: the rest of jsdom ignores this
document._mode = mode;
}
detachNode(node) {
node.remove();
}
insertText(parentNode, text) {
const { lastChild } = parentNode;
if (lastChild && lastChild.nodeType === nodeTypes.TEXT_NODE) {
lastChild.data += text;
} else {
const ownerDocument = this._ownerDocument();
const textNode = Text.createImpl(this._globalObject, [], { data: text, ownerDocument });
parentNode._append(textNode);
}
}
insertTextBefore(parentNode, text, referenceNode) {
const { previousSibling } = referenceNode;
if (previousSibling && previousSibling.nodeType === nodeTypes.TEXT_NODE) {
previousSibling.data += text;
} else {
const ownerDocument = this._ownerDocument();
const textNode = Text.createImpl(this._globalObject, [], { data: text, ownerDocument });
parentNode._append(textNode, referenceNode);
}
}
adoptAttributes(element, attrs) {
for (const attr of attrs) {
const prefix = attr.prefix === "" ? null : attr.prefix;
attributes.setAttributeValue(element, attr.name, attr.value, prefix, attr.namespace);
}
}
}
// Assign shared adapters with serializer.
Object.assign(JSDOMParse5Adapter.prototype, serializationAdapter);
function parseFragment(markup, contextElement) {
const ownerDocument = contextElement.localName === "template" ?
contextElement.content._ownerDocument :
contextElement._ownerDocument;
const config = Object.assign({}, ownerDocument._parseOptions, {
treeAdapter: new JSDOMParse5Adapter(ownerDocument, {
fragment: true
})
});
return parse5.parseFragment(contextElement, markup, config);
}
function parseIntoDocument(markup, ownerDocument) {
const config = Object.assign({}, ownerDocument._parseOptions, {
treeAdapter: new JSDOMParse5Adapter(ownerDocument)
});
return parse5.parse(markup, config);
}
module.exports = {
parseFragment,
parseIntoDocument
};

37
node_modules/jsdom/lib/jsdom/browser/parser/index.js generated vendored Normal file
View file

@ -0,0 +1,37 @@
"use strict";
const xmlParser = require("./xml");
const htmlParser = require("./html");
// https://w3c.github.io/DOM-Parsing/#dfn-fragment-parsing-algorithm
function parseFragment(markup, contextElement) {
const { _parsingMode } = contextElement._ownerDocument;
let parseAlgorithm;
if (_parsingMode === "html") {
parseAlgorithm = htmlParser.parseFragment;
} else if (_parsingMode === "xml") {
parseAlgorithm = xmlParser.parseFragment;
}
// Note: HTML and XML fragment parsing algorithm already return a document fragments; no need to do steps 3 and 4
return parseAlgorithm(markup, contextElement);
}
function parseIntoDocument(markup, ownerDocument) {
const { _parsingMode } = ownerDocument;
let parseAlgorithm;
if (_parsingMode === "html") {
parseAlgorithm = htmlParser.parseIntoDocument;
} else if (_parsingMode === "xml") {
parseAlgorithm = xmlParser.parseIntoDocument;
}
return parseAlgorithm(markup, ownerDocument);
}
module.exports = {
parseFragment,
parseIntoDocument
};

205
node_modules/jsdom/lib/jsdom/browser/parser/xml.js generated vendored Normal file
View file

@ -0,0 +1,205 @@
"use strict";
const { SaxesParser } = require("saxes");
const DOMException = require("domexception/webidl2js-wrapper");
const { createElement } = require("../../living/helpers/create-element");
const DocumentFragment = require("../../living/generated/DocumentFragment");
const DocumentType = require("../../living/generated/DocumentType");
const CDATASection = require("../../living/generated/CDATASection");
const Comment = require("../../living/generated/Comment");
const ProcessingInstruction = require("../../living/generated/ProcessingInstruction");
const Text = require("../../living/generated/Text");
const attributes = require("../../living/attributes");
const { HTML_NS } = require("../../living/helpers/namespaces");
const HTML5_DOCTYPE = /<!doctype html>/i;
const PUBLIC_DOCTYPE = /<!doctype\s+([^\s]+)\s+public\s+"([^"]+)"\s+"([^"]+)"/i;
const SYSTEM_DOCTYPE = /<!doctype\s+([^\s]+)\s+system\s+"([^"]+)"/i;
const CUSTOM_NAME_DOCTYPE = /<!doctype\s+([^\s>]+)/i;
function parseDocType(globalObject, ownerDocument, html) {
if (HTML5_DOCTYPE.test(html)) {
return createDocumentType(globalObject, ownerDocument, "html", "", "");
}
const publicPieces = PUBLIC_DOCTYPE.exec(html);
if (publicPieces) {
return createDocumentType(globalObject, ownerDocument, publicPieces[1], publicPieces[2], publicPieces[3]);
}
const systemPieces = SYSTEM_DOCTYPE.exec(html);
if (systemPieces) {
return createDocumentType(globalObject, ownerDocument, systemPieces[1], "", systemPieces[2]);
}
const namePiece = CUSTOM_NAME_DOCTYPE.exec(html)[1] || "html";
return createDocumentType(globalObject, ownerDocument, namePiece, "", "");
}
function createDocumentType(globalObject, ownerDocument, name, publicId, systemId) {
return DocumentType.createImpl(globalObject, [], { ownerDocument, name, publicId, systemId });
}
function isHTMLTemplateElement(element) {
return element.tagName === "template" && element.namespaceURI === HTML_NS;
}
function createParser(rootNode, globalObject, saxesOptions) {
const parser = new SaxesParser({
...saxesOptions,
// Browsers always have namespace support.
xmlns: true,
// We force the parser to treat all documents (even documents declaring themselves to be XML 1.1 documents) as XML
// 1.0 documents. See https://github.com/jsdom/jsdom/issues/2677 for a discussion of the stakes.
defaultXMLVersion: "1.0",
forceXMLVersion: true
});
const openStack = [rootNode];
function getOwnerDocument() {
const currentElement = openStack[openStack.length - 1];
return isHTMLTemplateElement(currentElement) ?
currentElement._templateContents._ownerDocument :
currentElement._ownerDocument;
}
function appendChild(child) {
const parentElement = openStack[openStack.length - 1];
if (isHTMLTemplateElement(parentElement)) {
parentElement._templateContents._insert(child, null);
} else {
parentElement._insert(child, null);
}
}
parser.on("text", saxesOptions.fragment ?
// In a fragment, all text events produced by saxes must result in a text
// node.
data => {
const ownerDocument = getOwnerDocument();
appendChild(Text.createImpl(globalObject, [], { data, ownerDocument }));
} :
// When parsing a whole document, we must ignore those text nodes that are
// produced outside the root element. Saxes produces events for them,
// but DOM trees do not record text outside the root element.
data => {
if (openStack.length > 1) {
const ownerDocument = getOwnerDocument();
appendChild(Text.createImpl(globalObject, [], { data, ownerDocument }));
}
});
parser.on("cdata", data => {
const ownerDocument = getOwnerDocument();
appendChild(CDATASection.createImpl(globalObject, [], { data, ownerDocument }));
});
parser.on("opentag", tag => {
const { local: tagLocal, attributes: tagAttributes } = tag;
const ownerDocument = getOwnerDocument();
const tagNamespace = tag.uri === "" ? null : tag.uri;
const tagPrefix = tag.prefix === "" ? null : tag.prefix;
const isValue = tagAttributes.is === undefined ? null : tagAttributes.is.value;
const elem = createElement(ownerDocument, tagLocal, tagNamespace, tagPrefix, isValue, true);
// We mark a script element as "parser-inserted", which prevents it from
// being immediately executed.
if (tagLocal === "script" && tagNamespace === HTML_NS) {
elem._parserInserted = true;
}
for (const key of Object.keys(tagAttributes)) {
const { prefix, local, uri, value } = tagAttributes[key];
attributes.setAttributeValue(
elem, local, value, prefix === "" ? null : prefix,
uri === "" ? null : uri
);
}
appendChild(elem);
openStack.push(elem);
});
parser.on("closetag", () => {
const elem = openStack.pop();
// Once a script is populated, we can execute it.
if (elem.localName === "script" && elem.namespaceURI === HTML_NS) {
elem._eval();
}
});
parser.on("comment", data => {
const ownerDocument = getOwnerDocument();
appendChild(Comment.createImpl(globalObject, [], { data, ownerDocument }));
});
parser.on("processinginstruction", ({ target, body }) => {
const ownerDocument = getOwnerDocument();
appendChild(ProcessingInstruction.createImpl(globalObject, [], { target, data: body, ownerDocument }));
});
parser.on("doctype", dt => {
const ownerDocument = getOwnerDocument();
appendChild(parseDocType(globalObject, ownerDocument, `<!doctype ${dt}>`));
const entityMatcher = /<!ENTITY ([^ ]+) "([^"]+)">/g;
let result;
while ((result = entityMatcher.exec(dt))) {
const [, name, value] = result;
if (!(name in parser.ENTITIES)) {
parser.ENTITIES[name] = value;
}
}
});
parser.on("error", err => {
throw DOMException.create(globalObject, [err.message, "SyntaxError"]);
});
return parser;
}
function parseFragment(markup, contextElement) {
const { _globalObject, _ownerDocument } = contextElement;
const fragment = DocumentFragment.createImpl(_globalObject, [], { ownerDocument: _ownerDocument });
// Only parseFragment needs resolvePrefix per the saxes documentation:
// https://github.com/lddubeau/saxes#parsing-xml-fragments
const parser = createParser(fragment, _globalObject, {
fragment: true,
resolvePrefix(prefix) {
// saxes wants undefined as the return value if the prefix is not defined, not null.
return contextElement.lookupNamespaceURI(prefix) || undefined;
}
});
parser.write(markup).close();
return fragment;
}
function parseIntoDocument(markup, ownerDocument) {
const { _globalObject } = ownerDocument;
const parser = createParser(ownerDocument, _globalObject, {
fileName: ownerDocument.location && ownerDocument.location.href
});
parser.write(markup).close();
return ownerDocument;
}
module.exports = {
parseFragment,
parseIntoDocument
};

View file

@ -0,0 +1,114 @@
"use strict";
class QueueItem {
constructor(onLoad, onError, dependentItem) {
this.onLoad = onLoad;
this.onError = onError;
this.data = null;
this.error = null;
this.dependentItem = dependentItem;
}
}
/**
* AsyncResourceQueue is the queue in charge of run the async scripts
* and notify when they finish.
*/
module.exports = class AsyncResourceQueue {
constructor() {
this.items = new Set();
this.dependentItems = new Set();
}
count() {
return this.items.size + this.dependentItems.size;
}
_notify() {
if (this._listener) {
this._listener();
}
}
_check(item) {
let promise;
if (item.onError && item.error) {
promise = item.onError(item.error);
} else if (item.onLoad && item.data) {
promise = item.onLoad(item.data);
}
promise
.then(() => {
this.items.delete(item);
this.dependentItems.delete(item);
if (this.count() === 0) {
this._notify();
}
});
}
setListener(listener) {
this._listener = listener;
}
push(request, onLoad, onError, dependentItem) {
const q = this;
const item = new QueueItem(onLoad, onError, dependentItem);
q.items.add(item);
return request
.then(data => {
item.data = data;
if (dependentItem && !dependentItem.finished) {
q.dependentItems.add(item);
return q.items.delete(item);
}
if (onLoad) {
return q._check(item);
}
q.items.delete(item);
if (q.count() === 0) {
q._notify();
}
return null;
})
.catch(err => {
item.error = err;
if (dependentItem && !dependentItem.finished) {
q.dependentItems.add(item);
return q.items.delete(item);
}
if (onError) {
return q._check(item);
}
q.items.delete(item);
if (q.count() === 0) {
q._notify();
}
return null;
});
}
notifyItem(syncItem) {
for (const item of this.dependentItems) {
if (item.dependentItem === syncItem) {
this._check(item);
}
}
}
};

View file

@ -0,0 +1,8 @@
"use strict";
const ResourceLoader = require("./resource-loader.js");
module.exports = class NoOpResourceLoader extends ResourceLoader {
fetch() {
return null;
}
};

View file

@ -0,0 +1,95 @@
"use strict";
const idlUtils = require("../../living/generated/utils");
const { fireAnEvent } = require("../../living/helpers/events");
module.exports = class PerDocumentResourceLoader {
constructor(document) {
this._document = document;
this._defaultEncoding = document._encoding;
this._resourceLoader = document._defaultView ? document._defaultView._resourceLoader : null;
this._requestManager = document._requestManager;
this._queue = document._queue;
this._deferQueue = document._deferQueue;
this._asyncQueue = document._asyncQueue;
}
fetch(url, { element, onLoad, onError }) {
const request = this._resourceLoader.fetch(url, {
cookieJar: this._document._cookieJar,
element: idlUtils.wrapperForImpl(element),
referrer: this._document.URL
});
if (request === null) {
return null;
}
this._requestManager.add(request);
const onErrorWrapped = error => {
this._requestManager.remove(request);
if (onError) {
onError(error);
}
fireAnEvent("error", element);
const err = new Error(`Could not load ${element.localName}: "${url}"`);
err.type = "resource loading";
err.detail = error;
this._document._defaultView._virtualConsole.emit("jsdomError", err);
return Promise.resolve();
};
const onLoadWrapped = data => {
this._requestManager.remove(request);
this._addCookies(url, request.response ? request.response.headers : {});
try {
const result = onLoad ? onLoad(data) : undefined;
return Promise.resolve(result)
.then(() => {
fireAnEvent("load", element);
return Promise.resolve();
})
.catch(err => {
return onErrorWrapped(err);
});
} catch (err) {
return onErrorWrapped(err);
}
};
if (element.localName === "script" && element.hasAttributeNS(null, "async")) {
this._asyncQueue.push(request, onLoadWrapped, onErrorWrapped, this._queue.getLastScript());
} else if (element.localName === "script" && element.hasAttributeNS(null, "defer")) {
this._deferQueue.push(request, onLoadWrapped, onErrorWrapped, false, element);
} else {
this._queue.push(request, onLoadWrapped, onErrorWrapped, false, element);
}
return request;
}
_addCookies(url, headers) {
let cookies = headers["set-cookie"];
if (!cookies) {
return;
}
if (!Array.isArray(cookies)) {
cookies = [cookies];
}
cookies.forEach(cookie => {
this._document._cookieJar.setCookieSync(cookie, url, { http: true, ignoreError: true });
});
}
};

View file

@ -0,0 +1,33 @@
"use strict";
/**
* Manage all the request and it is able to abort
* all pending request.
*/
module.exports = class RequestManager {
constructor() {
this.openedRequests = [];
}
add(req) {
this.openedRequests.push(req);
}
remove(req) {
const idx = this.openedRequests.indexOf(req);
if (idx !== -1) {
this.openedRequests.splice(idx, 1);
}
}
close() {
for (const openedRequest of this.openedRequests) {
openedRequest.abort();
}
this.openedRequests = [];
}
size() {
return this.openedRequests.length;
}
};

View file

@ -0,0 +1,127 @@
"use strict";
const fs = require("fs");
const { parseURL } = require("whatwg-url");
const dataURLFromRecord = require("data-urls").fromURLRecord;
const request = require("request-promise-native");
const wrapCookieJarForRequest = require("../../living/helpers/wrap-cookie-jar-for-request");
const packageVersion = require("../../../../package.json").version;
const IS_BROWSER = Object.prototype.toString.call(process) !== "[object process]";
module.exports = class ResourceLoader {
constructor({
strictSSL = true,
proxy = undefined,
userAgent = `Mozilla/5.0 (${process.platform || "unknown OS"}) AppleWebKit/537.36 ` +
`(KHTML, like Gecko) jsdom/${packageVersion}`
} = {}) {
this._strictSSL = strictSSL;
this._proxy = proxy;
this._userAgent = userAgent;
}
_readDataURL(urlRecord) {
const dataURL = dataURLFromRecord(urlRecord);
let timeoutId;
const promise = new Promise(resolve => {
timeoutId = setTimeout(resolve, 0, dataURL.body);
});
promise.abort = () => {
if (timeoutId !== undefined) {
clearTimeout(timeoutId);
}
};
return promise;
}
_readFile(filePath) {
let readableStream;
let abort; // Native Promises doesn't have an "abort" method.
/*
* Creating a promise for two reason:
* 1. fetch always return a promise.
* 2. We need to add an abort handler.
*/
const promise = new Promise((resolve, reject) => {
readableStream = fs.createReadStream(filePath);
let data = Buffer.alloc(0);
abort = reject;
readableStream.on("error", reject);
readableStream.on("data", chunk => {
data = Buffer.concat([data, chunk]);
});
readableStream.on("end", () => {
resolve(data);
});
});
promise.abort = () => {
readableStream.destroy();
const error = new Error("request canceled by user");
error.isAbortError = true;
abort(error);
};
return promise;
}
_getRequestOptions({ cookieJar, referrer, accept = "*/*" }) {
const requestOptions = {
encoding: null,
gzip: true,
jar: wrapCookieJarForRequest(cookieJar),
strictSSL: this._strictSSL,
proxy: this._proxy,
forever: true,
headers: {
"User-Agent": this._userAgent,
"Accept-Language": "en",
Accept: accept
}
};
if (referrer && !IS_BROWSER) {
requestOptions.headers.referer = referrer;
}
return requestOptions;
}
fetch(urlString, options = {}) {
const url = parseURL(urlString);
if (!url) {
return Promise.reject(new Error(`Tried to fetch invalid URL ${urlString}`));
}
switch (url.scheme) {
case "data": {
return this._readDataURL(url);
}
case "http":
case "https": {
const requestOptions = this._getRequestOptions(options);
return request(urlString, requestOptions);
}
case "file": {
// TODO: Improve the URL => file algorithm. See https://github.com/jsdom/jsdom/pull/2279#discussion_r199977987
const filePath = urlString
.replace(/^file:\/\//, "")
.replace(/^\/([a-z]):\//i, "$1:/")
.replace(/%20/g, " ");
return this._readFile(filePath);
}
default: {
return Promise.reject(new Error(`Tried to fetch URL ${urlString} with invalid scheme ${url.scheme}`));
}
}
}
};

View file

@ -0,0 +1,142 @@
"use strict";
/**
* Queue for all the resources to be download except async scripts.
* Async scripts have their own queue AsyncResourceQueue.
*/
module.exports = class ResourceQueue {
constructor({ paused, asyncQueue } = {}) {
this.paused = Boolean(paused);
this._asyncQueue = asyncQueue;
}
getLastScript() {
let head = this.tail;
while (head) {
if (head.isScript) {
return head;
}
head = head.prev;
}
return null;
}
_moreScripts() {
let found = false;
let head = this.tail;
while (head && !found) {
found = head.isScript;
head = head.prev;
}
return found;
}
_notify() {
if (this._listener) {
this._listener();
}
}
setListener(listener) {
this._listener = listener;
}
push(request, onLoad, onError, keepLast, element) {
const isScript = element ? element.localName === "script" : false;
if (!request) {
if (isScript && !this._moreScripts()) {
return onLoad();
}
request = new Promise(resolve => resolve());
}
const q = this;
const item = {
isScript,
err: null,
element,
fired: false,
data: null,
keepLast,
prev: q.tail,
check() {
if (!q.paused && !this.prev && this.fired) {
let promise;
if (this.err && onError) {
promise = onError(this.err);
}
if (!this.err && onLoad) {
promise = onLoad(this.data);
}
Promise.resolve(promise)
.then(() => {
if (this.next) {
this.next.prev = null;
this.next.check();
} else { // q.tail===this
q.tail = null;
q._notify();
}
this.finished = true;
if (q._asyncQueue) {
q._asyncQueue.notifyItem(this);
}
});
}
}
};
if (q.tail) {
if (q.tail.keepLast) {
// if the tail is the load event in document and we receive a new element to load
// we should add this new request before the load event.
if (q.tail.prev) {
q.tail.prev.next = item;
}
item.prev = q.tail.prev;
q.tail.prev = item;
item.next = q.tail;
} else {
q.tail.next = item;
q.tail = item;
}
} else {
q.tail = item;
}
return request
.then(data => {
item.fired = 1;
item.data = data;
item.check();
})
.catch(err => {
item.fired = true;
item.err = err;
item.check();
});
}
resume() {
if (!this.paused) {
return;
}
this.paused = false;
let head = this.tail;
while (head && head.prev) {
head = head.prev;
}
if (head) {
head.check();
}
}
};