Skip to content
Browse files
Web Inspector: Some CSS rules for Cocoa media controls defined in inl…
…ine styles inside user agent shadow root are not correctly parsed

Reviewed by Devin Rousso.

New test case: inspector/css/getMatchedStylesForNodeUserAgentShadowRoot.html

CSSSelectorParser has an exception that allows it to parse the following rules when the parser is in UASheetMode:
video::-webkit-media-text-track-container b
video::-webkit-media-text-track-container u
video::-webkit-media-text-track-container i
video::-webkit-media-text-track-container .hidden

These are technically invalid CSS selectors because only user action pseudo classes should be allowed after pseudo
element selectors, however exceptions exist in CSSSelectorParser to explicitly allow these otherwise invalid selectors
when the pseudo element is a WebKit-specific selector and the parsing mode is UASheetMode.

Web Inspector's InspectorStyleSheet did not previously set any special parsing mode because in a majority of cases we
never need to do this parsing of UA styles text because we do not need to resolve the actual locations of rules in a
source file since they are uneditable. These rules, being declared in the shadow root, are actually editable. To support
these rules, we now check if the owner node of the style sheet is part of a user agent shadow root, and set the parsing
mode to UASheetMode if it is.

* Source/WebCore/inspector/InspectorStyleSheet.cpp:
(ParsedStyleSheet::isInUserAgentShadowTree const):

* LayoutTests/inspector/css/getMatchedStylesForNodeUserAgentShadowRoot-expected.txt: Added.
* LayoutTests/inspector/css/getMatchedStylesForNodeUserAgentShadowRoot.html: Added.

Canonical link:
git-svn-id: 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
patrickangle committed Jun 22, 2022
1 parent 6ccebd6 commit 273e6e9
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 0 deletions.
@@ -0,0 +1,12 @@
Tests for the CSS.getMatchedStylesForNode command styles inside user-agent shadow roots.

== Running test suite: CSS.getMatchedStylesForNode.UserAgentShadowRoot
-- Running test case: CSS.getMatchedStylesForNode.UserAgentShadowRoot.StyleFollowingNormallyInvalidSelector
PASS: Should have matched rule for selector '.media-controls .time-label, .media-controls .status-label'
PASS: Should have matched rule for selector '.time-label'
PASS: Should have matched rule for selector '.media-controls-container, .media-controls-container *'
PASS: Should have matched rule for selector ':host(audio), :host(, *'
PASS: Should have matched rule for selector '*'

@@ -0,0 +1,73 @@
<!DOCTYPE html>
<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
function test()
let suite = InspectorTest.createAsyncSuite("CSS.getMatchedStylesForNode.UserAgentShadowRoot");

function nodeForId(id) {
return WI.domManager.nodeForId(id)

function addTestCase({name, description, resolveTargetDOMNode, expectedSelectors})
async test() {
let documentNode = await WI.domManager.requestDocument();
let domNode = await resolveTargetDOMNode(documentNode);
InspectorTest.assert(domNode, `Should find DOM Node.`);

let domNodeStyles = WI.cssManager.stylesForNode(domNode);
await domNodeStyles.refreshIfNeeded();

let matchedSelectorTexts = [];
for (let rule of domNodeStyles.matchedRules) {
let selectorText = rule.selectorText

// A sign of bad parsing from the backend is a disagreement between the individual selectors and the combined selector text.
for (let selector of rule.selectors)
InspectorTest.assert(selectorText.includes(selector.text), `Should find selector '${selector.text}' in whole selector text '${selectorText}'.`);

let matchedSelectors = => rule.selectorText);
for (let expectedSelector of expectedSelectors)
InspectorTest.expectThat(matchedSelectorTexts.includes(expectedSelector), `Should have matched rule for selector '${expectedSelector}'`);

name: "CSS.getMatchedStylesForNode.UserAgentShadowRoot.StyleFollowingNormallyInvalidSelector",
description: "Ensure that rules declared after a normaly invalid selector (that is exempted due to being in a UA Shadow Root) are correctly resolved.",
async resolveTargetDOMNode(documentNode) {
let videoElement = nodeForId(await documentNode.querySelector("#target"));
let shadowRoot = videoElement.shadowRoots()[0];
return nodeForId(await shadowRoot.querySelector(".time-label"));
expectedSelectors: [
".media-controls .time-label, .media-controls .status-label",
".media-controls-container, .media-controls-container *",
":host(audio), :host(, *",

<body onload="runTest()">
<p>Tests for the CSS.getMatchedStylesForNode command styles inside user-agent shadow roots.</p>
<video id="target" loop muted autoplay controls>
<source src="../../http/tests/resources/test.mp4" />
@@ -1436,6 +1436,12 @@ bool InspectorStyleSheet::ensureSourceData()
auto ruleSourceDataResult = makeUnique<RuleSourceDataList>();

CSSParserContext context(parserContextForDocument(m_pageStyleSheet->ownerDocument()));

// FIXME: <> Media control CSS uses out-of-spec selectors in inline user agent shadow root style
// element. See corresponding workaround in `CSSSelectorParser::extractCompoundFlags`.
if (auto* ownerNode = m_pageStyleSheet->ownerNode(); ownerNode && ownerNode->isInUserAgentShadowTree())
context.mode = UASheetMode;

StyleSheetHandler handler(m_parsedStyleSheet->text(), m_pageStyleSheet->ownerDocument(), ruleSourceDataResult.get());
CSSParser::parseSheetForInspector(context, newStyleSheet.ptr(), m_parsedStyleSheet->text(), handler);

0 comments on commit 273e6e9

Please sign in to comment.