diff --git a/packages/rrweb-snapshot/src/utils.ts b/packages/rrweb-snapshot/src/utils.ts index 8f63e44f08..75e9a18008 100644 --- a/packages/rrweb-snapshot/src/utils.ts +++ b/packages/rrweb-snapshot/src/utils.ts @@ -24,10 +24,38 @@ export function isNativeShadowDom(shadowRoot: ShadowRoot) { return Object.prototype.toString.call(shadowRoot) === '[object ShadowRoot]'; } +/** + * Browsers sometimes destructively modify the css rules they receive. + * This function tries to rectify the modifications the browser made to make it more cross platform compatible. + * @param cssText - output of `CSSStyleRule.cssText` + * @returns `cssText` with browser inconsistencies fixed. + */ +function fixBrowserCompatibilityIssuesInCSS(cssText: string): string { + /** + * Chrome outputs `-webkit-background-clip` as `background-clip` in `CSSStyleRule.cssText`. + * But then Chrome ignores `background-clip` as css input. + * Re-introduce `-webkit-background-clip` to fix this issue. + */ + if ( + cssText.includes(' background-clip: text;') && + !cssText.includes(' -webkit-background-clip: text;') + ) { + cssText = cssText.replace( + ' background-clip: text;', + ' -webkit-background-clip: text; background-clip: text;', + ); + } + return cssText; +} + export function getCssRulesString(s: CSSStyleSheet): string | null { try { const rules = s.rules || s.cssRules; - return rules ? Array.from(rules).map(getCssRuleString).join('') : null; + return rules + ? fixBrowserCompatibilityIssuesInCSS( + Array.from(rules).map(getCssRuleString).join(''), + ) + : null; } catch (error) { return null; } diff --git a/packages/rrweb-snapshot/test/__snapshots__/integration.test.ts.snap b/packages/rrweb-snapshot/test/__snapshots__/integration.test.ts.snap index 0706eba11c..29acb2b993 100644 --- a/packages/rrweb-snapshot/test/__snapshots__/integration.test.ts.snap +++ b/packages/rrweb-snapshot/test/__snapshots__/integration.test.ts.snap @@ -149,6 +149,20 @@ exports[`integration tests [html file]: about-mozilla.html 1`] = `

" `; +exports[`integration tests [html file]: background-clip-text.html 1`] = ` +" + + + + Document + + + +

The background is clipped to the foreground text.

+ + " +`; + exports[`integration tests [html file]: basic.html 1`] = ` " @@ -330,6 +344,25 @@ exports[`integration tests [html file]: picture.html 1`] = ` " `; +exports[`integration tests [html file]: picture-blob.html 1`] = ` +" + \\"This + + " +`; + +exports[`integration tests [html file]: picture-blob-in-frame.html 1`] = ` +" + + " +`; + +exports[`integration tests [html file]: picture-in-frame.html 1`] = ` +" + + " +`; + exports[`integration tests [html file]: preload.html 1`] = ` " diff --git a/packages/rrweb-snapshot/test/html/background-clip-text.html b/packages/rrweb-snapshot/test/html/background-clip-text.html new file mode 100644 index 0000000000..e4f1991a53 --- /dev/null +++ b/packages/rrweb-snapshot/test/html/background-clip-text.html @@ -0,0 +1,33 @@ + + + + + + + Document + + + +

The background is clipped to the foreground text.

+ + + diff --git a/packages/rrweb-snapshot/test/integration.test.ts b/packages/rrweb-snapshot/test/integration.test.ts index 6ee32f9426..a9e0a34d68 100644 --- a/packages/rrweb-snapshot/test/integration.test.ts +++ b/packages/rrweb-snapshot/test/integration.test.ts @@ -276,6 +276,23 @@ iframe.contentDocument.querySelector('center').clientHeight assert(snapshot.includes('"rr_dataURL"')); assert(snapshot.includes('data:image/webp;base64,')); }); + + it('should save background-clip: text; as the more compatible -webkit-background-clip: test;', async () => { + const page: puppeteer.Page = await browser.newPage(); + await page.goto(`http://localhost:3030/html/background-clip-text.html`, { + waitUntil: 'load', + }); + await waitForRAF(page); // wait for page to render + await page.evaluate(`${code} + window.snapshot = rrweb.snapshot(document, { + inlineStylesheet: true, + })`); + await page.waitFor(100); + const snapshot = (await page.evaluate( + 'JSON.stringify(window.snapshot, null, 2);', + )) as string; + assert(snapshot.includes('-webkit-background-clip: text;')); + }); }); describe('iframe integration tests', function (this: ISuite) {