diff --git a/packages/wujie-core/__test__/integration/adoptedStyleSheets.test.ts b/packages/wujie-core/__test__/integration/adoptedStyleSheets.test.ts new file mode 100644 index 000000000..f0cfe2891 --- /dev/null +++ b/packages/wujie-core/__test__/integration/adoptedStyleSheets.test.ts @@ -0,0 +1,81 @@ +import { awaitConsoleLogMessage } from "./utils"; +import { reactMainAppInfoMap, vueMainAppInfoMap, vueMainAppNameList, reactMainAppNameList } from "./common"; + +const generateTest = ( + AppInfoMap: typeof reactMainAppInfoMap | typeof vueMainAppInfoMap, + AppNameList: typeof vueMainAppNameList | typeof reactMainAppNameList, +) => { + AppNameList.slice(0, 1).forEach((appName) => { + it("adoptedStyleSheets proxy test", async () => { + const childApplicationMountedPromise = awaitConsoleLogMessage(page, AppInfoMap[appName].mountedMessage); + await page.click(AppInfoMap[appName].linkSelector); + await childApplicationMountedPromise; + + const result = await page.evaluate((childName) => { + const childWindowCollection = [window[0], window[1], window[2], window[3], window[4], window[5]]; + const childWindow: any = childWindowCollection.find((itemWindow) => itemWindow.name === childName); + const sandbox = childWindow.__WUJIE; + + if (sandbox.degrade) { + return { skipped: true, reason: "degrade mode" }; + } + + const shadowRoot = sandbox.shadowRoot as ShadowRoot; + const iframeDocument = childWindow.document; + + const sheet = new childWindow.CSSStyleSheet(); + sheet.replaceSync("body { --test-var: 1; }"); + + iframeDocument.adoptedStyleSheets = [sheet]; + + const isSameReference = iframeDocument.adoptedStyleSheets === shadowRoot.adoptedStyleSheets; + const hasSheet = shadowRoot.adoptedStyleSheets.length === 1; + + iframeDocument.adoptedStyleSheets = []; + const isCleared = shadowRoot.adoptedStyleSheets.length === 0; + + return { + skipped: false, + isSameReference, + hasSheet, + isCleared, + }; + }, appName); + + if (result.skipped) { + console.log(`Skipped: ${result.reason}`); + return; + } + + expect(result.isSameReference).toBe(true); + expect(result.hasSheet).toBe(true); + expect(result.isCleared).toBe(true); + }); + }); +}; + +describe("main react adoptedStyleSheets", () => { + beforeAll(async () => { + await page.evaluateOnNewDocument(() => { + localStorage.clear(); + localStorage.setItem("preload", "false"); + localStorage.setItem("degrade", "false"); + }); + await page.goto("http://localhost:7700/"); + }); + + generateTest(reactMainAppInfoMap, reactMainAppNameList); +}); + +describe("main vue adoptedStyleSheets", () => { + beforeAll(async () => { + await page.evaluateOnNewDocument(() => { + localStorage.clear(); + localStorage.setItem("preload", "false"); + localStorage.setItem("degrade", "false"); + }); + await page.goto("http://localhost:8000/"); + }); + + generateTest(vueMainAppInfoMap, vueMainAppNameList); +}); diff --git a/packages/wujie-core/src/shadow.ts b/packages/wujie-core/src/shadow.ts index 130b0654c..15465fbc3 100644 --- a/packages/wujie-core/src/shadow.ts +++ b/packages/wujie-core/src/shadow.ts @@ -46,6 +46,28 @@ export function defineWujieWebComponent() { const sandbox = getWujieById(this.getAttribute(WUJIE_APP_ID)); patchElementEffect(shadowRoot, sandbox.iframe.contentWindow); sandbox.shadowRoot = shadowRoot; + // 将 document.adoptedStyleSheets 代理到 shadowRoot.adoptedStyleSheets + if (!sandbox.degrade) { + const iframeWindow = sandbox.iframe.contentWindow; + const descriptor: PropertyDescriptor = { + configurable: true, + enumerable: true, + get: () => shadowRoot.adoptedStyleSheets, + set: (sheets: CSSStyleSheet[]) => { + shadowRoot.adoptedStyleSheets = sheets; + }, + }; + try { + Object.defineProperty(iframeWindow.Document.prototype, "adoptedStyleSheets", descriptor); + } catch (e) { + /* ignore */ + } + try { + Object.defineProperty(iframeWindow.document, "adoptedStyleSheets", descriptor); + } catch (e) { + /* ignore */ + } + } } disconnectedCallback(): void {