Socket
Socket
Sign inDemoInstall

jsdom

Package Overview
Dependencies
91
Maintainers
6
Versions
258
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 12.0.0 to 12.1.0

8

lib/api.js

@@ -75,3 +75,3 @@ "use strict";

runVMScript(script) {
runVMScript(script, options) {
if (!vm.isContext(this[window])) {

@@ -82,3 +82,3 @@ throw new TypeError("This jsdom was not configured to allow script running. " +

return script.runInContext(this[window]);
return script.runInContext(this[window], options);
}

@@ -250,6 +250,2 @@

if (options.userAgent !== undefined) {
transformed.windowOptions.userAgent = String(options.userAgent);
}
if (options.includeNodeLocations) {

@@ -256,0 +252,0 @@ if (transformed.windowOptions.parsingMode === "xml") {

"use strict";
const parse5 = require("parse5");
const sax = require("sax");
const saxes = require("saxes");
const attributes = require("../living/attributes");

@@ -32,3 +32,3 @@ const DocumentType = require("../living/generated/DocumentType");

constructor(parsingMode) {
this.parser = parsingMode === "xml" ? sax : parse5;
this.parser = parsingMode === "xml" ? saxes : parse5;
}

@@ -49,3 +49,3 @@

_doParse(...args) {
return this.parser === parse5 ? this._parseWithParse5(...args) : this._parseWithSax(...args);
return this.parser === parse5 ? this._parseWithParse5(...args) : this._parseWithSaxes(...args);
}

@@ -72,86 +72,75 @@

_parseWithSax(html, isFragment, contextNode) {
const SaxParser = this.parser.parser;
const parser = new SaxParser(/* strict = */true, { xmlns: true, strictEntities: true });
parser.noscript = false;
parser.looseCase = "toString";
_parseWithSaxes(html, isFragment, contextNode) {
const parserOptions = { xmlns: true };
if (isFragment) {
parserOptions.fragment = true;
parserOptions.resolvePrefix = prefix => {
// saxes wants undefined as the return value if the prefix is not
// defined, not null.
return contextNode.lookupNamespaceURI(prefix) || undefined;
};
}
const parser = new this.parser.SaxesParser(parserOptions);
const openStack = [contextNode];
const currentDocument = contextNode._ownerDocument || contextNode;
parser.ontext = text => {
setChildForSax(openStack[openStack.length - 1], {
type: "text",
data: text
});
appendChild(
openStack[openStack.length - 1],
currentDocument.createTextNode(text)
);
};
parser.oncdata = cdata => {
setChildForSax(openStack[openStack.length - 1], {
type: "cdata",
data: cdata
});
appendChild(
openStack[openStack.length - 1],
currentDocument.createCDATASection(cdata)
);
};
parser.onopentag = arg => {
const attrs = Object.keys(arg.attributes).map(key => {
const rawAttribute = arg.attributes[key];
parser.onopentag = tag => {
const { local: tagLocal, uri: tagURI, prefix: tagPrefix, attributes: tagAttributes } = tag;
const elem = currentDocument._createElementWithCorrectElementInterface(tagLocal, tagURI);
elem._prefix = tagPrefix || null;
elem._namespaceURI = tagURI || null;
// We mark a script element as "parser-inserted", which prevents it from
// being immediately executed.
if (tagLocal === "script" && tagURI === HTML_NS) {
elem._parserInserted = true;
}
let { prefix } = rawAttribute;
let localName = rawAttribute.local;
if (prefix === "xmlns" && localName === "") {
// intended weirdness in node-sax, see https://github.com/isaacs/sax-js/issues/165
localName = prefix;
prefix = null;
}
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
);
}
if (prefix === "") {
prefix = null;
}
const namespace = rawAttribute.uri === "" ? null : rawAttribute.uri;
return { name: rawAttribute.name, value: rawAttribute.value, prefix, localName, namespace };
});
const tag = {
type: "tag",
name: arg.local,
prefix: arg.prefix,
namespace: arg.uri,
attributes: attrs
};
if (arg.local === "script" && arg.uri === HTML_NS) {
openStack.push(tag);
} else {
const elem = setChildForSax(openStack[openStack.length - 1], tag);
openStack.push(elem);
}
appendChild(openStack[openStack.length - 1], elem);
openStack.push(elem);
};
parser.onclosetag = () => {
const elem = openStack.pop();
if (elem.constructor.name === "Object") { // we have an empty script tag
setChildForSax(openStack[openStack.length - 1], elem);
// Once a script is populated, we can execute it.
if (elem.localName === "script" && elem.namespaceURI === HTML_NS) {
elem._eval();
}
};
parser.onscript = scriptText => {
const tag = openStack.pop();
tag.children = [{ type: "text", data: scriptText }];
const elem = setChildForSax(openStack[openStack.length - 1], tag);
openStack.push(elem);
};
parser.oncomment = comment => {
setChildForSax(openStack[openStack.length - 1], {
type: "comment",
data: comment
});
appendChild(
openStack[openStack.length - 1],
currentDocument.createComment(comment)
);
};
parser.onprocessinginstruction = pi => {
setChildForSax(openStack[openStack.length - 1], {
type: "directive",
name: "?" + pi.name,
data: "?" + pi.name + " " + pi.body + "?"
});
parser.onprocessinginstruction = ({ target, body }) => {
appendChild(
openStack[openStack.length - 1],
currentDocument.createProcessingInstruction(target, body)
);
};
parser.ondoctype = dt => {
setChildForSax(openStack[openStack.length - 1], {
type: "directive",
name: "!doctype",
data: "!doctype " + dt
});
appendChild(
openStack[openStack.length - 1],
parseDocType(currentDocument, `<!doctype ${dt}>`)
);

@@ -175,73 +164,10 @@ const entityMatcher = /<!ENTITY ([^ ]+) "([^"]+)">/g;

function setChildForSax(parentImpl, node) {
const currentDocument = (parentImpl && parentImpl._ownerDocument) || parentImpl;
let newNode;
let isTemplateContents = false;
switch (node.type) {
case "tag":
case "script":
case "style":
newNode = currentDocument._createElementWithCorrectElementInterface(node.name, node.namespace);
newNode._prefix = node.prefix || null;
newNode._namespaceURI = node.namespace || null;
break;
case "root":
// If we are in <template> then add all children to the parent's _templateContents; skip this virtual root node.
if (parentImpl.tagName === "TEMPLATE" && parentImpl._namespaceURI === HTML_NS) {
newNode = parentImpl._templateContents;
isTemplateContents = true;
}
break;
case "text":
// HTML entities should already be decoded by the parser, so no need to decode them
newNode = currentDocument.createTextNode(node.data);
break;
case "cdata":
newNode = currentDocument.createCDATASection(node.data);
break;
case "comment":
newNode = currentDocument.createComment(node.data);
break;
case "directive":
if (node.name[0] === "?" && node.name.toLowerCase() !== "?xml") {
const data = node.data.slice(node.name.length + 1, -1);
newNode = currentDocument.createProcessingInstruction(node.name.substring(1), data);
} else if (node.name.toLowerCase() === "!doctype") {
newNode = parseDocType(currentDocument, "<" + node.data + ">");
}
break;
function appendChild(parent, child) {
if (parent._templateContents) {
// Template elements do not have children but instead store their content
// in a separate hierarchy.
parent._templateContents.appendChild(child);
} else {
parent.appendChild(child);
}
if (!newNode) {
return null;
}
if (node.attributes) {
for (const a of node.attributes) {
attributes.setAttributeValue(newNode, a.localName, a.value, a.prefix, a.namespace);
}
}
if (node.children) {
for (let c = 0; c < node.children.length; c++) {
setChildForSax(newNode, node.children[c]);
}
}
if (!isTemplateContents) {
if (parentImpl._templateContents) {
// Setting innerHTML on a <template>
parentImpl._templateContents.appendChild(newNode);
} else {
parentImpl.appendChild(newNode);
}
}
return newNode;
}

@@ -248,0 +174,0 @@

"use strict";
class QueueItem {
constructor(onLoad, onError, dependentItem) {
this.onLoad = onLoad;
this.onError = onError;
this.data = null;
this.error = null;
this.dependentItem = dependentItem;
}
}
/**

@@ -10,6 +20,7 @@ * AsyncResourceQueue is the queue in charge of run the async scripts

this.items = new Set();
this.dependentItems = new Set();
}
count() {
return this.items.size;
return this.items.size + this.dependentItems.size;
}

@@ -23,2 +34,22 @@

_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) {

@@ -28,33 +59,23 @@ this._listener = listener;

push(request, onLoad, onError) {
push(request, onLoad, onError, dependentItem) {
const q = this;
q.items.add(request);
const item = new QueueItem(onLoad, onError, dependentItem);
function check(error, data) {
let promise;
q.items.add(item);
if (onError && error) {
promise = onError(error);
} else if (onLoad && data) {
promise = onLoad(data);
}
return request
.then(data => {
item.data = data;
promise
.then(() => {
q.items.delete(request);
if (dependentItem && !dependentItem.finished) {
q.dependentItems.add(item);
return q.items.delete(item);
}
if (q.count() === 0) {
q._notify();
}
});
}
return request
.then(data => {
if (onLoad) {
return check(null, data);
return q._check(item);
}
q.items.delete(request);
q.items.delete(item);

@@ -68,7 +89,14 @@ if (q.count() === 0) {

.catch(err => {
item.error = err;
if (dependentItem && !dependentItem.finished) {
q.dependentItems.add(item);
return q.items.delete(item);
}
if (onError) {
return check(err);
return q._check(item);
}
q.items.delete(request);
q.items.delete(item);

@@ -82,2 +110,10 @@ if (q.count() === 0) {

}
notifyItem(syncItem) {
for (const item of this.dependentItems) {
if (item.dependentItem === syncItem) {
this._check(item);
}
}
}
};
"use strict";
const idlUtils = require("../../living/generated/utils");

@@ -17,2 +18,3 @@ module.exports = class PerDocumentResourceLoader {

cookieJar: this._document._cookieJar,
element: idlUtils.wrapperForImpl(element),
referrer: this._document.URL

@@ -74,3 +76,3 @@ });

if (element.localName === "script" && element.hasAttribute("async")) {
this._asyncQueue.push(request, onLoadWrapped, onErrorWrapped);
this._asyncQueue.push(request, onLoadWrapped, onErrorWrapped, this._queue.getLastScript());
} else if (element.localName === "script" && element.hasAttribute("defer")) {

@@ -77,0 +79,0 @@ this._deferQueue.push(request, onLoadWrapped, onErrorWrapped, false, element);

@@ -8,6 +8,20 @@ "use strict";

module.exports = class ResourceQueue {
constructor({ paused } = {}) {
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() {

@@ -75,2 +89,8 @@ let found = false;

}
this.finished = true;
if (q._asyncQueue) {
q._asyncQueue.notifyItem(this);
}
});

@@ -77,0 +97,0 @@ }

@@ -106,2 +106,20 @@ "use strict";

get type() {
if (!this || !module.exports.is(this)) {
throw new TypeError("Illegal invocation");
}
return this[impl]["type"];
}
get elements() {
if (!this || !module.exports.is(this)) {
throw new TypeError("Illegal invocation");
}
return utils.getSameObject(this, "elements", () => {
return utils.tryWrapperForImpl(this[impl]["elements"]);
});
}
get willValidate() {

@@ -140,2 +158,4 @@ if (!this || !module.exports.is(this)) {

name: { enumerable: true },
type: { enumerable: true },
elements: { enumerable: true },
willValidate: { enumerable: true },

@@ -142,0 +162,0 @@ validity: { enumerable: true },

"use strict";
const { domSymbolTree } = require("./internal-constants");
const { TEXT_NODE } = require("../node-type");
const { CDATA_SECTION_NODE, TEXT_NODE } = require("../node-type");
//
// https://dom.spec.whatwg.org/#concept-child-text-content
//
exports.childTextContent = node => {

@@ -9,3 +12,5 @@ let result = "";

for (const child of iterator) {
if (child.nodeType === TEXT_NODE) {
if (child.nodeType === TEXT_NODE ||
// The CDataSection extends Text.
child.nodeType === CDATA_SECTION_NODE) {
result += child.data;

@@ -12,0 +17,0 @@ }

@@ -180,5 +180,5 @@ "use strict";

this._lastModified = toLastModifiedString(privateData.options.lastModified || new Date());
this._queue = new ResourceQueue({ paused: false });
this._asyncQueue = new AsyncResourceQueue();
this._queue = new ResourceQueue({ asyncQueue: this._asyncQueue, paused: false });
this._deferQueue = new ResourceQueue({ paused: true });
this._asyncQueue = new AsyncResourceQueue();
this._requestManager = new RequestManager();

@@ -486,19 +486,24 @@ this.readyState = "loading";

const onLoad = () => {
this.readyState = "complete";
const ev = this.createEvent("HTMLEvents");
const doc = this;
function dispatchEvent() {
doc.readyState = "complete";
const ev = doc.createEvent("HTMLEvents");
ev.initEvent("load", false, false);
ev.initEvent("load", false, false);
this.dispatchEvent(ev);
};
const waitForAsync = new Promise(resolve => {
if (this._asyncQueue.count() === 0) {
return resolve();
doc.dispatchEvent(ev);
}
return this._asyncQueue.setListener(() => {
resolve();
return new Promise(resolve => {
if (this._asyncQueue.count() === 0) {
dispatchEvent();
return resolve();
}
return this._asyncQueue.setListener(() => {
dispatchEvent();
resolve();
});
});
});
};

@@ -508,3 +513,3 @@ this._queue.push(dummyPromise, onDOMContentLoad, null);

// As a side-effect the document's load-event will be dispatched.
this._queue.push(waitForAsync, onLoad, null, true);
this._queue.push(dummyPromise, onLoad, null, true);
}

@@ -511,0 +516,0 @@

"use strict";
const HTMLCollection = require("../generated/HTMLCollection");
const HTMLElementImpl = require("./HTMLElement-impl").implementation;

@@ -6,5 +7,14 @@ const DefaultConstraintValidationImpl =

const { mixin } = require("../../utils");
const { closest } = require("../helpers/traversal");
const { closest, descendantsByHTMLLocalNames } = require("../helpers/traversal");
const listedElements = new Set(["button", "fieldset", "input", "object", "output", "select", "textarea"]);
class HTMLFieldSetElementImpl extends HTMLElementImpl {
get elements() {
return HTMLCollection.createImpl([], {
element: this,
query: () => descendantsByHTMLLocalNames(this, listedElements)
});
}
get form() {

@@ -14,2 +24,6 @@ return closest(this, "form");

get type() {
return "fieldset";
}
_barredFromConstraintValidationSpecialization() {

@@ -16,0 +30,0 @@ return true;

@@ -214,7 +214,19 @@ "use strict";

if (this.type === "checkbox" || (this.type === "radio" && !this._preCheckedRadioState)) {
const inputEvent = Event.createImpl(["input", { isTrusted: true, bubbles: true, cancelable: true }], {});
this.dispatchEvent(inputEvent);
const inputEvent = Event.createImpl(
[
"input",
{ bubbles: true, cancelable: false }
],
{ isTrusted: true }
);
this._dispatch(inputEvent);
const changeEvent = Event.createImpl(["change", { bubbles: true, cancelable: true }], {});
this.dispatchEvent(changeEvent);
const changeEvent = Event.createImpl(
[
"change",
{ bubbles: true, cancelable: false }
],
{ isTrusted: true }
);
this._dispatch(changeEvent);
} else if (this.type === "submit") {

@@ -221,0 +233,0 @@ const { form } = this;

@@ -50,2 +50,6 @@ "use strict";

prepend(...nodes) {
this.insertBefore(convertNodesIntoNode(this._ownerDocument, nodes), this.firstChild);
}
append(...nodes) {

@@ -55,26 +59,22 @@ this.appendChild(convertNodesIntoNode(this._ownerDocument, nodes));

prepend(...nodes) {
this.insertBefore(convertNodesIntoNode(this._ownerDocument, nodes), this.firstChild);
querySelector(selectors) {
if (shouldAlwaysSelectNothing(this)) {
return null;
}
const matcher = addNwsapi(this);
return idlUtils.implForWrapper(matcher.first(selectors, idlUtils.wrapperForImpl(this)));
}
}
ParentNodeImpl.prototype.querySelector = function (selectors) {
if (shouldAlwaysSelectNothing(this)) {
return null;
}
const matcher = addNwsapi(this);
return idlUtils.implForWrapper(matcher.first(selectors, idlUtils.wrapperForImpl(this)));
};
// Warning for internal users: this returns a NodeList containing IDL wrappers instead of impls
querySelectorAll(selectors) {
if (shouldAlwaysSelectNothing(this)) {
return NodeList.create([], { nodes: [] });
}
const matcher = addNwsapi(this);
const list = matcher.select(selectors, idlUtils.wrapperForImpl(this));
// WARNING: this returns a NodeList containing IDL wrappers instead of impls
ParentNodeImpl.prototype.querySelectorAll = function (selectors) {
if (shouldAlwaysSelectNothing(this)) {
return NodeList.create([], { nodes: [] });
return NodeList.create([], { nodes: list.map(n => idlUtils.tryImplForWrapper(n)) });
}
const matcher = addNwsapi(this);
const list = matcher.select(selectors, idlUtils.wrapperForImpl(this));
}
return NodeList.create([], { nodes: list.map(n => idlUtils.tryImplForWrapper(n)) });
};
function shouldAlwaysSelectNothing(elImpl) {

@@ -81,0 +81,0 @@ // This is true during initialization.

{
"name": "jsdom",
"version": "12.0.0",
"version": "12.1.0",
"description": "A JavaScript implementation of many web standards",

@@ -27,3 +27,3 @@ "keywords": [

"cssom": ">= 0.3.2 < 0.4.0",
"cssstyle": "^1.0.0",
"cssstyle": "^1.1.1",
"data-urls": "^1.0.1",

@@ -38,3 +38,3 @@ "domexception": "^1.0.1",

"request-promise-native": "^1.0.5",
"sax": "^1.2.4",
"saxes": "^3.1.2",
"symbol-tree": "^3.2.2",

@@ -106,3 +106,6 @@ "tough-cookie": "^2.4.3",

},
"main": "./lib/api.js"
"main": "./lib/api.js",
"engines": {
"node": ">=8"
}
}

@@ -155,3 +155,3 @@ <h1 align="center">

- `proxy` is the address of a HTTP proxy to be used.
- `proxy` is the address of an HTTP proxy to be used.
- `strictSSL` can be set to false to disable the requirement that SSL certificates be valid.

@@ -167,3 +167,3 @@ - `userAgent` affects the `User-Agent` header sent, and thus the resulting value for `navigator.userAgent`. It defaults to <code>\`Mozilla/5.0 (${process.platform || "unknown OS"}) AppleWebKit/537.36 (KHTML, like Gecko) jsdom/${jsdomVersion}\`</code>.

if (url === "https://example.com/some-specific-script.js") {
return Buffer.from("window.someGlobal = 5;");
return Promise.resolve(Buffer.from("window.someGlobal = 5;"));
}

@@ -178,2 +178,16 @@

One of the options you will receive in `fetch()` will be the element (if applicable) that is fetching a resource.
```js
class CustomResourceLoader extends jsdom.ResourceLoader {
fetch(url, options) {
if (options.element) {
console.log(`Element ${options.element.localName} is requestion the url ${url}`);
}
return super.fetch(url, options);
}
}
```
### Virtual consoles

@@ -299,3 +313,3 @@

### Running vm-created scripts with `runVMScript(script)`
### Running vm-created scripts with `runVMScript(script[, options])`

@@ -325,2 +339,4 @@ The built-in `vm` module of Node.js allows you to create `Script` instances, which can be compiled ahead of time and then run multiple times on a given "VM context". Behind the scenes, a jsdom `Window` is indeed a VM context. To get access to this ability, use the `runVMScript()` method:

`runVMScript()` also takes an `options` object as its second argument. See the [Node.js docs](https://nodejs.org/api/vm.html#vm_script_runincontext_contextifiedsandbox_options) for details. (This functionality does not work when [using jsdom in a web browser](running-jsdom-inside-a-web-browser).)
### Reconfiguring the jsdom with `reconfigure(settings)`

@@ -327,0 +343,0 @@

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc