Add workaround for Chrome/Edge css import escaping bug (#1287)
* Upgrade to typescript 4.9.5 * Apply formatting changes * Add workaround for chrome incorrect escaping bug More info: https://bugs.chromium.org/p/chromium/issues/detail?id=1472259 * Apply formatting changes * Create itchy-dryers-double.md * Create rich-jars-remember.md * Apply formatting changes * Update packages/rrweb-snapshot/src/css.ts * Apply formatting changes * Update packages/rrweb-snapshot/test/__snapshots__/integration.test.ts.snap * Apply formatting changes * Update snapshot * Apply formatting changes * Rename and refactor fixBrowserCompatibilityIssuesInCSSImports, getCssRulesString and getCssRuleString based on @eoghanmurray feedback * Apply formatting changes * Apply formatting changes
This commit is contained in:
@@ -54,12 +54,51 @@ function fixBrowserCompatibilityIssuesInCSS(cssText: string): string {
|
||||
return cssText;
|
||||
}
|
||||
|
||||
export function getCssRulesString(s: CSSStyleSheet): string | null {
|
||||
// Remove this declaration once typescript has added `CSSImportRule.supportsText` to the lib.
|
||||
declare interface CSSImportRule extends CSSRule {
|
||||
readonly href: string;
|
||||
readonly layerName: string | null;
|
||||
readonly media: MediaList;
|
||||
readonly styleSheet: CSSStyleSheet;
|
||||
/**
|
||||
* experimental API, currently only supported in firefox
|
||||
* https://developer.mozilla.org/en-US/docs/Web/API/CSSImportRule/supportsText
|
||||
*/
|
||||
readonly supportsText?: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Browsers sometimes incorrectly escape `@import` on `.cssText` statements.
|
||||
* This function tries to correct the escaping.
|
||||
* more info: https://bugs.chromium.org/p/chromium/issues/detail?id=1472259
|
||||
* @param cssImportRule
|
||||
* @returns `cssText` with browser inconsistencies fixed, or null if not applicable.
|
||||
*/
|
||||
export function escapeImportStatement(rule: CSSImportRule): string {
|
||||
const { cssText } = rule;
|
||||
if (cssText.split('"').length < 3) return cssText;
|
||||
|
||||
const statement = ['@import', `url(${JSON.stringify(rule.href)})`];
|
||||
if (rule.layerName === '') {
|
||||
statement.push(`layer`);
|
||||
} else if (rule.layerName) {
|
||||
statement.push(`layer(${rule.layerName})`);
|
||||
}
|
||||
if (rule.supportsText) {
|
||||
statement.push(`supports(${rule.supportsText})`);
|
||||
}
|
||||
if (rule.media.length) {
|
||||
statement.push(rule.media.mediaText);
|
||||
}
|
||||
return statement.join(' ') + ';';
|
||||
}
|
||||
|
||||
export function stringifyStylesheet(s: CSSStyleSheet): string | null {
|
||||
try {
|
||||
const rules = s.rules || s.cssRules;
|
||||
return rules
|
||||
? fixBrowserCompatibilityIssuesInCSS(
|
||||
Array.from(rules).map(getCssRuleString).join(''),
|
||||
Array.from(rules).map(stringifyRule).join(''),
|
||||
)
|
||||
: null;
|
||||
} catch (error) {
|
||||
@@ -67,16 +106,22 @@ export function getCssRulesString(s: CSSStyleSheet): string | null {
|
||||
}
|
||||
}
|
||||
|
||||
export function getCssRuleString(rule: CSSRule): string {
|
||||
let cssStringified = rule.cssText;
|
||||
export function stringifyRule(rule: CSSRule): string {
|
||||
let importStringified;
|
||||
if (isCSSImportRule(rule)) {
|
||||
try {
|
||||
cssStringified = getCssRulesString(rule.styleSheet) || cssStringified;
|
||||
} catch {
|
||||
importStringified =
|
||||
// for same-origin stylesheets,
|
||||
// we can access the imported stylesheet rules directly
|
||||
stringifyStylesheet(rule.styleSheet) ||
|
||||
// work around browser issues with the raw string `@import url(...)` statement
|
||||
escapeImportStatement(rule);
|
||||
} catch (error) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
return validateStringifiedCssRule(cssStringified);
|
||||
|
||||
return validateStringifiedCssRule(importStringified || rule.cssText);
|
||||
}
|
||||
|
||||
export function validateStringifiedCssRule(cssStringified: string): string {
|
||||
|
||||
Reference in New Issue
Block a user