Skip to content

Commit

Permalink
Revert back to nwsapi
Browse files Browse the repository at this point in the history
This reverts (most of) commits c039e52 and 908f27d. Per #3659, the performance of dom-selector is currently too slow.

Closes #3659.
  • Loading branch information
domenic committed Jan 21, 2024
1 parent 5b1a49e commit ac815ff
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 115 deletions.
62 changes: 38 additions & 24 deletions lib/jsdom/living/helpers/selectors.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,47 @@
"use strict";

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;
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
});
}
};

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

exports.closest = (selectors, elementImpl) => {
const element = wrapperForImpl(elementImpl);
return domSelector.closest(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.querySelector = (selectors, parentNodeImpl) => {
const node = wrapperForImpl(parentNodeImpl);
return domSelector.querySelector(selectors, node);
};
if (!document._nwsapi) {
document._nwsapi = initNwsapi(parentNode);
document._nwsapi.configure({
LOGERRORS: false,
IDS_DUPES: true,
MIXEDCASE: true
});
}

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

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

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

// Naive implementation of https://drafts.csswg.org/css-cascade-4/#cascading
Expand Down
22 changes: 12 additions & 10 deletions lib/jsdom/living/nodes/Element-impl.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
"use strict";
const { closest, matches } = require("../helpers/selectors");
const { addNwsapi } = 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 @@ -545,15 +546,8 @@ class ElementImpl extends NodeImpl {
}

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

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

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

// https://html.spec.whatwg.org/#reflecting-content-attributes-in-idl-attributes
Expand Down Expand Up @@ -592,6 +586,14 @@ 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: 19 additions & 4 deletions lib/jsdom/living/nodes/ParentNode-impl.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
"use strict";

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

querySelector(selectors) {
return querySelector(selectors, this);
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) {
const nodes = querySelectorAll(selectors, this);
return NodeList.create(this._globalObject, [], { nodes });
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)) });
}
}

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

module.exports = {
implementation: ParentNodeImpl
};
58 changes: 6 additions & 52 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
"license": "MIT",
"repository": "jsdom/jsdom",
"dependencies": {
"@asamuzakjp/dom-selector": "^2.0.1",
"cssstyle": "^4.0.1",
"data-urls": "^5.0.0",
"decimal.js": "^10.4.3",
Expand All @@ -29,6 +28,7 @@
"http-proxy-agent": "^7.0.0",
"https-proxy-agent": "^7.0.2",
"is-potential-custom-element-name": "^1.0.1",
"nwsapi": "^2.2.7",
"parse5": "^7.1.2",
"rrweb-cssom": "^0.6.0",
"saxes": "^6.0.0",
Expand Down

0 comments on commit ac815ff

Please sign in to comment.