-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix :scope selector matching in the case of DocumentFragment/ShadowRoot
When used in querySelector and querySelectorAll, :scope should match the element or the DocumentFragment/ShadowRoot the querySelector is called on. However, WPT adeed in crrev.com/c/1158445 shows that :scope matches nothing when querySelector is called on shadow root, while it works fine when querySelector is called on true element. This CL fixes :scope selector matching process so that :scope itself matches DocumentFragment/ShadowRoot when querySelector is called on DocumentFragment/ShadowRoot. Tests for DocumentFragment are also added in this CL. Link to the spec: https://drafts.csswg.org/selectors-4/#the-scope-pseudo Link to related issue: w3c/csswg-drafts#3016 Link to related CL: crrev.com/c/1158445 Bug: 859692 Change-Id: If20fc4b122d93a553dc478be0ee958958340a34f Reviewed-on: https://chromium-review.googlesource.com/1203472 Reviewed-by: Rune Lillesveen <futhark@chromium.org> Reviewed-by: Rakina Zata Amni <rakina@chromium.org> Commit-Queue: Momoko Sumida <momon@google.com> Cr-Commit-Position: refs/heads/master@{#590595}
- Loading branch information
1 parent
8f0b9c6
commit 4052983
Showing
1 changed file
with
65 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,86 @@ | ||
<!doctype html> | ||
<link rel='help' href='https://drafts.csswg.org/selectors-4/#the-scope-pseudo'> | ||
<meta name='description' content=':scope should match when context object is a shadow root'> | ||
<meta name='description' content=':scope should match when context object is a ShadowRoot or a DocumentFragment'> | ||
<script src='/resources/testharness.js'></script> | ||
<script src='/resources/testharnessreport.js'></script> | ||
<div id='shadowHost'></div> | ||
<script> | ||
'use strict' | ||
const shadowRoot = shadowHost.attachShadow({mode:'open'}) | ||
shadowRoot.innerHTML = '<div class="div" id="external_div">Shadow Element<div id="nesteddiv">nested</div></div>'; | ||
const shadowRoot = shadowHost.attachShadow({mode:'open'}) | ||
const externalDiv = document.createElement('div'); | ||
externalDiv.setAttribute('id', 'external'); | ||
const nestedDiv = document.createElement('div'); | ||
nestedDiv.setAttribute('id', 'nested'); | ||
shadowRoot.appendChild(externalDiv); | ||
externalDiv.appendChild(nestedDiv); | ||
|
||
test(() => { | ||
assert_equals(shadowRoot.firstChild.querySelectorAll(':scope > div').length, 1, 'should get the number of direct children of external_div'); | ||
assert_equals(shadowRoot.firstChild.querySelector(':scope > div'), shadowRoot.getElementById("nesteddiv"), 'should get the first direct child of external_div'); | ||
assert_equals(shadowRoot.firstChild.querySelector(':scope > div').innerHTML, 'nested', 'should get the text in nesteddiv'); | ||
const nestedShadowRoot = nestedDiv.attachShadow({mode:'open'}) | ||
const shadowExternalDiv = document.createElement('div'); | ||
shadowExternalDiv.setAttribute('id', 'shadow_external'); | ||
const shadowNestedDiv = document.createElement('div'); | ||
shadowNestedDiv.setAttribute('id', 'shadow_nested'); | ||
nestedShadowRoot.appendChild(shadowExternalDiv); | ||
shadowExternalDiv.appendChild(shadowNestedDiv); | ||
|
||
test (() => { | ||
assert_equals(shadowRoot.firstChild.querySelectorAll(':scope > div').length, 1, 'should get the number of direct children of externalDiv'); | ||
assert_equals(shadowRoot.firstChild.querySelector(':scope > div'), shadowRoot.getElementById('nested'), 'should get the first direct child of externalDiv'); | ||
assert_equals(shadowRoot.firstChild.querySelector(':scope > div'), shadowRoot.getElementById('nested'), 'should get nestedDiv'); | ||
}, 'scope selector works in shadowRoot.firstChild') | ||
|
||
test(() => { | ||
assert_equals(shadowRoot.querySelector(':scope > div'), shadowRoot.getElementById('external_div'), 'should get the direct child of shadowRoot'); | ||
test (() => { | ||
assert_equals(shadowRoot.querySelector(':scope > div'), shadowRoot.getElementById('external'), 'should get the direct child of shadowRoot'); | ||
assert_equals(shadowRoot.querySelectorAll(':scope > div').length, 1, 'should get the number of direct div children of shadowRoot'); | ||
}, 'Selecting direct child of shadow root with :scope should work') | ||
|
||
test(() => { | ||
assert_equals(shadowRoot.querySelector(':scope div'), shadowRoot.getElementById('external_div'), 'should get the first div descendant of shadowRoot'); | ||
assert_equals(shadowRoot.querySelectorAll(':scope div').length, 2, 'should get the number of the div descendants of shadowRoot'); | ||
test (() => { | ||
assert_equals(shadowRoot.querySelector(':scope div'), shadowRoot.getElementById('external'), 'should get the first div descendant of shadowRoot'); | ||
assert_equals(shadowRoot.querySelectorAll(':scope div').length, 2, 'should get the number of the div descendants of shadowRoot, :scope div should not match for nestedShadow'); | ||
}, 'Selecting descendants of shadow root with :scope should work') | ||
|
||
test (() => { | ||
assert_equals(nestedShadowRoot.querySelector(':scope > div'), nestedShadowRoot.getElementById('shadow_external'), 'should get the direct child of nestedShadowRoot'); | ||
assert_equals(nestedShadowRoot.querySelectorAll(':scope > div').length, 1, 'should get the number of direct div children of nestedShadowRoot'); | ||
}, 'Selecting direct child of nested shadow root with :scope should work') | ||
|
||
test (() => { | ||
assert_equals(nestedShadowRoot.querySelector(':scope div'), nestedShadowRoot.getElementById('shadow_external'), 'should get the first div descendant of nestedShadowRoot'); | ||
assert_equals(nestedShadowRoot.querySelectorAll(':scope div').length, 2, 'should get the number of the div descendants of nestedShadowRoot'); | ||
}, 'Selecting descendants of nested shadow root with :scope should work') | ||
|
||
const documentFragment = document.createDocumentFragment(); | ||
const external_div = document.createElement('div'); | ||
external_div.setAttribute('id', 'external_div'); | ||
const nested_div = document.createElement('div'); | ||
nested_div.setAttribute('id', 'nested_div'); | ||
documentFragment.appendChild(external_div); | ||
external_div.appendChild(nested_div); | ||
|
||
test(() => { | ||
assert_equals(documentFragment.firstChild.querySelectorAll(':scope > div').length, 1, 'should get the number of direct children of external_div'); | ||
assert_equals(documentFragment.firstChild.querySelector(':scope > div'), documentFragment.getElementById('nested_div'), 'should get the first direct child of external_div'); | ||
assert_equals(documentFragment.firstChild.querySelector(':scope > div'), documentFragment.getElementById('nested_div'), 'should get the text in nesteddiv'); | ||
}, 'scope selector works in documentFragment.firstChild') | ||
|
||
test(() => { | ||
assert_equals(documentFragment.querySelector(':scope > div'), documentFragment.getElementById('external_div'), 'should get the direct child of DocumentFragment'); | ||
assert_equals(documentFragment.querySelectorAll(':scope > div').length, 1, 'should get the number of direct div children of DocumentFragment'); | ||
}, 'Selecting direct child of document fragment with :scope should work') | ||
|
||
test(() => { | ||
assert_equals(documentFragment.querySelector(':scope div'), documentFragment.getElementById('external_div'), 'should get the first div descendant of DocumentFragment'); | ||
assert_equals(documentFragment.querySelectorAll(':scope div').length, 2, 'should get the number of the div descendants of DocumentFragment'); | ||
}, 'Selecting descendants of document fragment with :scope should work') | ||
|
||
test(() => { | ||
assert_equals(shadowRoot.firstChild.querySelector(':scope'), null, 'should return null'); | ||
assert_equals(shadowRoot.firstChild.querySelectorAll(':scope').length, 0, 'should return 0'); | ||
|
||
assert_equals(shadowRoot.querySelector(':scope'), null, 'should return null'); | ||
assert_equals(shadowRoot.querySelectorAll(':scope').length, 0, 'should return 0'); | ||
}, 'querySelector() with ":scope" should return null, whether the context object is an element or a shadow root') | ||
|
||
assert_equals(documentFragment.querySelector(':scope'), null, 'should return null'); | ||
assert_equals(documentFragment.querySelectorAll(':scope').length, 0, 'should return 0'); | ||
|
||
}, 'querySelector() with ":scope" should return null, whether the context object is an element, a shadow root or a document fragment') | ||
</script> |