diff --git a/packages/trace-viewer/src/snapshotRenderer.ts b/packages/trace-viewer/src/snapshotRenderer.ts index b98b359f76..9092d97e8c 100644 --- a/packages/trace-viewer/src/snapshotRenderer.ts +++ b/packages/trace-viewer/src/snapshotRenderer.ts @@ -401,13 +401,20 @@ export function rewriteURLForCustomProtocol(href: string): string { // Pass through if possible. const isBlob = url.protocol === 'blob:'; - if (!isBlob && schemas.includes(url.protocol)) + const isFile = url.protocol === 'file:'; + if (!isBlob && !isFile && schemas.includes(url.protocol)) return href; - // Rewrite blob and custom schemas. + // Rewrite blob, file and custom schemas. const prefix = 'pw-' + url.protocol.slice(0, url.protocol.length - 1); - url.protocol = 'https:'; + if (!isFile) + url.protocol = 'https:'; url.hostname = url.hostname ? `${prefix}--${url.hostname}` : prefix; + if (isFile) { + // File URIs can only have their protocol changed after the hostname + // is set. (For all other URIs, we must set the protocol first.) + url.protocol = 'https:'; + } return url.toString(); } catch { return href; @@ -423,7 +430,8 @@ const urlInCSSRegex = /url\(['"]?([\w-]+:)\/\//ig; function rewriteURLsInStyleSheetForCustomProtocol(text: string): string { return text.replace(urlInCSSRegex, (match: string, protocol: string) => { const isBlob = protocol === 'blob:'; - if (!isBlob && schemas.includes(protocol)) + const isFile = protocol === 'file:'; + if (!isBlob && !isFile && schemas.includes(protocol)) return match; return match.replace(protocol + '//', `https://pw-${protocol.slice(0, -1)}--`); }); diff --git a/tests/library/trace-viewer.spec.ts b/tests/library/trace-viewer.spec.ts index e0c5055692..c2634df148 100644 --- a/tests/library/trace-viewer.spec.ts +++ b/tests/library/trace-viewer.spec.ts @@ -18,6 +18,7 @@ import type { TraceViewerFixtures } from '../config/traceViewerFixtures'; import { traceViewerFixtures } from '../config/traceViewerFixtures'; import fs from 'fs'; import path from 'path'; +import { pathToFileURL } from 'url'; import { expect, playwrightTest } from '../config/browserTest'; import type { FrameLocator } from '@playwright/test'; @@ -558,6 +559,17 @@ test('should handle src=blob', async ({ page, server, runAndTrace, browserName } expect(size).toBe(10); }); +test('should handle file URIs', async ({ page, runAndTrace, browserName }) => { + test.skip(browserName !== 'chromium'); + + const traceViewer = await runAndTrace(async () => { + await page.goto(pathToFileURL(path.join(__dirname, '..', 'assets', 'one-style.html')).href); + }); + + const frame = await traceViewer.snapshotFrame('goto'); + await expect(frame.locator('body')).toHaveCSS('background-color', 'rgb(255, 192, 203)'); +}); + test('should preserve currentSrc', async ({ browser, server, showTraceViewer }) => { const traceFile = test.info().outputPath('trace.zip'); const page = await browser.newPage({ deviceScaleFactor: 3 });