Related to - AdguardTeam/AdguardFilters#242087 (comment)
As far as I understand, current scriptlet does not work for some elements because website uses Declarative Shadow DOM https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM#declaratively_with_html and then add elements to it.
Steps to reproduce:
- Add to user rules:
fiddle.jshell.net###displayAdCard
fiddle.jshell.net#%#//scriptlet('inject-css-in-shadow-dom', '#displayAdCard, #adBanner { display: none !important; }')
- Navigate to - https://jsfiddle.net/ar7vct9L/
- Click run
Code, in case if test page will be removed
<style>
#displayAdCard { height: 250px; background: #fde68a; }
</style>
<div class="content">Outside: page content</div>
<div id="displayAdCard">Outside: page ad (a regular ## rule's job)</div>
<div id="host">
<template shadowrootmode="open">
<style>
#displayAdCard { height: 250px; background: #fde68a; }
</style>
<span>I'm in the shadow DOM</span>
<div class="content">Inside: shadow DOM content</div>
<div id="displayAdCard">Inside: ad card in the declarative shadow root</div>
</template>
</div>
<script>
// Created by the browser's HTML parser: attachShadow is never called for it.
const declarativeRoot = document.getElementById('host').shadowRoot;
const adCard = declarativeRoot.getElementById('displayAdCard');
// adCard is already connected, so adCard.getRootNode() === declarativeRoot
// when attachShadow runs. The <slot> keeps adCard's own text rendered.
adCard.attachShadow({ mode: 'open' }).innerHTML = `
<slot></slot>
<div id="adBanner">Nested: ad banner in the attached shadow root</div>
`;
const nestedRoot = adCard.shadowRoot;
const isRendered = (element) => element.getClientRects().length > 0;
console.table({
'outside content': isRendered(document.querySelector('.content')),
'outside #displayAdCard': isRendered(document.getElementById('displayAdCard')),
'inside content': isRendered(declarativeRoot.querySelector('.content')),
'inside #displayAdCard': isRendered(adCard),
'nested #adBanner': isRendered(nestedRoot.getElementById('adBanner'))
});
</script>
All yellow highlighted elements should be hidden.
#displayAdCard inside shadow root is not hidden.
Screenshot
I guess in hijackAttachShadow:
|
export const hijackAttachShadow = ( |
|
context: typeof globalThis, |
|
hostSelector: string, |
|
callback: AttachShadowCallback, |
|
): void => { |
|
const handlerWrapper = (target: AttachShadow, thisArg: Element, args: unknown[]): ShadowRoot => { |
|
const shadowRoot: ShadowRoot = Reflect.apply(target, thisArg, args); |
|
|
|
if (thisArg && thisArg.matches(hostSelector || '*')) { |
|
callback(shadowRoot); |
|
} |
|
|
|
return shadowRoot; |
|
}; |
|
|
|
const attachShadowHandler: ProxyHandler<AttachShadow> = { |
|
apply: handlerWrapper, |
|
}; |
|
|
|
context.Element.prototype.attachShadow = new Proxy<AttachShadow>( |
|
context.Element.prototype.attachShadow, |
|
attachShadowHandler, |
|
); |
|
}; |
we probably could do something like:
const hostRoot = thisArg.getRootNode();
if (hostRoot instanceof ShadowRoot) {
callback(hostRoot);
}
To inject styleSheet to this element.
Perhaps this could be controlled by an additional parameter.
Related to - AdguardTeam/AdguardFilters#242087 (comment)
As far as I understand, current scriptlet does not work for some elements because website uses Declarative Shadow DOM https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_shadow_DOM#declaratively_with_html and then add elements to it.
Steps to reproduce:
Code, in case if test page will be removed
All yellow highlighted elements should be hidden.
#displayAdCardinside shadow root is not hidden.Screenshot
I guess in
hijackAttachShadow:Scriptlets/src/helpers/shadow-dom-utils.ts
Lines 17 to 40 in 6c3693f
we probably could do something like:
To inject styleSheet to this element.
Perhaps this could be controlled by an additional parameter.