Skip to content

Commit

Permalink
Replace relative URLs with absolute URLs when stringifying stylesheets
Browse files Browse the repository at this point in the history
  • Loading branch information
jeffdnguyen committed Jul 16, 2024
1 parent 40bbc25 commit d685bc6
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 71 deletions.
67 changes: 3 additions & 64 deletions packages/rrweb-snapshot/src/snapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
getInputType,
toLowerCase,
extractFileExtension,
absoluteToStylesheet,
} from './utils';

let _id = 1;
Expand Down Expand Up @@ -53,71 +54,9 @@ function getValidTagName(element: HTMLElement): Lowercase<string> {
return processedTagName;
}

function extractOrigin(url: string): string {
let origin = '';
if (url.indexOf('//') > -1) {
origin = url.split('/').slice(0, 3).join('/');
} else {
origin = url.split('/')[0];
}
origin = origin.split('?')[0];
return origin;
}

let canvasService: HTMLCanvasElement | null;
let canvasCtx: CanvasRenderingContext2D | null;

const URL_IN_CSS_REF = /url\((?:(')([^']*)'|(")(.*?)"|([^)]*))\)/gm;
const URL_PROTOCOL_MATCH = /^(?:[a-z+]+:)?\/\//i;
const URL_WWW_MATCH = /^www\..*/i;
const DATA_URI = /^(data:)([^,]*),(.*)/i;
export function absoluteToStylesheet(
cssText: string | null,
href: string,
): string {
return (cssText || '').replace(
URL_IN_CSS_REF,
(
origin: string,
quote1: string,
path1: string,
quote2: string,
path2: string,
path3: string,
) => {
const filePath = path1 || path2 || path3;
const maybeQuote = quote1 || quote2 || '';
if (!filePath) {
return origin;
}
if (URL_PROTOCOL_MATCH.test(filePath) || URL_WWW_MATCH.test(filePath)) {
return `url(${maybeQuote}${filePath}${maybeQuote})`;
}
if (DATA_URI.test(filePath)) {
return `url(${maybeQuote}${filePath}${maybeQuote})`;
}
if (filePath[0] === '/') {
return `url(${maybeQuote}${
extractOrigin(href) + filePath
}${maybeQuote})`;
}
const stack = href.split('/');
const parts = filePath.split('/');
stack.pop();
for (const part of parts) {
if (part === '.') {
continue;
} else if (part === '..') {
stack.pop();
} else {
stack.push(part);
}
}
return `url(${maybeQuote}${stack.join('/')}${maybeQuote})`;
},
);
}

// eslint-disable-next-line no-control-regex
const SRCSET_NOT_SPACES = /^[^ \t\n\r\u000c]+/; // Don't use \s, to avoid matching non-breaking space
// eslint-disable-next-line no-control-regex
Expand Down Expand Up @@ -664,7 +603,7 @@ function serializeElementNode(
if (cssText) {
delete attributes.rel;
delete attributes.href;
attributes._cssText = absoluteToStylesheet(cssText, stylesheet!.href!);
attributes._cssText = cssText;
}
}
// dynamic stylesheet
Expand All @@ -678,7 +617,7 @@ function serializeElementNode(
(n as HTMLStyleElement).sheet as CSSStyleSheet,
);
if (cssText) {
attributes._cssText = absoluteToStylesheet(cssText, getHref(doc));
attributes._cssText = cssText;
}
}
// form fields
Expand Down
74 changes: 69 additions & 5 deletions packages/rrweb-snapshot/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,13 @@ export function escapeImportStatement(rule: CSSImportRule): string {
export function stringifyStylesheet(s: CSSStyleSheet): string | null {
try {
const rules = s.rules || s.cssRules;
return rules
? fixBrowserCompatibilityIssuesInCSS(
Array.from(rules, stringifyRule).join(''),
)
: null;
const stringifiedRules = Array.from(rules, stringifyRule)
.map((rule) => {
return s.href ? absoluteToStylesheet(rule, s.href) : rule;
})
.join('');

return rules ? fixBrowserCompatibilityIssuesInCSS(stringifiedRules) : null;
} catch (error) {
return null;
}
Expand Down Expand Up @@ -351,3 +353,65 @@ export function extractFileExtension(
const match = url.pathname.match(regex);
return match?.[1] ?? null;
}

function extractOrigin(url: string): string {
let origin = '';
if (url.indexOf('//') > -1) {
origin = url.split('/').slice(0, 3).join('/');
} else {
origin = url.split('/')[0];
}
origin = origin.split('?')[0];
return origin;
}

const URL_IN_CSS_REF = /url\((?:(')([^']*)'|(")(.*?)"|([^)]*))\)/gm;
const URL_PROTOCOL_MATCH = /^(?:[a-z+]+:)?\/\//i;
const URL_WWW_MATCH = /^www\..*/i;
const DATA_URI = /^(data:)([^,]*),(.*)/i;
export function absoluteToStylesheet(
cssText: string | null,
href: string,
): string {
return (cssText || '').replace(
URL_IN_CSS_REF,
(
origin: string,
quote1: string,
path1: string,
quote2: string,
path2: string,
path3: string,
) => {
const filePath = path1 || path2 || path3;
const maybeQuote = quote1 || quote2 || '';
if (!filePath) {
return origin;
}
if (URL_PROTOCOL_MATCH.test(filePath) || URL_WWW_MATCH.test(filePath)) {
return `url(${maybeQuote}${filePath}${maybeQuote})`;
}
if (DATA_URI.test(filePath)) {
return `url(${maybeQuote}${filePath}${maybeQuote})`;
}
if (filePath[0] === '/') {
return `url(${maybeQuote}${
extractOrigin(href) + filePath
}${maybeQuote})`;
}
const stack = href.split('/');
const parts = filePath.split('/');
stack.pop();
for (const part of parts) {
if (part === '.') {
continue;
} else if (part === '..') {
stack.pop();
} else {
stack.push(part);
}
}
return `url(${maybeQuote}${stack.join('/')}${maybeQuote})`;
},
);
}
3 changes: 1 addition & 2 deletions packages/rrweb-snapshot/test/snapshot.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@
import { JSDOM } from 'jsdom';
import { describe, it, expect } from 'vitest';
import {
absoluteToStylesheet,
serializeNodeWithId,
_isBlockedElement,
} from '../src/snapshot';
import snapshot from '../src/snapshot';
import { serializedNodeWithId, elementNode } from '../src/types';
import { Mirror } from '../src/utils';
import { Mirror, absoluteToStylesheet } from '../src/utils';

describe('absolute url to stylesheet', () => {
const href = 'http://localhost/css/style.css';
Expand Down

0 comments on commit d685bc6

Please sign in to comment.