fix: filter non-page targets from Playwright (issue #14)
Service workers, web workers, and iframes are now filtered out at the server level, preventing Playwright from trying to initialize these targets which caused timeouts and errors.
This commit is contained in:
@@ -2,6 +2,10 @@
|
||||
|
||||
## 0.0.44
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **Filter non-page targets from Playwright (issue #14)**: Service workers, web workers, and other non-page targets are now filtered out at the server level. This prevents Playwright from trying to initialize these targets, which would cause timeouts waiting for `executionContextCreated` events and errors on `Target.detachFromTarget`.
|
||||
|
||||
### Features
|
||||
|
||||
- **Search context lines**: `accessibilitySnapshot`, `getCleanHTML`, and `getLatestLogs` now include 5 lines of context above and below each search match
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
- generic [ref=e55]:
|
||||
- text: "Google offered in:"
|
||||
- link [ref=e56] [cursor=pointer]:
|
||||
- /url: https://www.google.com/setprefs?sig=0_-1OPkiTlOBOVFrBWG18fjvZuNsY%3D&hl=it&source=homepage&sa=X&ved=0ahUKEwjstcWBt4SSAxVe4jgGHTucPWEQ2ZgBCBU
|
||||
- /url: https://www.google.com/setprefs?sig=0_naJUnYTqN6Qpp-iLC0lAP9bnktM%3D&hl=it&source=homepage&sa=X&ved=0ahUKEwiW-c_xsoaSAxX5rZUCHdnrMyMQ2ZgBCBU
|
||||
- text: Italiano
|
||||
- contentinfo [ref=e58]:
|
||||
- generic [ref=e59]: Italy
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -763,6 +763,15 @@ export async function startPlayWriterCDPRelayServer({ port = 19988, host = '127.
|
||||
|
||||
if (method === 'Target.attachedToTarget') {
|
||||
const targetParams = params as Protocol.Target.AttachedToTargetEvent
|
||||
const targetType = targetParams.targetInfo.type
|
||||
|
||||
// Filter out non-page targets (service workers, web workers, etc.)
|
||||
// These targets can't be properly controlled through chrome.debugger API
|
||||
// and cause issues when Playwright tries to initialize them (issue #14)
|
||||
if (targetType !== 'page') {
|
||||
logger?.log(chalk.gray(`[Server] Ignoring non-page target: ${targetType} (${targetParams.targetInfo.url})`))
|
||||
return
|
||||
}
|
||||
|
||||
if (!targetParams.targetInfo.url) {
|
||||
logger?.error(chalk.red('[Extension] WARNING: Target.attachedToTarget received with empty URL!'), JSON.stringify({ method, params: targetParams, sessionId }))
|
||||
|
||||
@@ -11,7 +11,7 @@ test('formatHtmlForPrompt', async () => {
|
||||
"<!doctype html>
|
||||
<html data-redirect-timezone="1">
|
||||
<body>
|
||||
<div data-framer-hydrate-v2="{"routeId":"Zw20hns9v","localeId":"default","breakpoints":[{"hash":"2ngqvi","mediaQuery":"(min-width: 1200px)"},{"hash":"11ziuji","mediaQuery":"(min-width: 810px) and (max-width: 1199.98px)"},{"hash":...237 more characters" data-framer-ssr-released-at="2026-01-09T18:19:52.935Z" data-framer-page-optimized-at="2026-01-09T18:42:33.421Z" data-framer-generated-page="">
|
||||
<div data-framer-hydrate-v2="{"routeId":"Zw20hns9v","localeId":"default","breakpoints":[{"hash":"2ngqvi","mediaQuery":"(min-width: 1200px)"},{"hash":"11ziuji","mediaQuery":"(min-width: 810px) and (max-width: 1199.98px)"},{"hash":...237 more characters" data-framer-ssr-released-at="2026-01-12T00:23:47.512Z" data-framer-page-optimized-at="2026-01-12T09:49:28.497Z" data-framer-generated-page="">
|
||||
<div data-layout-template="true" data-selection="true">
|
||||
<div>
|
||||
<nav data-framer-name="Desktop Nav" data-hide-scrollbars="true">
|
||||
@@ -2546,7 +2546,7 @@ test('formatHtmlForPrompt', async () => {
|
||||
<div data-framer-name="UI">
|
||||
<div data-framer-name="Stats">
|
||||
<div data-border="true" data-framer-name="Content">
|
||||
<p>January 9, 2026</p>
|
||||
<p>January 12, 2026</p>
|
||||
<div>
|
||||
<div data-framer-component-type="RichTextContainer">
|
||||
<p>Pageviews</p>
|
||||
@@ -2806,7 +2806,7 @@ test('formatHtmlForPrompt', async () => {
|
||||
<div data-framer-name="UI">
|
||||
<div data-framer-name="Stats">
|
||||
<div data-border="true" data-framer-name="Content">
|
||||
<p>January 9, 2026</p>
|
||||
<p>January 12, 2026</p>
|
||||
<div>
|
||||
<div data-framer-component-type="RichTextContainer">
|
||||
<p>Pageviews</p>
|
||||
@@ -3066,7 +3066,7 @@ test('formatHtmlForPrompt', async () => {
|
||||
<div data-framer-name="UI">
|
||||
<div data-framer-name="Stats">
|
||||
<div data-border="true" data-framer-name="Content">
|
||||
<p>January 9, 2026</p>
|
||||
<p>January 12, 2026</p>
|
||||
<div>
|
||||
<div data-framer-component-type="RichTextContainer">
|
||||
<p>Pageviews</p>
|
||||
|
||||
+27
-29
@@ -3541,18 +3541,21 @@ describe('Service Worker Target Tests', () => {
|
||||
return testCtx.browserContext
|
||||
}
|
||||
|
||||
it('should not disconnect when page has service worker (issue #14)', async () => {
|
||||
// This test reproduces issue #14: pages with service workers cause disconnection loops.
|
||||
// The problem is that Target.setAutoAttach attaches to service workers, and when
|
||||
// Playwright tries to enable Network/Runtime on the SW session, the extension can't
|
||||
// find a matching tab, causing errors and disconnection.
|
||||
it('should not expose service worker targets to Playwright (issue #14)', async () => {
|
||||
// This test verifies that service worker targets are NOT forwarded to Playwright.
|
||||
// Issue #14: pages with service workers cause problems because Playwright tries to
|
||||
// initialize service worker sessions with Runtime.enable/Network.enable, which
|
||||
// times out waiting for executionContextCreated (service workers don't have main frames).
|
||||
//
|
||||
// The fix is to filter out non-page targets (service_worker, worker, etc.) in the
|
||||
// server so Playwright never sees them.
|
||||
|
||||
const browserContext = getBrowserContext()
|
||||
const serviceWorker = await getExtensionServiceWorker(browserContext)
|
||||
|
||||
// Discord has a service worker - navigate there
|
||||
// web.dev has a service worker - navigate there
|
||||
const page = await browserContext.newPage()
|
||||
await page.goto('https://discord.com/login', { waitUntil: 'load' })
|
||||
await page.goto('https://web.dev/', { waitUntil: 'load' })
|
||||
await page.bringToFront()
|
||||
|
||||
// Attach extension to the page
|
||||
@@ -3561,33 +3564,28 @@ describe('Service Worker Target Tests', () => {
|
||||
})
|
||||
await new Promise(r => setTimeout(r, 500))
|
||||
|
||||
// Connect via Playwright CDP - this triggers Target.setAutoAttach which
|
||||
// will also attach to the service worker
|
||||
// Connect via Playwright CDP - this triggers Target.setAutoAttach
|
||||
const browser = await chromium.connectOverCDP(getCdpUrl({ port: TEST_PORT }))
|
||||
const context = browser.contexts()[0]
|
||||
|
||||
// Track disconnection events
|
||||
let disconnected = false
|
||||
browser.on('disconnected', () => {
|
||||
disconnected = true
|
||||
console.log('Browser disconnected!')
|
||||
})
|
||||
// Get all targets/pages that Playwright knows about
|
||||
const pages = context.pages()
|
||||
|
||||
// Wait for potential disconnection - issue says ~10s loop
|
||||
// We wait 15 seconds to be sure
|
||||
console.log('Waiting 15 seconds to check for disconnection loop...')
|
||||
await new Promise(r => setTimeout(r, 15000))
|
||||
// All pages should be actual pages, not service workers or workers
|
||||
for (const p of pages) {
|
||||
const url = p.url()
|
||||
console.log('Page URL:', url)
|
||||
// Service workers would have URLs like https://web.dev/sw.js
|
||||
expect(url).not.toMatch(/sw\.js$/i)
|
||||
expect(url).not.toMatch(/service.?worker/i)
|
||||
}
|
||||
|
||||
// Should still be connected - this is what we're testing
|
||||
expect(disconnected).toBe(false)
|
||||
expect(browser.isConnected()).toBe(true)
|
||||
// Verify we can interact with the main page
|
||||
const targetPage = pages.find(p => p.url().includes('web.dev'))
|
||||
expect(targetPage).toBeDefined()
|
||||
|
||||
// Verify we can still interact with the page
|
||||
const pages = browser.contexts()[0].pages()
|
||||
const discordPage = pages.find(p => p.url().includes('discord.com'))
|
||||
expect(discordPage).toBeDefined()
|
||||
|
||||
const url = await discordPage!.evaluate(() => window.location.href)
|
||||
expect(url).toContain('discord.com')
|
||||
const title = await targetPage!.title()
|
||||
expect(title).toBeTruthy()
|
||||
|
||||
await browser.close()
|
||||
await page.close()
|
||||
|
||||
Reference in New Issue
Block a user