diff --git a/packages/playwright-core/src/server/injected/highlight.css b/packages/playwright-core/src/server/injected/highlight.css index 9ee0370720..5126f74c4f 100644 --- a/packages/playwright-core/src/server/injected/highlight.css +++ b/packages/playwright-core/src/server/injected/highlight.css @@ -20,6 +20,11 @@ color: #333; } +svg { + position: absolute; + height: 0; +} + x-pw-tooltip { backdrop-filter: blur(5px); background-color: white; @@ -122,16 +127,10 @@ x-pw-tool-gripper:active { } x-pw-tool-gripper > x-div { - width: 100%; - height: 100%; - -webkit-mask-repeat: no-repeat; - -webkit-mask-position: center; - -webkit-mask-size: 20px; - mask-repeat: no-repeat; - mask-position: center; - mask-size: 16px; - -webkit-mask-image: url("data:image/svg+xml;utf8,"); - mask-image: url("data:image/svg+xml;utf8,"); + width: 16px; + height: 16px; + margin: 6px 4px; + clip-path: url(#x-pw-gripper-icon); background-color: #555555; } @@ -169,14 +168,9 @@ x-pw-tool-item.active:not(.disabled):hover { } x-pw-tool-item > x-div { - width: 100%; - height: 100%; - -webkit-mask-repeat: no-repeat; - -webkit-mask-position: center; - -webkit-mask-size: 20px; - mask-repeat: no-repeat; - mask-position: center; - mask-size: 16px; + width: 16px; + height: 16px; + margin: 6px; background-color: #3a3a3a; } @@ -203,51 +197,41 @@ x-pw-tool-item.accept > x-div { x-pw-tool-item.record > x-div { /* codicon: circle-large-filled */ - -webkit-mask-image: url("data:image/svg+xml;utf8,"); - mask-image: url("data:image/svg+xml;utf8,"); + clip-path: url(#x-pw-record-icon); } x-pw-tool-item.pick-locator > x-div { /* codicon: inspect */ - -webkit-mask-image: url("data:image/svg+xml;utf8,"); - mask-image: url("data:image/svg+xml;utf8,"); + clip-path: url(#x-pw-pick-locator-icon); } x-pw-tool-item.text > x-div { /* codicon: whole-word */ - -webkit-mask-image: url("data:image/svg+xml;utf8,"); - mask-image: url("data:image/svg+xml;utf8,"); + clip-path: url(#x-pw-text-icon); } x-pw-tool-item.visibility > x-div { /* codicon: eye */ - -webkit-mask-image: url("data:image/svg+xml;utf8,"); - mask-image: url("data:image/svg+xml;utf8,"); + clip-path: url(#x-pw-visibility-icon); } x-pw-tool-item.value > x-div { /* codicon: symbol-constant */ - -webkit-mask-image: url("data:image/svg+xml;utf8,"); - mask-image: url("data:image/svg+xml;utf8,"); + clip-path: url(#x-pw-value-icon); } x-pw-tool-item.accept > x-div { - -webkit-mask-image: url("data:image/svg+xml;utf8,"); - mask-image: url("data:image/svg+xml;utf8,"); + clip-path: url(#x-pw-accept-icon); } x-pw-tool-item.cancel > x-div { - -webkit-mask-image: url("data:image/svg+xml;utf8,"); - mask-image: url("data:image/svg+xml;utf8,"); + clip-path: url(#x-pw-cancel-icon); } x-pw-tool-item.succeeded > x-div { /* codicon: pass */ - -webkit-mask-image: url("data:image/svg+xml;utf8,") !important; - mask-image: url("data:image/svg+xml;utf8,") !important; + clip-path: url(#x-pw-succeeded-icon); background-color: #388a34 !important; - -webkit-mask-size: 18px !important; - mask-size: 18px !important; } x-pw-overlay { diff --git a/packages/playwright-core/src/server/injected/highlight.ts b/packages/playwright-core/src/server/injected/highlight.ts index 0487bf04d7..bb8c8fd123 100644 --- a/packages/playwright-core/src/server/injected/highlight.ts +++ b/packages/playwright-core/src/server/injected/highlight.ts @@ -75,10 +75,10 @@ export class Highlight { this._actionPointElement = document.createElement('x-pw-action-point'); this._actionPointElement.setAttribute('hidden', 'true'); this._glassPaneShadow = this._glassPaneElement.attachShadow({ mode: this._isUnderTest ? 'open' : 'closed' }); + const sheet = new CSSStyleSheet(); + sheet.replaceSync(highlightCSS); + this._glassPaneShadow.adoptedStyleSheets = [sheet]; this._glassPaneShadow.appendChild(this._actionPointElement); - const styleElement = document.createElement('style'); - styleElement.textContent = highlightCSS; - this._glassPaneShadow.appendChild(styleElement); } install() { diff --git a/packages/playwright-core/src/server/injected/recorder/recorder.ts b/packages/playwright-core/src/server/injected/recorder/recorder.ts index 2074ffa67f..e4b886eb17 100644 --- a/packages/playwright-core/src/server/injected/recorder/recorder.ts +++ b/packages/playwright-core/src/server/injected/recorder/recorder.ts @@ -50,6 +50,20 @@ interface RecorderTool { onScroll?(event: Event): void; } +const kIcons = ` + + + + + + + + + + + +`; + class NoneTool implements RecorderTool { cursor() { return 'default'; @@ -769,7 +783,8 @@ class Overlay { this._recorder = recorder; const document = this._recorder.injectedScript.document; this._overlayElement = document.createElement('x-pw-overlay'); - + const iconsSvgElement = new DOMParser().parseFromString(kIcons, 'image/svg+xml').documentElement; + this._overlayElement.appendChild(iconsSvgElement); const toolsListElement = document.createElement('x-pw-tools-list'); this._overlayElement.appendChild(toolsListElement); @@ -942,7 +957,7 @@ export class Recorder { private _actionSelectorModel: HighlightModel | null = null; readonly highlight: Highlight; readonly overlay: Overlay | undefined; - private _styleElement: HTMLStyleElement; + private _stylesheet: CSSStyleSheet; state: UIState = { mode: 'none', testIdAttributeName: 'data-testid', language: 'javascript', overlay: { offsetX: 0 } }; readonly document: Document; delegate: RecorderDelegate = {}; @@ -966,11 +981,11 @@ export class Recorder { this.overlay = new Overlay(this); this.overlay.setUIState(this.state); } - this._styleElement = this.document.createElement('style'); - this._styleElement.textContent = ` + this._stylesheet = new CSSStyleSheet(); + this._stylesheet.replaceSync(` body[data-pw-cursor=pointer] *, body[data-pw-cursor=pointer] *::after { cursor: pointer !important; } body[data-pw-cursor=text] *, body[data-pw-cursor=text] *::after { cursor: text !important; } - `; + `); this.installListeners(); if (injectedScript.isUnderTest) @@ -999,7 +1014,7 @@ export class Recorder { ]; this.highlight.install(); this.overlay?.install(); - this.injectedScript.document.head.appendChild(this._styleElement); + this.document.adoptedStyleSheets = [...this.document.adoptedStyleSheets, this._stylesheet]; } private _switchCurrentTool() { diff --git a/tests/library/inspector/cli-codegen-1.spec.ts b/tests/library/inspector/cli-codegen-1.spec.ts index a54ae4d421..ee6e174f12 100644 --- a/tests/library/inspector/cli-codegen-1.spec.ts +++ b/tests/library/inspector/cli-codegen-1.spec.ts @@ -15,6 +15,7 @@ */ import { test, expect } from './inspectorTest'; +import type { ConsoleMessage } from 'playwright'; test.describe('cli codegen', () => { test.skip(({ mode }) => mode !== 'default'); @@ -841,4 +842,12 @@ await page.GetByRole(AriaRole.Button, new() { Name = "Submit" }).ClickAsync();`) await page.goto(server.PREFIX + `/empty.html`); await recorder.waitForOutput('JavaScript', `await page.goto('${server.PREFIX}/empty.html');`); }); + + test('should not throw csp directive violation errors', async ({ page, openRecorder, server }) => { + await openRecorder(); + await page.goto(server.PREFIX + '/csp.html'); + const predicate = (msg: ConsoleMessage) => msg.type() === 'error' && /Content[\- ]Security[\- ]Policy/i.test(msg.text()); + await expect(page.waitForEvent('console', { predicate, timeout: 1000 })).rejects.toThrow(); + await page.pause(); + }); });