Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch to DOM selector CI run PR #3656

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
194 changes: 194 additions & 0 deletions benchmark/selectors/complex-selectors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
"use strict";
const suite = require("../document-suite");

exports.matches = () => {
let node;

return suite({
setup(document) {
const x = 5;
const y = 5;
const z = 5;
const xFrag = document.createDocumentFragment();
for (let i = 0; i < x; i++) {
const xNode = document.createElement("div");
xNode.id = `box${i}`;
xNode.classList.add("box", "container");
const yFrag = document.createDocumentFragment();
for (let j = 0; j < y; j++) {
const yNode = document.createElement("div");
yNode.id = `div${i}-${j}`;
yNode.classList.add("block", "outer");
for (let k = 0; k < z; k++) {
const zNode = document.createElement("div");
zNode.id = `div${i}-${j}-${k}`;
zNode.classList.add("block", "inner");
const p = document.createElement("p");
p.id = `p${i}-${j}-${k}`;
p.classList.add("content");
p.textContent = `${i}-${j}-${k}`;
zNode.append(p);
yNode.append(zNode);
}
yFrag.append(yNode);
}
xNode.append(yFrag);
xFrag.append(xNode);
}
const container = document.createElement("div");
container.setAttribute("id", "container");
container.classList.add("container");
container.append(xFrag);
document.body.append(container);
node = document.getElementById(`p${x - 1}-${y - 1}-${z - 1}`);
},
fn() {
const selector = ".box:first-child ~ .box:nth-of-type(4n) + .box .block.inner > .content";
node.matches(selector);
}
});
};

exports.closest = function () {
let node;

return suite({
setup(document) {
const x = 5;
const y = 5;
const z = 5;
const xFrag = document.createDocumentFragment();
for (let i = 0; i < x; i++) {
const xNode = document.createElement("div");
xNode.id = `box${i}`;
xNode.classList.add("box", "container");
const yFrag = document.createDocumentFragment();
for (let j = 0; j < y; j++) {
const yNode = document.createElement("div");
yNode.id = `div${i}-${j}`;
yNode.classList.add("block", "outer");
for (let k = 0; k < z; k++) {
const zNode = document.createElement("div");
zNode.id = `div${i}-${j}-${k}`;
zNode.classList.add("block", "inner");
const p = document.createElement("p");
p.id = `p${i}-${j}-${k}`;
p.classList.add("content");
p.textContent = `${i}-${j}-${k}`;
zNode.append(p);
yNode.append(zNode);
}
yFrag.append(yNode);
}
xNode.append(yFrag);
xFrag.append(xNode);
}
const container = document.createElement("div");
container.setAttribute("id", "container");
container.classList.add("container");
container.append(xFrag);
document.body.append(container);
node = document.getElementById(`p${x - 1}-${y - 1}-${z - 1}`);
},
fn() {
const selector = ".box:first-child ~ .box:nth-of-type(4n) + .box .block.inner > .content";
node.closest(selector);
}
});
};

exports.querySelector = () => {
let node;

return suite({
setup(document) {
const x = 5;
const y = 5;
const z = 5;
const xFrag = document.createDocumentFragment();
for (let i = 0; i < x; i++) {
const xNode = document.createElement("div");
xNode.id = `box${i}`;
xNode.classList.add("box", "container");
const yFrag = document.createDocumentFragment();
for (let j = 0; j < y; j++) {
const yNode = document.createElement("div");
yNode.id = `div${i}-${j}`;
yNode.classList.add("block", "outer");
for (let k = 0; k < z; k++) {
const zNode = document.createElement("div");
zNode.id = `div${i}-${j}-${k}`;
zNode.classList.add("block", "inner");
const p = document.createElement("p");
p.id = `p${i}-${j}-${k}`;
p.classList.add("content");
p.textContent = `${i}-${j}-${k}`;
zNode.append(p);
yNode.append(zNode);
}
yFrag.append(yNode);
}
xNode.append(yFrag);
xFrag.append(xNode);
}
const container = document.createElement("div");
container.setAttribute("id", "container");
container.classList.add("container");
container.append(xFrag);
document.body.append(container);
node = document;
},
fn() {
const selector = ".box:first-child ~ .box:nth-of-type(4n) + .box .block.inner > .content";
node.querySelector(selector);
}
});
};

exports.querySelectorAll = function () {
let node;

return suite({
setup(document) {
const x = 5;
const y = 5;
const z = 5;
const xFrag = document.createDocumentFragment();
for (let i = 0; i < x; i++) {
const xNode = document.createElement("div");
xNode.id = `box${i}`;
xNode.classList.add("box", "container");
const yFrag = document.createDocumentFragment();
for (let j = 0; j < y; j++) {
const yNode = document.createElement("div");
yNode.id = `div${i}-${j}`;
yNode.classList.add("block", "outer");
for (let k = 0; k < z; k++) {
const zNode = document.createElement("div");
zNode.id = `div${i}-${j}-${k}`;
zNode.classList.add("block", "inner");
const p = document.createElement("p");
p.id = `p${i}-${j}-${k}`;
p.classList.add("content");
p.textContent = `${i}-${j}-${k}`;
zNode.append(p);
yNode.append(zNode);
}
yFrag.append(yNode);
}
xNode.append(yFrag);
xFrag.append(xNode);
}
const container = document.createElement("div");
container.setAttribute("id", "container");
container.classList.add("container");
container.append(xFrag);
document.body.append(container);
node = document;
},
fn() {
const selector = ".box:first-child ~ .box:nth-of-type(4n) + .box .block.inner > .content";
node.querySelectorAll(selector);
}
});
};
3 changes: 2 additions & 1 deletion benchmark/selectors/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use strict";

module.exports = {
selector: require("./selector")
"complex-selectors": require("./complex-selectors"),
"sizzle": require("./sizzle")
};
File renamed without changes.
62 changes: 24 additions & 38 deletions lib/jsdom/living/helpers/selectors.js
Original file line number Diff line number Diff line change
@@ -1,47 +1,33 @@
"use strict";

const nwsapi = require("nwsapi");

const idlUtils = require("../generated/utils");

function initNwsapi(node) {
const { _globalObject, _ownerDocument } = node;

return nwsapi({
document: idlUtils.wrapperForImpl(_ownerDocument),
DOMException: _globalObject.DOMException
});
}

exports.matchesDontThrow = (elImpl, selector) => {
const document = elImpl._ownerDocument;

if (!document._nwsapiDontThrow) {
document._nwsapiDontThrow = initNwsapi(elImpl);
document._nwsapiDontThrow.configure({
LOGERRORS: false,
VERBOSITY: false,
IDS_DUPES: true,
MIXEDCASE: true
});
const domSelector = require("@asamuzakjp/dom-selector");
const { wrapperForImpl } = require("../generated/utils");

exports.matchesDontThrow = (selectors, elementImpl) => {
const element = wrapperForImpl(elementImpl);
try {
return domSelector.matches(selectors, element);
} catch {
return false;
}
};

return document._nwsapiDontThrow.match(selector, idlUtils.wrapperForImpl(elImpl));
exports.matches = (selectors, elementImpl) => {
const element = wrapperForImpl(elementImpl);
return domSelector.matches(selectors, element);
};

// nwsapi 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.
exports.addNwsapi = parentNode => {
const document = parentNode._ownerDocument;
exports.closest = (selectors, elementImpl) => {
const element = wrapperForImpl(elementImpl);
return domSelector.closest(selectors, element);
};

if (!document._nwsapi) {
document._nwsapi = initNwsapi(parentNode);
document._nwsapi.configure({
LOGERRORS: false,
IDS_DUPES: true,
MIXEDCASE: true
});
}
exports.querySelector = (selectors, parentNodeImpl) => {
const node = wrapperForImpl(parentNodeImpl);
return domSelector.querySelector(selectors, node);
};

return document._nwsapi;
exports.querySelectorAll = (selectors, parentNodeImpl) => {
const node = wrapperForImpl(parentNodeImpl);
return domSelector.querySelectorAll(selectors, node);
};
5 changes: 3 additions & 2 deletions lib/jsdom/living/helpers/style-rules.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"use strict";

const cssom = require("rrweb-cssom");
const { CSSStyleDeclaration } = require("cssstyle");
const defaultStyleSheet = require("../../browser/default-stylesheet");
Expand Down Expand Up @@ -168,8 +169,8 @@ exports.getDeclarationForElement = elementImpl => {
return declaration;
};

function matches(rule, element) {
return matchesDontThrow(element, rule.selectorText);
function matches(rule, elementImpl) {
return matchesDontThrow(rule.selectorText, elementImpl);
}

// Naive implementation of https://drafts.csswg.org/css-cascade-4/#cascading
Expand Down
22 changes: 10 additions & 12 deletions lib/jsdom/living/nodes/Element-impl.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
"use strict";
const { addNwsapi } = require("../helpers/selectors");
const { closest, matches } = require("../helpers/selectors");
const { HTML_NS } = require("../helpers/namespaces");
const { mixin, memoizeQuery } = require("../../utils");
const idlUtils = require("../generated/utils");
const NodeImpl = require("./Node-impl").implementation;
const ParentNodeImpl = require("./ParentNode-impl").implementation;
const ChildNodeImpl = require("./ChildNode-impl").implementation;
Expand Down Expand Up @@ -546,8 +545,15 @@ class ElementImpl extends NodeImpl {
}

closest(selectors) {
const matcher = addNwsapi(this);
return matcher.closest(selectors, idlUtils.wrapperForImpl(this));
return closest(selectors, this);
}

matches(selectors) {
return matches(selectors, this);
}

webkitMatchesSelector(selectors) {
return matches(selectors, this);
}

// https://html.spec.whatwg.org/#reflecting-content-attributes-in-idl-attributes
Expand Down Expand Up @@ -586,14 +592,6 @@ ElementImpl.prototype.getElementsByClassName = memoizeQuery(function (classNames
return listOfElementsWithClassNames(classNames, this);
});

ElementImpl.prototype.matches = function (selectors) {
const matcher = addNwsapi(this);

return matcher.match(selectors, idlUtils.wrapperForImpl(this));
};

ElementImpl.prototype.webkitMatchesSelector = ElementImpl.prototype.matches;

module.exports = {
implementation: ElementImpl
};
23 changes: 4 additions & 19 deletions lib/jsdom/living/nodes/ParentNode-impl.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
"use strict";

const idlUtils = require("../generated/utils");
const NodeList = require("../generated/NodeList");
const HTMLCollection = require("../generated/HTMLCollection");
const { addNwsapi } = require("../helpers/selectors");
const { querySelector, querySelectorAll } = require("../helpers/selectors");
const { domSymbolTree } = require("../helpers/internal-constants");
const NODE_TYPE = require("../node-type");
const { convertNodesIntoNode } = require("../node");
Expand Down Expand Up @@ -62,30 +61,16 @@ class ParentNodeImpl {
}

querySelector(selectors) {
if (shouldAlwaysSelectNothing(this)) {
return null;
}
const matcher = addNwsapi(this);
return idlUtils.implForWrapper(matcher.first(selectors, idlUtils.wrapperForImpl(this)));
return querySelector(selectors, this);
}

// Warning for internal users: this returns a NodeList containing IDL wrappers instead of impls
querySelectorAll(selectors) {
if (shouldAlwaysSelectNothing(this)) {
return NodeList.create(this._globalObject, [], { nodes: [] });
}
const matcher = addNwsapi(this);
const list = matcher.select(selectors, idlUtils.wrapperForImpl(this));

return NodeList.create(this._globalObject, [], { nodes: list.map(n => idlUtils.tryImplForWrapper(n)) });
const nodes = querySelectorAll(selectors, this);
return NodeList.create(this._globalObject, [], { nodes });
}
}

function shouldAlwaysSelectNothing(elImpl) {
// This is true during initialization.
return elImpl === elImpl._ownerDocument && !elImpl.documentElement;
}

module.exports = {
implementation: ParentNodeImpl
};