Accessing shadow DOM elements

How to access shadow DOM elements

Shadow DOM elements are elements that are isolated from the main DOM of the web page: instead of being part of the main document subtree, they belong to a document fragment which is just another subtree of nodes that are not as vulnerable to scripts (and styles) as normal DOM nodes are.

We can see and inspect them in the browser but we cannot reach them by writing a normal selector.

We are currently able to tackle:

  • open or closed shadow roots (see paragraph about browser compatibility below): they are created by calling the DOM API element.attachShadow or via declarative shadow DOM. CV providers can use shadow roots to hide the elements that distinguish the sponsored content from the non-sponsored content.

  • svg-use pattern: another encapsulating pattern used by CV providers. In SVG, the element is used to reference and reuse existing elements defined elsewhere in the document, whose id matches the value stored in the href or xlink:href attribute.

The demarcators

The following snippets can handle the new demarcators:

  • hide-if-contains

  • hide-if-contains-image

  • hide-if-contains-similar-text

  • hide-if-contains-visible-text (^^sh^^ only, does NOT support ^^svg^^)

  • hide-if-contains-and-matches-style

  • hide-if-has-and-matches-style (currently only in selector and searchSelector params, not in the search parameter!)

  • hide-if-labelled-by

  • simulate-mouse-event

The blank spaces around the demarcators are irrelevant and will be ignored: "div.shadowRootParent ^^sh^^ span.childNode" will be behave the same as "div.shadowRootParent^^sh^^span.childNode". In this document the demarcators are surround by blank spaces for readability's sake. More than one demarcator can be used in the same selector, in order to access nested encapsulated elements.

type of encapsulationdemarcatorselector examplehow it works

open or closed shadow root

^^sh^^

.shadowRootParent ^^sh^^ span.childNode

The selector is split at the demarcator. The first part of the query is executed. This leads to the shadow root parent. Then the shadow root is accessed with this specific API. At this point nodes inside the shadow root are targeted by using the second part of the selector.

svg-use pattern

^^svg^^

.shadowRootParent ^^svg^^

The 1st part of the query before the demarcator is executed, which will likely point to a element. The value of the xlink:href or href attribute is extracted from the found element and it is used to find the matching original element by its id. The referenced ids until no more demarcators are found in the selector.

Browser compatibility

While all our snippets are supported from Chrome 77+ or Opera 64+, keep in mind that snippets that use the new demarcator for the shadow root (^^sh^^) will work only from Chrome 88+ and Opera 74+, because we rely on an API that was introduced with Chrome 88 / Opera 74.

Since the API we use to access shadow-roots is available only for extensions, the ^^sh^^ demarcator won't work on Chromium-based browsers.

Even though the snippet filters using the new ^^sh^^ demarcator are expected to fail in older browsers and Chromium browsers, no breakage, visible errors or disruptive behaviors are expected. The filters will fail silently without interfering with the user experience.

No limitations have been observed on our oldest supported Firefox version, Firefox 68.

How to write filters

Shadow roots

Write your CSS selector as usual: the first part of the selector should target the closest shadow root parent in the light DOM, then use the demarcator ^^sh^^ and continue by adding any other reference to nodes inside the shadow root.

1. Simple shadow root example:

<div class="myDiv">
   #shadow-root (closed)
  <span id="sponsored">Sponsored</span>
</div>

Filter example:

example.com#$#hide-if-contains /Sponsored/ '.myDiv' '.myDiv ^^sh^^ #sponsored'

2. Nested shadow root example:

<div class="myDiv">
    #shadow-root (closed)
        <span class="mySpan">
            #shadow-root (closed)
                <span id="sponsored">Sponsored</span>
        </span>
</div>

Filter example:

example.com#$#hide-if-contains /Sponsored/ '.myDiv' '.myDiv ^^sh^^ .mySpan ^^sh^^ #sponsored'

Svg-use pattern

The first part of the selector will always point to the parent of the enclosed DOM element (i.e.: to the <use> element).

When entering the ^^svg^^ the snippet will take care of finding the original content by the id referenced in or , which in the following example is <text>, so further selectors are needed after ^^svg^^

1. Simple svg-use:

<svg>
    <text id="sponsored">Sponsored</text>
</svg>
 
<div class="myDiv">
    <svg class="mySvg">
        <use href="#sponsored">
            #shadow-root (user-agent)
                <text id="sponsored">Sponsored</text>
        </use>
    </svg>
</div>

Filter example:

example.com#$#hide-if-contains /Sponsored/ '.myDiv' '.myDiv .mySvg use ^^svg^^'

2. Referenced element has children:

In this case the "Sponsored" string lives in a child of the referenced element, so after the ^^svg^^ demarcator we add the the selector to target the subtree element that hosts the string we are looking for.

<svg>
    <text id="sponsored">
        <tspan>Sponsored</tspan>
    </text>
</svg>
 
<div class="myDiv">
    <svg class="mySvg">
        <use href="#sponsored">
            #shadow-root (user-agent)
                <text id="sponsored">
                    <tspan>Sponsored</tspan>
                </text>
        </use>
    </svg>
</div>

Filter example:

example.com#$#hide-if-contains /Sponsored/ '.myDiv' '.myDiv .mySvg use ^^svg^^ tspan'

3. Nested <use> elements:

More than one ^^svg^^ demarcator can be used in the same selector, allowing to follow a series of nested <use> tags.

<svg>
    <text id="sponsored">Sponsored</text>
</svg>
<svg>
    <use id="nested-sponsored" href="#sponsored">
        #shadow-root (user-agent)
            <text id="sponsored">Sponsored</text>
    </use>
</svg>
 
 
<div class="myDiv">
    <svg class="mySvg">
        <use href="#nested-sponsored">
            #shadow-root (user-agent)
                 <use id="nested-sponsored" href="sponsored">
                      #shadow-root (user-agent)
                          <text id="sponsored">Sponsored</text>
                 </use>
        </use>
    </svg>
</div>

Filter example:

example.com#$#hide-if-contains /Sponsored/ '.myDiv' '.myDiv .mySvg use ^^svg^^ ^^svg^^'

Last updated