Added logging, changed some directory structure

This commit is contained in:
2018-01-13 21:33:40 -05:00
parent f079a5f067
commit 8e72ffb917
73656 changed files with 35284 additions and 53718 deletions

View File

@@ -0,0 +1,53 @@
"use strict";
const internalQuerySelector = require("./selectors").querySelector;
const whatwgURL = require("whatwg-url");
exports.documentBaseURL = document => {
// https://html.spec.whatwg.org/multipage/infrastructure.html#document-base-url
const firstBase = internalQuerySelector(document, "base[href]");
const fallbackBaseURL = exports.fallbackBaseURL(document);
if (firstBase === null) {
return fallbackBaseURL;
}
return frozenBaseURL(firstBase, fallbackBaseURL);
};
exports.documentBaseURLSerialized = document => {
return whatwgURL.serializeURL(exports.documentBaseURL(document));
};
exports.fallbackBaseURL = document => {
// https://html.spec.whatwg.org/multipage/infrastructure.html#fallback-base-url
// Unimplemented: <iframe srcdoc>
if (document.URL === "about:blank" && document._defaultView &&
document._defaultView._parent !== document._defaultView) {
return exports.documentBaseURL(document._defaultView._parent._document);
}
return document._URL;
};
exports.parseURLToResultingURLRecord = (url, document) => {
// https://html.spec.whatwg.org/#resolve-a-url
// Encoding stuff ignored; always UTF-8 for us, for now.
const baseURL = exports.documentBaseURL(document);
return whatwgURL.parseURL(url, { baseURL });
// This returns the resulting URL record; to get the resulting URL string, just serialize it.
};
function frozenBaseURL(baseElement, fallbackBaseURL) {
// https://html.spec.whatwg.org/multipage/semantics.html#frozen-base-url
// The spec is eager (setting the frozen base URL when things change); we are lazy (getting it when we need to)
const baseHrefAttribute = baseElement.getAttribute("href");
const result = whatwgURL.parseURL(baseHrefAttribute, { baseURL: fallbackBaseURL });
return result === "failure" ? fallbackBaseURL : result;
}

View File

@@ -0,0 +1,74 @@
"use strict";
const nodeType = require("../node-type.js");
const FocusEvent = require("../generated/FocusEvent.js");
const idlUtils = require("../generated/utils.js");
const isDisabled = require("./form-controls.js").isDisabled;
const focusableFormElements = new Set(["input", "select", "textarea", "button"]);
// https://html.spec.whatwg.org/multipage/interaction.html#focusable-area, but also some of
// https://html.spec.whatwg.org/multipage/interaction.html#focusing-steps: e.g., Documents are not actually focusable
// areas, but their viewports are, and the first step of the latter algorithm translates Documents to their viewports.
// And also https://html.spec.whatwg.org/multipage/interaction.html#specially-focusable!
exports.isFocusableAreaElement = elImpl => {
if (!elImpl._ownerDocument._defaultView && !elImpl._defaultView) {
return false;
}
if (elImpl._nodeType === nodeType.DOCUMENT_NODE) {
return true;
}
if (!Number.isNaN(parseInt(elImpl.getAttribute("tabindex")))) {
return true;
}
if (elImpl._namespaceURI === "http://www.w3.org/1999/xhtml") {
if (elImpl._localName === "iframe") {
return true;
}
if (elImpl._localName === "a" && elImpl.hasAttribute("href")) {
return true;
}
if (focusableFormElements.has(elImpl._localName) && !isDisabled(elImpl)) {
if (elImpl._localName === "input" && elImpl.type === "hidden") {
return false;
}
return true;
}
}
return false;
};
// https://html.spec.whatwg.org/multipage/interaction.html#fire-a-focus-event plus the steps of
// https://html.spec.whatwg.org/multipage/interaction.html#focus-update-steps that adjust Documents to Windows
exports.fireFocusEventWithTargetAdjustment = (name, target, relatedTarget) => {
if (target === null) {
// E.g. firing blur with nothing previously focused.
return;
}
const event = FocusEvent.createImpl(
[name, {
bubbles: false,
cancelable: false,
relatedTarget,
view: target._ownerDocument._defaultView,
detail: 0
}],
{
isTrusted: true
}
);
if (target._defaultView) {
target = idlUtils.implForWrapper(target._defaultView);
}
// _dispatch allows setting isTrusted
target._dispatch(event);
};

View File

@@ -0,0 +1,43 @@
"use strict";
const submittableLocalNames = new Set(["button", "input", "keygen", "object", "select", "textarea"]);
exports.isDisabled = formControl => {
if (formControl.localName === "button" || formControl.localName === "input" || formControl.localName === "select" ||
formControl.localName === "textarea") {
if (formControl.hasAttribute("disabled")) {
return true;
}
}
let e = formControl.parentNode;
while (e) {
if (e.localName === "fieldset" && e.hasAttribute("disabled")) {
const firstLegendElementChild = e.querySelector("legend");
if (!firstLegendElementChild || !firstLegendElementChild.contains(formControl)) {
return true;
}
}
e = e.parentNode;
}
return false;
};
exports.isSubmittable = formControl => {
// https://html.spec.whatwg.org/multipage/forms.html#category-submit
return submittableLocalNames.has(formControl.localName);
};
exports.isButton = formControl => {
// https://html.spec.whatwg.org/multipage/forms.html#concept-button
return formControl.type === "button" || formControl.type === "submit" || formControl.type === "reset" ||
formControl.type === "image" || formControl.localName === "button";
};
exports.normalizeToCRLF = string => {
return string.replace(/\r([^\n])/g, "\r\n$1")
.replace(/\r$/, "\r\n")
.replace(/([^\r])\n/g, "$1\r\n")
.replace(/^\n/, "\r\n");
};

View File

@@ -0,0 +1,13 @@
"use strict";
const SymbolTree = require("symbol-tree");
exports.cloningSteps = Symbol("cloning steps");
exports.locationInfo = Symbol("location info");
// TODO: the many underscore-prefixed hooks should move here
// E.g. _attrModified (which maybe should be split into its per-spec variants)
/**
* This SymbolTree is used to build the tree for all Node in a document
*/
exports.domSymbolTree = new SymbolTree("DOM SymbolTree");

View File

@@ -0,0 +1,5 @@
"use strict";
module.exports = function orderedSetParser(input) {
return new Set(input.split(/[\t\n\f\r ]+/).filter(Boolean));
};

View File

@@ -0,0 +1,11 @@
"use strict";
// https://html.spec.whatwg.org/multipage/webappapis.html#event-handlers-on-elements,-document-objects,-and-window-objects
module.exports = new Set(["onblur", "onerror", "onfocus", "onload", "onresize", "onscroll", "onafterprint",
"onbeforeprint", "onbeforeunload", "onhashchange", "onlanguagechange", "onmessage", "onoffline", "ononline",
"onpagehide", "onpageshow", "onpopstate", "onstorage", "onunload"]);
// level2/html sets up setters/getters on HTMLBodyElement that proxy to the window (setting data properties there)
// level1/core sets up so that modifying the appropriate attributes on body elements will forward to setting on
// the window, with the appropriate `this`.

View File

@@ -0,0 +1,68 @@
"use strict";
const util = require("util");
const ErrorEvent = require("../generated/ErrorEvent");
const errorReportingMode = Symbol("error reporting mode");
// https://html.spec.whatwg.org/multipage/webappapis.html#report-the-error
// Omits script parameter and any check for muted errors; takes error object, message, and location as params, unlike
// the spec. Returns whether the event was handled or not.
function reportAnError(line, col, target, errorObject, message, location) {
if (target[errorReportingMode]) {
return false;
}
target[errorReportingMode] = true;
// TODO Events: use constructor directly, once they are no longer tied to a window.
const event = ErrorEvent.createImpl(["error", {
bubbles: false,
cancelable: true,
message,
filename: location,
lineno: line,
colno: col,
error: errorObject
}]);
try {
target.dispatchEvent(event);
} finally {
target[errorReportingMode] = false;
return event.defaultPrevented;
}
}
module.exports = function reportException(window, error, filenameHint) {
// This function will give good results on real Error objects with stacks; poor ones otherwise
const stack = error && error.stack;
const lines = stack && stack.split("\n");
// Find the first line that matches; important for multi-line messages
let pieces;
if (lines) {
for (let i = 1; i < lines.length && !pieces; ++i) {
pieces = lines[i].match(/at (?:(.+)\s+)?\(?(?:(.+?):(\d+):(\d+)|([^)]+))\)?/);
}
}
const fileName = pieces && pieces[2] || filenameHint || window._document.URL;
const lineNumber = pieces && parseInt(pieces[3]) || 0;
const columnNumber = pieces && parseInt(pieces[4]) || 0;
const handled = reportAnError(lineNumber, columnNumber, window, error, error.message, fileName);
if (!handled) {
const errorString = shouldBeDisplayedAsError(error) ? `[${error.name}: ${error.message}]` : util.inspect(error);
const jsdomError = new Error(`Uncaught ${errorString}`);
jsdomError.detail = error;
jsdomError.type = "unhandled exception";
window._virtualConsole.emit("jsdomError", jsdomError);
}
};
function shouldBeDisplayedAsError(x) {
return x.name && x.message !== undefined && x.stack;
}

View File

@@ -0,0 +1,30 @@
"use strict";
const idlUtils = require("../generated/utils");
const nwmatcher = require("nwmatcher/src/nwmatcher-noqsa");
const domSymbolTree = require("./internal-constants").domSymbolTree;
// Internal method so you don't have to go through the public API
exports.querySelector = function (parentNode, selectors) {
if (!domSymbolTree.hasChildren(parentNode) ||
(parentNode === parentNode._ownerDocument && !parentNode.documentElement)) {
// This allows us to avoid the explosion that occurs if you try to add nwmatcher to a document that is not yet
// initialized.
return null;
}
return addNwmatcher(parentNode).first(selectors, idlUtils.wrapperForImpl(parentNode));
};
// nwmatcher gets `document.documentElement` at creation-time, so we have to initialize lazily, since in the initial
// stages of Document initialization, there is no documentElement present yet.
function addNwmatcher(parentNode) {
const document = parentNode._ownerDocument;
if (!document._nwmatcher) {
document._nwmatcher = nwmatcher({ document });
document._nwmatcher.configure({ UNIQUE_ID: false });
}
return document._nwmatcher;
}

View File

@@ -0,0 +1,6 @@
"use strict";
// https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace
exports.stripAndCollapseASCIIWhitespace = s => {
return s.replace(/[ \t\n\f\r]+/g, " ").replace(/^[ \t\n\f\r]+/, "").replace(/[ \t\n\f\r]+$/, "");
};

View File

@@ -0,0 +1,78 @@
"use strict";
const cssom = require("cssom");
const whatwgEncoding = require("whatwg-encoding");
const whatwgURL = require("whatwg-url");
const resourceLoader = require("../../browser/resource-loader");
exports.fetchStylesheet = (elementImpl, urlString, sheet) => {
const parsedURL = whatwgURL.parseURL(urlString);
return fetchStylesheetInternal(elementImpl, urlString, parsedURL, sheet);
};
exports.evaluateStylesheet = (elementImpl, data, sheet, baseURL) => {
let newStyleSheet;
try {
newStyleSheet = cssom.parse(data);
} catch (e) {
if (elementImpl._ownerDocument._defaultView) {
const error = new Error("Could not parse CSS stylesheet");
error.detail = data;
error.type = "css parsing";
elementImpl._ownerDocument._defaultView._virtualConsole.emit("jsdomError", error);
}
elementImpl._ownerDocument.styleSheets.push(sheet);
return;
}
const spliceArgs = newStyleSheet.cssRules;
spliceArgs.unshift(0, sheet.cssRules.length);
Array.prototype.splice.apply(sheet.cssRules, spliceArgs);
scanForImportRules(elementImpl, sheet.cssRules, baseURL);
elementImpl._ownerDocument.styleSheets.push(sheet);
};
function fetchStylesheetInternal(elementImpl, urlString, parsedURL, sheet) {
let defaultEncoding = elementImpl._ownerDocument._encoding;
if (elementImpl.localName === "link" && elementImpl.hasAttribute("charset")) {
defaultEncoding = whatwgEncoding.labelToName(elementImpl.getAttribute("charset"));
}
resourceLoader.load(elementImpl, urlString, { defaultEncoding }, data => {
// TODO: MIME type checking?
exports.evaluateStylesheet(elementImpl, data, sheet, parsedURL);
});
}
function scanForImportRules(elementImpl, cssRules, baseURL) {
if (!cssRules) {
return;
}
for (let i = 0; i < cssRules.length; ++i) {
if (cssRules[i].cssRules) {
// @media rule: keep searching inside it.
scanForImportRules(elementImpl, cssRules[i].cssRules, baseURL);
} else if (cssRules[i].href) {
// @import rule: fetch the resource and evaluate it.
// See http://dev.w3.org/csswg/cssom/#css-import-rule
// If loading of the style sheet fails its cssRules list is simply
// empty. I.e. an @import rule always has an associated style sheet.
const parsed = whatwgURL.parseURL(cssRules[i].href, { baseURL });
if (parsed === "failure") {
const window = elementImpl._ownerDocument._defaultView;
if (window) {
const error = new Error(`Could not parse CSS @import URL ${cssRules[i].href} relative to base URL ` +
`"${whatwgURL.serializeURL(baseURL)}"`);
error.type = "css @import URL parsing";
window._virtualConsole.emit("jsdomError", error);
}
} else {
fetchStylesheetInternal(elementImpl, whatwgURL.serializeURL(parsed), parsed, elementImpl.sheet);
}
}
}
}

View File

@@ -0,0 +1,72 @@
"use strict";
const domSymbolTree = require("./internal-constants").domSymbolTree;
// All these operate on and return impls, not wrappers!
exports.closest = (e, localName) => {
while (e) {
if (e.localName === localName) {
return e;
}
e = domSymbolTree.parent(e);
}
return null;
};
exports.childrenByHTMLLocalName = (parent, localName) => {
return domSymbolTree.childrenToArray(parent, { filter(node) {
return node._localName === localName && node._namespaceURI === "http://www.w3.org/1999/xhtml";
} });
};
exports.descendantsByHTMLLocalName = (parent, localName) => {
return domSymbolTree.treeToArray(parent, { filter(node) {
return node._localName === localName && node._namespaceURI === "http://www.w3.org/1999/xhtml" && node !== parent;
} });
};
exports.childrenByHTMLLocalNames = (parent, localNamesSet) => {
return domSymbolTree.childrenToArray(parent, { filter(node) {
return localNamesSet.has(node._localName) && node._namespaceURI === "http://www.w3.org/1999/xhtml";
} });
};
exports.descendantsByHTMLLocalNames = (parent, localNamesSet) => {
return domSymbolTree.treeToArray(parent, { filter(node) {
return localNamesSet.has(node._localName) &&
node._namespaceURI === "http://www.w3.org/1999/xhtml" &&
node !== parent;
} });
};
exports.firstChildWithHTMLLocalName = (parent, localName) => {
const iterator = domSymbolTree.childrenIterator(parent);
for (const child of iterator) {
if (child._localName === localName && child._namespaceURI === "http://www.w3.org/1999/xhtml") {
return child;
}
}
return null;
};
exports.firstChildWithHTMLLocalNames = (parent, localNamesSet) => {
const iterator = domSymbolTree.childrenIterator(parent);
for (const child of iterator) {
if (localNamesSet.has(child._localName) && child._namespaceURI === "http://www.w3.org/1999/xhtml") {
return child;
}
}
return null;
};
exports.firstDescendantWithHTMLLocalName = (parent, localName) => {
const iterator = domSymbolTree.treeIterator(parent);
for (const descendant of iterator) {
if (descendant._localName === localName && descendant._namespaceURI === "http://www.w3.org/1999/xhtml") {
return descendant;
}
}
return null;
};

View File

@@ -0,0 +1,62 @@
"use strict";
const xnv = require("xml-name-validator");
const DOMException = require("../../web-idl/DOMException");
// https://dom.spec.whatwg.org/#validate
exports.name = function (name) {
const result = xnv.name(name);
if (!result.success) {
throw new DOMException(DOMException.INVALID_CHARACTER_ERR,
"\"" + name + "\" did not match the Name production: " + result.error);
}
};
exports.qname = function (qname) {
exports.name(qname);
const result = xnv.qname(qname);
if (!result.success) {
throw new DOMException(DOMException.NAMESPACE_ERR,
"\"" + qname + "\" did not match the QName production: " + result.error);
}
};
exports.validateAndExtract = function (namespace, qualifiedName) {
if (namespace === "") {
namespace = null;
}
exports.qname(qualifiedName);
let prefix = null;
let localName = qualifiedName;
const colonIndex = qualifiedName.indexOf(":");
if (colonIndex !== -1) {
prefix = qualifiedName.substring(0, colonIndex);
localName = qualifiedName.substring(colonIndex + 1);
}
if (prefix !== null && namespace === null) {
throw new DOMException(DOMException.NAMESPACE_ERR,
"A namespace was given but a prefix was also extracted from the qualifiedName");
}
if (prefix === "xml" && namespace !== "http://www.w3.org/XML/1998/namespace") {
throw new DOMException(DOMException.NAMESPACE_ERR,
"A prefix of \"xml\" was given but the namespace was not the XML namespace");
}
if ((qualifiedName === "xmlns" || prefix === "xmlns") && namespace !== "http://www.w3.org/2000/xmlns/") {
throw new DOMException(DOMException.NAMESPACE_ERR,
"A prefix or qualifiedName of \"xmlns\" was given but the namespace was not the XMLNS namespace");
}
if (namespace === "http://www.w3.org/2000/xmlns/" && qualifiedName !== "xmlns" && prefix !== "xmlns") {
throw new DOMException(DOMException.NAMESPACE_ERR,
"The XMLNS namespace was given but neither the prefix nor qualifiedName was \"xmlns\"");
}
return { namespace, prefix, localName };
};