wxt
Version:
⚡ Next-gen Web Extension Framework
78 lines (77 loc) • 2.83 kB
JavaScript
import { logger } from "../internal/logger.mjs";
import { applyPosition, createMountFunctions, mountUi } from "./shared.mjs";
import { splitShadowRootCss } from "../split-shadow-root-css.mjs";
import { browser } from "wxt/browser";
import { createIsolatedElement } from "@webext-core/isolated-element";
//#region src/utils/content-script-ui/shadow-root.ts
/** @module wxt/utils/content-script-ui/shadow-root */
/**
* Create a content script UI inside a
* [`ShadowRoot`](https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot).
*
* > This function is async because it has to load the CSS via a network call.
*
* @see https://wxt.dev/guide/essentials/content-scripts.html#shadow-root
*/
async function createShadowRootUi(ctx, options) {
const instanceId = Math.random().toString(36).substring(2, 15);
const css = [];
if (!options.inheritStyles) css.push(`/* WXT Shadow Root Reset */ :host{all:initial !important;}`);
if (options.css) css.push(options.css);
if (ctx.options?.cssInjectionMode === "ui") {
const entryCss = await loadCss();
css.push(entryCss.replaceAll(":root", ":host"));
}
const { shadowCss, documentCss } = splitShadowRootCss(css.join("\n").trim());
const { isolatedElement: uiContainer, parentElement: shadowHost, shadow } = await createIsolatedElement({
name: options.name,
css: { textContent: shadowCss },
mode: options.mode ?? "open",
isolateEvents: options.isolateEvents
});
let mounted;
const mount = () => {
mountUi(shadowHost, options);
applyPosition(shadowHost, uiContainer, options);
if (documentCss && !document.querySelector(`style[wxt-shadow-root-document-styles="${instanceId}"]`)) {
const style = document.createElement("style");
style.textContent = documentCss;
style.setAttribute("wxt-shadow-root-document-styles", instanceId);
(document.head ?? document.body).append(style);
}
mounted = options.onMount(uiContainer, shadow, shadowHost);
};
const remove = () => {
options.onRemove?.(mounted);
shadowHost.remove();
document.querySelector(`style[wxt-shadow-root-document-styles="${instanceId}"]`)?.remove();
while (uiContainer.lastChild) uiContainer.removeChild(uiContainer.lastChild);
mounted = void 0;
};
const mountFunctions = createMountFunctions({
mount,
remove
}, options);
ctx.onInvalidated(remove);
return {
shadow,
shadowHost,
uiContainer,
...mountFunctions,
get mounted() {
return mounted;
}
};
}
/** Load the CSS for the current entrypoint. */
async function loadCss() {
const url = browser.runtime.getURL(`/content-scripts/${import.meta.env.ENTRYPOINT}.css`);
try {
return await (await fetch(url)).text();
} catch (err) {
logger.warn(`Failed to load styles @ ${url}. Did you forget to import the stylesheet in your entrypoint?`, err);
return "";
}
}
//#endregion
export { createShadowRootUi };