зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1893923 - [devtools] Add ChromeOnly CSSStyleRule::querySelectorAll. r=layout-reviewers,emilio.
For the selector highlighter, we were retrieving the desugared selector of each displayed rule, and using the selector text in querySelectorAll to retrieve the elements matching the rule. This can be very expensive, especially for deeply nested rule, for a feature that might not even be used. This patch is adding a method which takes a root node, and will return the elements inside the root node that match the rule's selectors. We're only exposing the method that existed in glue.rs to get the SelectorList of a given Rule, and call `Servo_SelectorList_QueryAll` with it to get our NodeList. A test file is added to ensure this works as expected. Differential Revision: https://phabricator.services.mozilla.com/D208363
This commit is contained in:
Родитель
a309abec2f
Коммит
09fec812c0
|
@ -22,6 +22,10 @@ interface CSSStyleRule : CSSGroupingRule {
|
|||
optional [LegacyNullToEmptyString] DOMString pseudo = "",
|
||||
optional boolean includeVisitedStyle = false);
|
||||
[ChromeOnly] sequence<SelectorWarning> getSelectorWarnings();
|
||||
// Get elements on the page matching the rule's selectors. This is helpful for DevTools
|
||||
// so we can avoid computing a desugared selector, which can be very expensive on deeply
|
||||
// nested rules.
|
||||
[ChromeOnly] NodeList querySelectorAll(Node root);
|
||||
};
|
||||
|
||||
enum SelectorWarningKind {
|
||||
|
|
|
@ -14,6 +14,8 @@ support-files = ["test_bug708874.css"]
|
|||
["test_bug727834.xhtml"]
|
||||
support-files = ["test_bug727834.css"]
|
||||
|
||||
["test_CSSStyleRule_querySelectorAll.html"]
|
||||
|
||||
["test_fontFaceGeneric.xhtml"]
|
||||
|
||||
["test_fontFaceRanges.xhtml"]
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Test CSSStyleRule::QuerySelectorAll</title>
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
|
||||
<style>
|
||||
.test-simple {
|
||||
}
|
||||
.test-nested-parent {
|
||||
.test-nested-child {
|
||||
.test-nested-and-non-nested {
|
||||
}
|
||||
}
|
||||
}
|
||||
.test-nested-and-non-nested {
|
||||
}
|
||||
.test-no-match {
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(doTest);
|
||||
|
||||
function doTest() {
|
||||
let { cssRules } = document.styleSheets[1];
|
||||
|
||||
info("Testing simple case");
|
||||
let rule = cssRules[0];
|
||||
let result = rule.querySelectorAll(document);
|
||||
is(result.length, 2, `2 elements are matching "${rule.selectorText}"`);
|
||||
is(
|
||||
result[0].id,
|
||||
"a",
|
||||
`Got expected id for first element matching "${rule.selectorText}"`
|
||||
);
|
||||
is(
|
||||
result[1].id,
|
||||
"b",
|
||||
`Got expected id for second element matching "${rule.selectorText}"`
|
||||
);
|
||||
|
||||
info("Testing nested rule");
|
||||
rule = cssRules[1].cssRules[0];
|
||||
result = rule.querySelectorAll(document);
|
||||
is(result.length, 1, `1 element is matching "${rule.selectorText}"`);
|
||||
is(
|
||||
result[0].id,
|
||||
"d",
|
||||
`Got expected id for element matching "${rule.selectorText}"`
|
||||
);
|
||||
|
||||
info("Testing multi-level deep nested rule");
|
||||
rule = cssRules[1].cssRules[0].cssRules[0];
|
||||
result = rule.querySelectorAll(document);
|
||||
// Check that we're not retrieving `f`, as the rule selectorText is `.test-nested-and-non-nested`,
|
||||
// but it is nested inside `.test-nested-child`.
|
||||
is(result.length, 1, `1 element is matching "${rule.selectorText}"`);
|
||||
is(
|
||||
result[0].id,
|
||||
"e",
|
||||
`Got expected id for element matching "${rule.selectorText}"`
|
||||
);
|
||||
|
||||
info(
|
||||
"Testing rule matching multiple elements with the same class, some nested, some not"
|
||||
);
|
||||
rule = cssRules[2];
|
||||
result = rule.querySelectorAll(document);
|
||||
is(result.length, 2, `2 elements are matching "${rule.selectorText}"`);
|
||||
is(
|
||||
result[0].id,
|
||||
"e",
|
||||
`Got expected id for first element matching "${rule.selectorText}"`
|
||||
);
|
||||
is(
|
||||
result[1].id,
|
||||
"f",
|
||||
`Got expected id for second element matching "${rule.selectorText}"`
|
||||
);
|
||||
|
||||
info("Testing that search results are limited by the passed root node");
|
||||
rule = cssRules[2];
|
||||
result = rule.querySelectorAll(document.querySelector("#c"));
|
||||
is(
|
||||
result.length,
|
||||
1,
|
||||
`An element is matching "${rule.selectorText}" in #c`
|
||||
);
|
||||
is(
|
||||
result[0].id,
|
||||
"e",
|
||||
`Got expected id for element matching "${rule.selectorText}"`
|
||||
);
|
||||
|
||||
info("Testing rule with no matching elements");
|
||||
rule = cssRules[3];
|
||||
result = rule.querySelectorAll(document);
|
||||
is(result.length, 0, `No elements matching "${rule.selectorText}"`);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Test CSSStyleRule::QuerySelectorAll</h1>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
<div id="a" class="test-simple"></div>
|
||||
<div id="b" class="test-simple"></div>
|
||||
<div id="c" class="test-nested-parent">
|
||||
<span id="d" class="test-nested-child">
|
||||
<b id="e" class="test-nested-and-non-nested"></b>
|
||||
</span>
|
||||
</div>
|
||||
<b id="f" class="test-nested-and-non-nested"></b>
|
||||
</div>
|
||||
<pre id="test"></pre>
|
||||
</body>
|
||||
</html>
|
|
@ -312,6 +312,18 @@ void CSSStyleRule::GetSelectorWarnings(
|
|||
}
|
||||
}
|
||||
|
||||
already_AddRefed<nsINodeList> CSSStyleRule::QuerySelectorAll(nsINode& aRoot) {
|
||||
AutoTArray<const StyleLockedStyleRule*, 8> rules;
|
||||
CollectStyleRules(*this, /* aDesugared = */ true, rules);
|
||||
StyleSelectorList* list = Servo_StyleRule_GetSelectorList(&rules);
|
||||
|
||||
RefPtr<nsSimpleContentList> contentList = new nsSimpleContentList(&aRoot);
|
||||
Servo_SelectorList_QueryAll(&aRoot, list, contentList.get(),
|
||||
/* useInvalidation */ false);
|
||||
Servo_SelectorList_Drop(list);
|
||||
return contentList.forget();
|
||||
}
|
||||
|
||||
/* virtual */
|
||||
JSObject* CSSStyleRule::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) {
|
||||
|
|
|
@ -75,6 +75,7 @@ class CSSStyleRule final : public css::GroupRule, public SupportsWeakPtr {
|
|||
bool aRelevantLinkVisited);
|
||||
NotNull<DeclarationBlock*> GetDeclarationBlock() const;
|
||||
void GetSelectorWarnings(nsTArray<SelectorWarning>& aResult) const;
|
||||
already_AddRefed<nsINodeList> QuerySelectorAll(nsINode& aRoot);
|
||||
|
||||
// WebIDL interface
|
||||
StyleCssRuleType Type() const final;
|
||||
|
|
|
@ -2548,6 +2548,11 @@ fn desugared_selector_list(rules: &ThinVec<&LockedStyleRule>) -> SelectorList {
|
|||
selectors.expect("Empty rule chain?")
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn Servo_StyleRule_GetSelectorList(rules: &ThinVec<&LockedStyleRule>) -> *mut SelectorList {
|
||||
Box::into_raw(Box::new(desugared_selector_list(rules)))
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn Servo_StyleRule_GetSelectorDataAtIndex(
|
||||
rules: &ThinVec<&LockedStyleRule>,
|
||||
|
|
Загрузка…
Ссылка в новой задаче