Skip to content

Commit

Permalink
add inject-css-in-shadow-dom scriplet
Browse files Browse the repository at this point in the history
  • Loading branch information
stanislav-atr committed Jan 17, 2023
1 parent 59528ff commit c8808ba
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/helpers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ export * from './parse-flags';
export * from './parse-keyword-value';
export * from './random-id';
export * from './throttle';
export * from './shadow-dom-utils';
37 changes: 37 additions & 0 deletions src/helpers/shadow-dom-utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* Function to make arbitrary operations on shadow root element,
* to be passed as callback to hijackAttachShadow
*
* @callback attachShadowCallback
* @param {HTMLElement} shadowRoot
* @returns {void}
*/

/**
* Overrides attachShadow method of Element API on a given context
* to pass retrieved shadowRoots to callback
*
* @param {Object} context e.g global window object or contentWindow of an iframe
* @param {attachShadowCallback} callback callback to call on shadow root
*/
export const hijackAttachShadow = (context, callback) => {
const originalAttachShadow = context.Element.prototype.attachShadow;
// eslint-disable-next-line func-names, no-param-reassign
context.Element.prototype.attachShadow = function (originalArgs) {
const shadowRoot = originalAttachShadow.call(this, originalArgs);
callback(shadowRoot);
return shadowRoot;
};

const handlerWrapper = (target, thisArg, args) => {
const shadowRoot = Reflect.apply(target, thisArg, args);
callback(shadowRoot);
return shadowRoot;
};

const attachShadowHandler = {
apply: handlerWrapper,
};

context.Element.prototype.attachShadow = new Proxy(context.Element.prototype.attachShadow, attachShadowHandler);
};
59 changes: 59 additions & 0 deletions src/scriptlets/inject-css-in-shadow-dom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {
hit,
hijackAttachShadow,
} from '../helpers/index';

/* eslint-disable max-len */
/**
* @scriptlet inject-css-in-shadow-dom
* @description
* Injects css rules into all Shadow DOM subtrees on a page
*
* **Syntax**
* ```
* example.org#%#//scriptlet('inject-css-in-shadow-dom', cssRule)
* ```
*
* - `cssRule` - required, string of comma-separated css rules
*
* **Examples**
* ```
* ! apply single style
* example.org#%#//scriptlet('inject-css-in-shadow-dom', '#advertisement { display: none !important; }')
*
* ! apply multiple css rules
* example.org#%#//scriptlet('inject-css-in-shadow-dom', '#advertisement { display: none !important; }, #content { margin-top: 0 !important; }')
* ```
*/
/* eslint-enable max-len */

export function injectCssInShadowDom(source, cssRule) {
// do nothing if browser does not support ShadowRoot
// https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot
if (!Element.prototype.attachShadow) {
return;
}

const parsedStyleRules = cssRule.split(',');
const callback = (shadowRoot) => {
const stylesheet = new CSSStyleSheet();

// fill stylesheet with rules
parsedStyleRules.forEach((rule) => stylesheet.insertRule(rule));

// attach stylesheet to shadow root so the whole subtree would be affected
// https://developer.mozilla.org/en-US/docs/Web/API/Document/adoptedStyleSheets
shadowRoot.adoptedStyleSheets = [...shadowRoot.adoptedStyleSheets, stylesheet];
};

hijackAttachShadow(window, callback);
}

injectCssInShadowDom.names = [
'inject-css-in-shadow-dom',
];

injectCssInShadowDom.injections = [
hit,
hijackAttachShadow,
];
1 change: 1 addition & 0 deletions src/scriptlets/scriptlets-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,4 @@ export * from './trusted-set-cookie-reload';
export * from './trusted-replace-fetch-response';
export * from './trusted-set-local-storage-item';
export * from './trusted-set-constant';
export * from './inject-css-in-shadow-dom';

0 comments on commit c8808ba

Please sign in to comment.