From 744a2418e13d93dac9d0cbbf6154d7a51b2cfa3d Mon Sep 17 00:00:00 2001 From: jerrychan7 Date: Sun, 1 Nov 2026 15:42:47 +0800 Subject: [PATCH 01/26] refactor(cdp-agent): move snapDOM rendering out of the screencast into a shared render The screencast loaded snapDOM, picked the page background, hid DevTools' own elements and encoded the canvas itself. Move that into page/render.ts as createRender and encode, so that Page.captureScreenshot can render with the same loaded snapDOM. The render also takes a page-coordinate clip besides 'viewport'. createCdpSession builds the render once from loadSnapdom; the screencast keeps its own error when snapDOM is not configured. --- .../src/cdp-agent/domains/page/render.ts | 66 +++++++++++++++++++ .../src/cdp-agent/domains/page/screencast.ts | 57 +++------------- .../devtools-anywhere/src/cdp-agent/index.ts | 7 +- 3 files changed, 78 insertions(+), 52 deletions(-) create mode 100644 packages/devtools-anywhere/src/cdp-agent/domains/page/render.ts diff --git a/packages/devtools-anywhere/src/cdp-agent/domains/page/render.ts b/packages/devtools-anywhere/src/cdp-agent/domains/page/render.ts new file mode 100644 index 0000000..64142d6 --- /dev/null +++ b/packages/devtools-anywhere/src/cdp-agent/domains/page/render.ts @@ -0,0 +1,66 @@ +import { isOwn } from '../../core/nodes'; + +/** A rectangle in page (document) coordinates, CSS pixels. */ +export interface PageRect { + x: number; + y: number; + width: number; + height: number; +} + +/** The part of snapDOM (`@zumer/snapdom`) the screencast and screenshots use. */ +export type Snapdom = ( + element: Element, + options: { + clip: 'viewport' | PageRect; + exclude: ((el: Element) => boolean)[]; + excludeMode: 'hide'; + backgroundColor: string; + } +) => Promise<{ toCanvas(options: { scale: number; dpr: number }): Promise }>; + +/** Loads snapDOM on demand: it is large, and only the screencast and screenshots need it. @internal */ +export type SnapdomLoader = () => Promise<{ snapdom: Snapdom }>; + +/** + * Renders the page's DOM as an image, since page scripts cannot read the pixels on screen: + * the viewport or a region of the page. DevTools' own elements are left out. + */ +export type Render = (clip: 'viewport' | PageRect, scale: number, dpr: number) => Promise; + +/** The color the browser paints behind the page: the root's or body's background, or white. */ +function background() { + for (const el of [document.documentElement, document.body]) { + const color = el && getComputedStyle(el).backgroundColor; + if (color && color !== 'transparent' && !/^rgba\(.*,\s*0\)$/.test(color)) return color; + } + return '#fff'; +} + +/** A Render on snapDOM, loaded on first use. */ +export function createRender(load: SnapdomLoader): Render { + let snapdom: Promise | null = null; + return async (clip, scale, dpr) => { + // A failed load is not kept, so that the next render loads snapDOM again. + const capture = await (snapdom ??= load().then( + m => m.snapdom, + (e: unknown) => { + snapdom = null; + throw e; + } + )); + const result = await capture(document.documentElement, { + clip, + exclude: [isOwn], + excludeMode: 'hide', + backgroundColor: background(), + }); + return result.toCanvas({ scale, dpr }); + }; +} + +/** The canvas as base64 image data, in a CDP image format (`quality` from 0 to 100). */ +export function encode(canvas: HTMLCanvasElement, format: 'png' | 'jpeg' | 'webp', quality: number | undefined) { + const url = canvas.toDataURL(`image/${format}`, (quality ?? 80) / 100); + return url.slice(url.indexOf(',') + 1); +} diff --git a/packages/devtools-anywhere/src/cdp-agent/domains/page/screencast.ts b/packages/devtools-anywhere/src/cdp-agent/domains/page/screencast.ts index 64543c4..d3fce99 100644 --- a/packages/devtools-anywhere/src/cdp-agent/domains/page/screencast.ts +++ b/packages/devtools-anywhere/src/cdp-agent/domains/page/screencast.ts @@ -1,43 +1,18 @@ import type { Protocol } from 'devtools-protocol'; import type { Context } from '../../core/context'; -import { isOwn } from '../../core/nodes'; import { CdpError } from '../../core/session'; - -/** The part of snapDOM (`@zumer/snapdom`) the screencast uses. */ -export type Snapdom = ( - element: Element, - options: { - clip: 'viewport'; - exclude: ((el: Element) => boolean)[]; - excludeMode: 'hide'; - backgroundColor: string; - } -) => Promise<{ toCanvas(options: { scale: number; dpr: number }): Promise }>; - -/** Loads snapDOM on demand: it is large, and only the screencast needs it. @internal */ -export type SnapdomLoader = () => Promise<{ snapdom: Snapdom }>; - -/** The color the browser paints behind the page: the root's or body's background, or white. */ -function background() { - for (const el of [document.documentElement, document.body]) { - const color = el && getComputedStyle(el).backgroundColor; - if (color && color !== 'transparent' && !/^rgba\(.*,\s*0\)$/.test(color)) return color; - } - return '#fff'; -} +import { encode, type Render } from './render'; /** Shortest time between two frames, in milliseconds. */ const MIN_INTERVAL = 500; /** - * Page.startScreencast: renders the viewport with snapDOM, the page's DOM redrawn as an - * image, since page scripts cannot read the pixels on screen. A frame is sent once the + * Page.startScreencast: renders the viewport with snapDOM (render.ts). A frame is sent once the * previous one is acknowledged and at most every MIN_INTERVAL ms, or every two rendering * times when rendering is slower; nothing is rendered while the page is hidden. - * DevTools' own elements are left out. */ -export function registerScreencast({ session: cdpSession }: Context, loadSnapdom: SnapdomLoader | undefined) { - if (!loadSnapdom) { +export function registerScreencast({ session: cdpSession }: Context, render: Render | undefined) { + if (!render) { cdpSession.register('Page', { startScreencast() { throw new CdpError('Screencast is not available: snapDOM is not configured'); @@ -46,39 +21,23 @@ export function registerScreencast({ session: cdpSession }: Context, loadSnapdom }); return; } - const load = loadSnapdom; + const draw = render; let frameSession = 0; let params: Protocol.Page.StartScreencastRequest | null = null; let acked = true; let timer: ReturnType | undefined; - let snapdom: Promise | null = null; async function frame(current: number) { - // A failed load is not kept, so that the retry loads snapDOM again. - const capture = await (snapdom ??= load().then( - m => m.snapdom, - (e: unknown) => { - snapdom = null; - throw e; - } - )); const start = performance.now(); const width = innerWidth; const height = innerHeight; const scale = Math.min(1, (params?.maxWidth ?? width) / width, (params?.maxHeight ?? height) / height); - const result = await capture(document.documentElement, { - clip: 'viewport', - exclude: [isOwn], - excludeMode: 'hide', - backgroundColor: background(), - }); - const canvas = await result.toCanvas({ scale, dpr: 1 }); - const png = params?.format === 'png'; - const url = canvas.toDataURL(png ? 'image/png' : 'image/jpeg', (params?.quality ?? 80) / 100); + const canvas = await draw('viewport', scale, 1); + const data = encode(canvas, params?.format === 'png' ? 'png' : 'jpeg', params?.quality); if (current !== frameSession) return; acked = false; cdpSession.emit('Page.screencastFrame', { - data: url.slice(url.indexOf(',') + 1), + data, sessionId: current, metadata: { offsetTop: 0, diff --git a/packages/devtools-anywhere/src/cdp-agent/index.ts b/packages/devtools-anywhere/src/cdp-agent/index.ts index 47a2676..7534882 100644 --- a/packages/devtools-anywhere/src/cdp-agent/index.ts +++ b/packages/devtools-anywhere/src/cdp-agent/index.ts @@ -12,7 +12,8 @@ import { registerNetwork } from './domains/network/network'; import { registerOverlay } from './domains/overlay/overlay'; import { registerPage } from './domains/page/page'; import { registerRuntime } from './domains/runtime/runtime'; -import { registerScreencast, type SnapdomLoader } from './domains/page/screencast'; +import { createRender, type SnapdomLoader } from './domains/page/render'; +import { registerScreencast } from './domains/page/screencast'; import { registerStorage } from './domains/storage/storage'; import { registerStubs } from './domains/stubs'; import { registerWebMcp } from './domains/webmcp/webmcp'; @@ -34,7 +35,7 @@ export { storeShellOpen, } from './probe/shell'; export { FRAME_ID, LOADER_ID } from './core/context'; -export type { SnapdomLoader } from './domains/page/screencast'; +export type { SnapdomLoader } from './domains/page/render'; /** @internal */ export interface CdpSessionOptions { @@ -88,7 +89,7 @@ export function createCdpSession(options: CdpSessionOptions = {}): CdpSession { registerStorage(ctx); registerDomDebugger(ctx); registerDebugger(ctx); - registerScreencast(ctx, options.loadSnapdom); + registerScreencast(ctx, options.loadSnapdom && createRender(options.loadSnapdom)); registerInput(ctx); registerWebMcp(ctx); return ctx.session; From 58cec3890197fa912fd25a2315ff1afb2beeabbb Mon Sep 17 00:00:00 2001 From: jerrychan7 Date: Sun, 1 Nov 2026 15:46:22 +0800 Subject: [PATCH 02/26] feat(cdp-agent): take screenshots with Page.captureScreenshot and report Page.getLayoutMetrics Tools and agents that drive the page over CDP can already read the DOM, but had no image of it. Page.captureScreenshot renders with snapDOM, like the screencast, and follows Chrome's parameters as checked against real Chrome: clip is in page coordinates with or without captureBeyondViewport, the image has device pixels (devicePixelRatio times clip.scale), and captureBeyondViewport without a clip takes the whole page. The response adds approximate: true, a field Chrome does not send, since the image is the DOM redrawn rather than painted pixels. Images over 4096 x 4096 pixels, Safari's canvas limit, are scaled down to fit. Page.getLayoutMetrics returns the viewport, visual viewport and content size in CSS pixels in both sets of fields, as Chrome does on an unzoomed page; DevTools uses it for node screenshots. The conformance tests compare image sizes and the clipped content with Chrome's, at device scale factor 2 too, and getLayoutMetrics field by field. --- .../domains/page/page.conformance.test.ts | 123 ++++++++++++++++++ .../src/cdp-agent/domains/page/page.ts | 26 ++++ .../src/cdp-agent/domains/page/screenshot.ts | 44 +++++++ .../devtools-anywhere/src/cdp-agent/index.ts | 8 +- 4 files changed, 199 insertions(+), 2 deletions(-) create mode 100644 packages/devtools-anywhere/src/cdp-agent/domains/page/screenshot.ts diff --git a/packages/devtools-anywhere/src/cdp-agent/domains/page/page.conformance.test.ts b/packages/devtools-anywhere/src/cdp-agent/domains/page/page.conformance.test.ts index 1654152..25c05e7 100644 --- a/packages/devtools-anywhere/src/cdp-agent/domains/page/page.conformance.test.ts +++ b/packages/devtools-anywhere/src/cdp-agent/domains/page/page.conformance.test.ts @@ -152,3 +152,126 @@ test('the screencast scales frames to maxWidth, and encodes them as asked', asyn await s.call('Page.stopScreencast'); } }); + +// Screenshots: ours renders the DOM with snapDOM, so only the size and coarse content of the +// image can match Chrome's. A page of its own: it is made long, scrolled and emulated. +test('captureScreenshot without snapDOM reports that it is not available', async () => { + await assert.rejects(t.ours.send('Page.captureScreenshot', {}), /snapDOM is not configured/); +}); + +function isScreenshot(r: unknown): r is { data: string; approximate?: unknown } { + return typeof r === 'object' && r !== null && 'data' in r && typeof r.data === 'string'; +} + +/** A page 2000 × 3000 CSS pixels with a red box at (10, 520), scrolled to (0, 500). */ +async function screenshotPage() { + assert.ok(env); + const s = await openPage(env, []); + await s.page.evaluate(() => { + document.body.innerHTML = ''; + document.body.style.cssText = 'margin:0;width:2000px;height:3000px;background:#fff'; + const box = document.createElement('div'); + box.style.cssText = 'position:absolute;left:10px;top:520px;width:100px;height:50px;background:rgb(255,0,0)'; + document.body.append(box); + scrollTo(0, 500); + }); + const snapdom = env.baseUrl + '/packages/devtools-anywhere/dist/snapdom.js'; + /** Takes a screenshot with ours, with the real snapDOM. */ + const ours = async (params: object) => { + const shot = await s.page.evaluate( + (url, p) => + window.DevtoolsAnywhere.createCdpSession({ loadSnapdom: () => import(url) }).call('Page.captureScreenshot', p), + snapdom, + params + ); + assert.ok(isScreenshot(shot), JSON.stringify(shot)); + return shot; + }; + const chrome = (params: object) => s.chrome.send('Page.captureScreenshot', params); + /** The size of a base64 image and the color at its center, decoded by the page. */ + const look = (data: string) => + s.page.evaluate(async d => { + const img = new Image(); + img.src = 'data:image;base64,' + d; + await img.decode(); + const canvas = Object.assign(document.createElement('canvas'), { width: img.width, height: img.height }); + const g = canvas.getContext('2d'); + if (!g) throw new Error('no 2d context'); + g.drawImage(img, 0, 0); + const [r, gr, b] = g.getImageData(img.width >> 1, img.height >> 1, 1, 1).data; + return { width: img.width, height: img.height, center: [r, gr, b] }; + }, data); + return { s, ours, chrome, look }; +} + +test('captureScreenshot matches Chrome in size, at the viewport, the whole page and a clip', async () => { + const { s, ours, chrome, look } = await screenshotPage(); + try { + const clip = { x: 10, y: 520, width: 100, height: 50, scale: 1 }; + for (const params of [ + {}, + { captureBeyondViewport: true }, + { clip }, + { clip, captureBeyondViewport: true }, + { clip: { ...clip, scale: 2 } }, + ]) { + const [a, b] = await Promise.all([ours(params), chrome(params)]); + const [mine, theirs] = await Promise.all([look(a.data), look(b.data)]); + assert.equal(mine.width, theirs.width, JSON.stringify(params)); + assert.equal(mine.height, theirs.height, JSON.stringify(params)); + // A clip is in page coordinates, with or without captureBeyondViewport: the red box. + if ('clip' in params) assert.deepEqual(mine.center, theirs.center, JSON.stringify(params)); + } + assert.deepEqual((await look((await ours({ clip })).data)).center, [255, 0, 0]); + } finally { + await s.page.close(); + } +}); + +test('captureScreenshot has device pixels, encodes as asked and says it is approximate', async () => { + const { s, ours, chrome, look } = await screenshotPage(); + try { + await s.chrome.send('Emulation.setDeviceMetricsOverride', { + width: 400, + height: 300, + deviceScaleFactor: 2, + mobile: false, + }); + const [a, b] = await Promise.all([ours({}), chrome({})]); + assert.deepEqual( + [(await look(a.data)).width, (await look(a.data)).height], + [(await look(b.data)).width, (await look(b.data)).height] + ); + assert.equal(a.approximate, true); + assert.ok(a.data.startsWith('iVBOR'), 'PNG by default'); + assert.ok((await ours({ format: 'jpeg', quality: 50 })).data.startsWith('/9j/'), 'JPEG'); + assert.ok((await ours({ format: 'webp' })).data.startsWith('UklGR'), 'WebP'); + } finally { + await s.page.close(); + } +}); + +test('captureScreenshot scales a page too large for a canvas down', async () => { + const { s, ours, look } = await screenshotPage(); + try { + await s.page.evaluate(() => (document.body.style.height = '20000px')); + const { width, height } = await look((await ours({ captureBeyondViewport: true })).data); + assert.ok(width * height <= 4096 * 4096, `${width} × ${height}`); + assert.ok(Math.abs(width / height - 2000 / 20000) < 0.01, `${width} × ${height}`); + } finally { + await s.page.close(); + } +}); + +test('getLayoutMetrics matches Chrome on a scrolled page', async () => { + const { s } = await screenshotPage(); + try { + const [ours, chrome] = await Promise.all([ + s.ours.send('Page.getLayoutMetrics'), + s.chrome.send('Page.getLayoutMetrics'), + ]); + assert.deepEqual(ours, chrome); + } finally { + await s.page.close(); + } +}); diff --git a/packages/devtools-anywhere/src/cdp-agent/domains/page/page.ts b/packages/devtools-anywhere/src/cdp-agent/domains/page/page.ts index 91785f6..27a8a6a 100644 --- a/packages/devtools-anywhere/src/cdp-agent/domains/page/page.ts +++ b/packages/devtools-anywhere/src/cdp-agent/domains/page/page.ts @@ -63,6 +63,32 @@ export function registerPage({ session, probe }: Context) { return { url: link.href, errors: [], data: await res.text(), manifest: {} }; }, + /** Chrome reports CSS pixels in both sets of fields while the page is not zoomed. */ + getLayoutMetrics(): Protocol.Page.GetLayoutMetricsResponse { + const root = document.documentElement; + const layout = { pageX: scrollX, pageY: scrollY, clientWidth: root.clientWidth, clientHeight: root.clientHeight }; + const vv = visualViewport; + const visual = { + offsetX: vv?.offsetLeft ?? 0, + offsetY: vv?.offsetTop ?? 0, + pageX: vv?.pageLeft ?? scrollX, + pageY: vv?.pageTop ?? scrollY, + clientWidth: vv?.width ?? root.clientWidth, + clientHeight: vv?.height ?? root.clientHeight, + scale: vv?.scale ?? 1, + zoom: 1, + }; + const content = { x: 0, y: 0, width: root.scrollWidth, height: root.scrollHeight }; + return { + layoutViewport: layout, + visualViewport: visual, + contentSize: content, + cssLayoutViewport: layout, + cssVisualViewport: visual, + cssContentSize: content, + }; + }, + getNavigationHistory: (): Protocol.Page.GetNavigationHistoryResponse => ({ currentIndex: 0, entries: [ diff --git a/packages/devtools-anywhere/src/cdp-agent/domains/page/screenshot.ts b/packages/devtools-anywhere/src/cdp-agent/domains/page/screenshot.ts new file mode 100644 index 0000000..18f03c0 --- /dev/null +++ b/packages/devtools-anywhere/src/cdp-agent/domains/page/screenshot.ts @@ -0,0 +1,44 @@ +import type { Protocol } from 'devtools-protocol'; +import type { Context } from '../../core/context'; +import { CdpError } from '../../core/session'; +import { encode, type Render } from './render'; + +/** + * The most pixels a screenshot has: Safari's limit on a canvas's area (4096 × 4096). A larger + * screenshot, of a long page in full, is scaled down to it. + */ +const MAX_PIXELS = 4096 * 4096; + +/** + * Page.captureScreenshot's response, with `approximate`: the image is the page's DOM redrawn + * by snapDOM, not the pixels Chrome painted (README, "Limitations"). + */ +export type CaptureScreenshotResponse = Protocol.Page.CaptureScreenshotResponse & { approximate: true }; + +/** + * Page.captureScreenshot, as Chrome does it: `clip` is in page coordinates whether or not + * `captureBeyondViewport` is set, and the image has device pixels, the CSS size times + * devicePixelRatio times `clip.scale`. Without a clip it is the viewport, or with + * `captureBeyondViewport` the whole page. + */ +export function registerScreenshot({ session }: Context, render: Render | undefined) { + session.register('Page', { + async captureScreenshot(params: Protocol.Page.CaptureScreenshotRequest): Promise { + if (!render) throw new CdpError('Screenshots are not available: snapDOM is not configured'); + const root = document.documentElement; + const { clip } = params; + const area = clip + ? { x: clip.x, y: clip.y, width: clip.width, height: clip.height } + : params.captureBeyondViewport + ? { x: 0, y: 0, width: root.scrollWidth, height: root.scrollHeight } + : null; + const width = area?.width ?? innerWidth; + const height = area?.height ?? innerHeight; + if (!(width > 0 && height > 0)) throw new CdpError('Cannot take screenshot with 0 width or height'); + const dpr = devicePixelRatio; + const scale = Math.min(clip?.scale ?? 1, Math.sqrt(MAX_PIXELS / (width * height)) / dpr); + const canvas = await render(area ?? 'viewport', scale, dpr); + return { data: encode(canvas, params.format ?? 'png', params.quality), approximate: true }; + }, + }); +} diff --git a/packages/devtools-anywhere/src/cdp-agent/index.ts b/packages/devtools-anywhere/src/cdp-agent/index.ts index 7534882..7ff2491 100644 --- a/packages/devtools-anywhere/src/cdp-agent/index.ts +++ b/packages/devtools-anywhere/src/cdp-agent/index.ts @@ -14,6 +14,7 @@ import { registerPage } from './domains/page/page'; import { registerRuntime } from './domains/runtime/runtime'; import { createRender, type SnapdomLoader } from './domains/page/render'; import { registerScreencast } from './domains/page/screencast'; +import { registerScreenshot } from './domains/page/screenshot'; import { registerStorage } from './domains/storage/storage'; import { registerStubs } from './domains/stubs'; import { registerWebMcp } from './domains/webmcp/webmcp'; @@ -41,7 +42,8 @@ export type { SnapdomLoader } from './domains/page/render'; export interface CdpSessionOptions { /** * Loads snapDOM (`() => import('@zumer/snapdom')`), which renders the frames of - * Page.startScreencast. Without it, screencasting is not available. + * Page.startScreencast and the images of Page.captureScreenshot. Without it, neither is + * available. */ loadSnapdom?: SnapdomLoader; } @@ -89,7 +91,9 @@ export function createCdpSession(options: CdpSessionOptions = {}): CdpSession { registerStorage(ctx); registerDomDebugger(ctx); registerDebugger(ctx); - registerScreencast(ctx, options.loadSnapdom && createRender(options.loadSnapdom)); + const render = options.loadSnapdom && createRender(options.loadSnapdom); + registerScreencast(ctx, render); + registerScreenshot(ctx, render); registerInput(ctx); registerWebMcp(ctx); return ctx.session; From fcce1c3a0aaab4b50b17a26d293174e583b9d174 Mon Sep 17 00:00:00 2001 From: jerrychan7 Date: Sun, 1 Nov 2026 15:46:23 +0800 Subject: [PATCH 03/26] docs: describe screenshots and their limits README lists screenshots under the limitations next to the screencast: rendered by snapDOM, marked approximate, Chrome's parameters otherwise, scaled down past Safari's canvas limit, PNG where WebP cannot be encoded. The changelog notes the two new methods under Unreleased. --- CHANGELOG.md | 2 ++ CHANGELOG.zh-CN.md | 2 ++ README.md | 5 +++-- README.zh-CN.md | 5 +++-- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e974f1f..d65c2ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All packages share one version and are released together (see DEV.md, "Releasing ## Unreleased +- `Page.captureScreenshot` and `Page.getLayoutMetrics`: screenshots of the viewport, a region or the whole page, rendered by snapDOM and marked `approximate: true`, for tools and agents that drive the page over CDP. + ## 0.1.0 Based on Chrome 156 (156.0.8078.80). diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md index b2421cf..077c319 100644 --- a/CHANGELOG.zh-CN.md +++ b/CHANGELOG.zh-CN.md @@ -4,6 +4,8 @@ ## Unreleased +- 新增 `Page.captureScreenshot` 和 `Page.getLayoutMetrics`:可以截取视口、指定区域或整个页面,由 snapDOM 绘制,并标注 `approximate: true`,供通过 CDP 操作页面的工具和 agent 使用。 + ## 0.1.0 基于 Chrome 156(156.0.8078.80)。 diff --git a/README.md b/README.md index 768529c..0e0614a 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,8 @@ This project depends on neither Chii nor chobitsu. Their source is read as a ref - User-agent styles are not shown: browsers do not expose their default style sheet, and a bundled copy could differ from the browser's. - Event listeners are recorded by the probe from the moment it runs (it must be the first script); their source locations are not available. - WebSocket connections and their frames are shown, but not the handshake headers, which the page cannot see. - - The screencast (the toolbar's device button, off by default) shows the viewport as [snapDOM](https://github.com/zumerlab/snapdom) redraws it from the DOM, not the pixels on screen: canvas, video and cross-origin images may be missing, and it updates about twice a second at most. snapDOM (about 90 KB gzip) is loaded only when the screencast starts. + - The screencast (the toolbar's device button, off by default) shows the viewport as [snapDOM](https://github.com/zumerlab/snapdom) redraws it from the DOM, not the pixels on screen: canvas, video and cross-origin images may be missing, and it updates about twice a second at most. snapDOM (about 90 KB gzip) is loaded only when the screencast starts or a screenshot is taken. + - Screenshots (`Page.captureScreenshot`, for tools and agents that drive the page over CDP) are rendered by snapDOM the same way, so they miss what the screencast misses. The response says so with a field Chrome does not send, `approximate: true`. Otherwise the parameters behave as in Chrome: `clip` is in page coordinates, the image has device pixels (`devicePixelRatio` times `clip.scale`), and `captureBeyondViewport` without a clip takes the whole page. An image larger than 4096 × 4096 pixels (Safari's canvas limit) is scaled down to fit. Browsers that cannot encode WebP (Safari) return PNG when WebP is asked for. - Input from the screencast is replayed as DOM events, which are not trusted (`isTrusted` is false): focus, clicks, scrolling and text editing are carried out, but pages that check `isTrusted` ignore them. - Cross-origin style sheets are read through a fetched copy, which needs CORS headers; their rules are read-only. - Forced pseudo-classes (`:hover`, `:focus`, … from the Styles pane) are emulated with companion style sheets that keep the cascade order; sheets from `@import` and `adoptedStyleSheets` are not covered. @@ -106,7 +107,7 @@ An umbrella package plus scoped packages under the [`@devtools-anywhere`](https: | Package | Contents | |---|---| -| `devtools-anywhere` | Umbrella package: `probe.iife.js` (probe plus auto-mounting), `devtools-anywhere.iife.js` (the `` element, which the probe loads), `devtools-anywhere-service-worker.js`, `snapdom.js` (the screencast's renderer, loaded on demand), and TypeScript types for the element, its events and the script and toggler attributes. There is no module to import: the scripts are the runtime | +| `devtools-anywhere` | Umbrella package: `probe.iife.js` (probe plus auto-mounting), `devtools-anywhere.iife.js` (the `` element, which the probe loads), `devtools-anywhere-service-worker.js`, `snapdom.js` (the renderer of the screencast and of screenshots, loaded on demand), and TypeScript types for the element, its events and the script and toggler attributes. There is no module to import: the scripts are the runtime | | `@devtools-anywhere/devtools-frontend` | Prebuilt frontend static assets with English and Chinese UI, self-hostable or served from a CDN | | `@devtools-anywhere/devtools-frontend-locales` | Optional: the other 80 UI languages, at the same paths as in the frontend package. Serve both from one directory (the Vite plugin does this when it is installed); a missing language falls back to English | | `@devtools-anywhere/vite-plugin` | Vite plugin | diff --git a/README.zh-CN.md b/README.zh-CN.md index 3ecda1e..121a566 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -40,7 +40,8 @@ - 不显示浏览器默认样式(user-agent styles):浏览器不开放它的默认样式表,内置一份副本又可能和浏览器实际使用的不一致。 - 事件监听器由探针从它执行的那一刻起记录(探针必须是第一个脚本);拿不到处理函数的源码位置。 - 能显示 WebSocket 连接和帧,但没有握手阶段的请求头和响应头,页面拿不到它们。 - - 投屏(工具栏上的设备按钮,默认关闭)显示的是 [snapDOM](https://github.com/zumerlab/snapdom) 根据 DOM 重新绘制的视口,不是屏幕上的真实像素:canvas、视频和跨域图片可能缺失,每秒最多更新约两次。snapDOM(gzip 后约 90 KB)只在开始投屏时加载。 + - 投屏(工具栏上的设备按钮,默认关闭)显示的是 [snapDOM](https://github.com/zumerlab/snapdom) 根据 DOM 重新绘制的视口,不是屏幕上的真实像素:canvas、视频和跨域图片可能缺失,每秒最多更新约两次。snapDOM(gzip 后约 90 KB)只在开始投屏或截图时加载。 + - 截图(`Page.captureScreenshot`,供通过 CDP 操作页面的工具和 agent 使用)同样由 snapDOM 绘制,投屏缺失的内容截图里也会缺失。返回结果里会多一个 Chrome 没有的字段 `approximate: true` 来说明这一点。其余参数的行为和 Chrome 一致:`clip` 按页面坐标计算,图片按设备像素输出(`devicePixelRatio` 乘以 `clip.scale`),不带 `clip` 时设置 `captureBeyondViewport` 会截取整个页面。超过 4096 × 4096 像素(Safari 画布的上限)的图片会等比缩小。不支持编码 WebP 的浏览器(Safari)在请求 WebP 时返回 PNG。 - 投屏上的输入以 DOM 事件重放,这些事件不是可信事件(`isTrusted` 为 false):聚焦、点击、滚动和文本编辑都会执行,但检查 `isTrusted` 的页面会忽略它们。 - 跨域样式表通过重新下载的副本读取,需要对方返回 CORS 头;这些规则是只读的。 - 强制伪类(样式面板里的 `:hover`、`:focus` 等)通过伴随样式表模拟,层叠顺序保持不变;`@import` 引入的样式表和 `adoptedStyleSheets` 不在覆盖范围内。 @@ -106,7 +107,7 @@ | 包 | 内容 | |---|---| -| `devtools-anywhere` | 总包:`probe.iife.js`(探针加自动挂载)、`devtools-anywhere.iife.js`(`` 组件,由探针加载)、`devtools-anywhere-service-worker.js`、`snapdom.js`(投屏的绘制器,按需加载),以及组件、事件、探针脚本和切换器属性的 TypeScript 类型。没有可以 import 的模块,运行时只有这些脚本 | +| `devtools-anywhere` | 总包:`probe.iife.js`(探针加自动挂载)、`devtools-anywhere.iife.js`(`` 组件,由探针加载)、`devtools-anywhere-service-worker.js`、`snapdom.js`(投屏和截图的绘制器,按需加载),以及组件、事件、探针脚本和切换器属性的 TypeScript 类型。没有可以 import 的模块,运行时只有这些脚本 | | `@devtools-anywhere/devtools-frontend` | 构建好的前端静态资源,界面带英文和中文,可以自托管,也可以从 CDN 引用 | | `@devtools-anywhere/devtools-frontend-locales` | 可选:其余 80 种界面语言,文件路径与前端包一致。两个包放在同一目录下提供即可(装了它的话 Vite 插件会自动这样做);缺少的语言会退回英文 | | `@devtools-anywhere/vite-plugin` | Vite 插件 | From 53334642305f393bbd3ecf4fcd0715b31caaa2cd Mon Sep 17 00:00:00 2001 From: jerrychan7 Date: Sun, 1 Nov 2026 16:22:27 +0800 Subject: [PATCH 04/26] fix(cdp-agent): scale screenshots down only when the canvas cannot hold them A screenshot was capped at 4096 x 4096 pixels, the smallest canvas limit of any browser, so a long page came out scaled down in Chrome too. Pass the size asked for to snapDOM instead: it already scales an image past the browser's canvas limits down to fit (32767 pixels a side, 16384 on Safari, 16384 x 16384 in area). Should drawing still fail, by throwing or by encoding an empty data URL, the screenshot is halved and tried again; once it fits in 4096 x 4096 the failure is reported. The conformance tests check that a 2000 x 20000 page is taken at full size and that a 2000 x 200000 page is scaled down to the longest side Chrome allows. README states the limits. --- README.md | 2 +- README.zh-CN.md | 2 +- .../domains/page/page.conformance.test.ts | 18 ++++++++++++--- .../src/cdp-agent/domains/page/screenshot.ts | 22 ++++++++++++++----- 4 files changed, 33 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 0e0614a..1e8e729 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ This project depends on neither Chii nor chobitsu. Their source is read as a ref - Event listeners are recorded by the probe from the moment it runs (it must be the first script); their source locations are not available. - WebSocket connections and their frames are shown, but not the handshake headers, which the page cannot see. - The screencast (the toolbar's device button, off by default) shows the viewport as [snapDOM](https://github.com/zumerlab/snapdom) redraws it from the DOM, not the pixels on screen: canvas, video and cross-origin images may be missing, and it updates about twice a second at most. snapDOM (about 90 KB gzip) is loaded only when the screencast starts or a screenshot is taken. - - Screenshots (`Page.captureScreenshot`, for tools and agents that drive the page over CDP) are rendered by snapDOM the same way, so they miss what the screencast misses. The response says so with a field Chrome does not send, `approximate: true`. Otherwise the parameters behave as in Chrome: `clip` is in page coordinates, the image has device pixels (`devicePixelRatio` times `clip.scale`), and `captureBeyondViewport` without a clip takes the whole page. An image larger than 4096 × 4096 pixels (Safari's canvas limit) is scaled down to fit. Browsers that cannot encode WebP (Safari) return PNG when WebP is asked for. + - Screenshots (`Page.captureScreenshot`, for tools and agents that drive the page over CDP) are rendered by snapDOM the same way, so they miss what the screencast misses. The response says so with a field Chrome does not send, `approximate: true`. Otherwise the parameters behave as in Chrome: `clip` is in page coordinates, the image has device pixels (`devicePixelRatio` times `clip.scale`), and `captureBeyondViewport` without a clip takes the whole page. An image past the browser's canvas limits (32767 pixels a side, 16384 on Safari, 16384 × 16384 in area) is scaled down to fit, and snapDOM warns about it in the page's console. Browsers that cannot encode WebP (Safari) return PNG when WebP is asked for. - Input from the screencast is replayed as DOM events, which are not trusted (`isTrusted` is false): focus, clicks, scrolling and text editing are carried out, but pages that check `isTrusted` ignore them. - Cross-origin style sheets are read through a fetched copy, which needs CORS headers; their rules are read-only. - Forced pseudo-classes (`:hover`, `:focus`, … from the Styles pane) are emulated with companion style sheets that keep the cascade order; sheets from `@import` and `adoptedStyleSheets` are not covered. diff --git a/README.zh-CN.md b/README.zh-CN.md index 121a566..ce5c246 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -41,7 +41,7 @@ - 事件监听器由探针从它执行的那一刻起记录(探针必须是第一个脚本);拿不到处理函数的源码位置。 - 能显示 WebSocket 连接和帧,但没有握手阶段的请求头和响应头,页面拿不到它们。 - 投屏(工具栏上的设备按钮,默认关闭)显示的是 [snapDOM](https://github.com/zumerlab/snapdom) 根据 DOM 重新绘制的视口,不是屏幕上的真实像素:canvas、视频和跨域图片可能缺失,每秒最多更新约两次。snapDOM(gzip 后约 90 KB)只在开始投屏或截图时加载。 - - 截图(`Page.captureScreenshot`,供通过 CDP 操作页面的工具和 agent 使用)同样由 snapDOM 绘制,投屏缺失的内容截图里也会缺失。返回结果里会多一个 Chrome 没有的字段 `approximate: true` 来说明这一点。其余参数的行为和 Chrome 一致:`clip` 按页面坐标计算,图片按设备像素输出(`devicePixelRatio` 乘以 `clip.scale`),不带 `clip` 时设置 `captureBeyondViewport` 会截取整个页面。超过 4096 × 4096 像素(Safari 画布的上限)的图片会等比缩小。不支持编码 WebP 的浏览器(Safari)在请求 WebP 时返回 PNG。 + - 截图(`Page.captureScreenshot`,供通过 CDP 操作页面的工具和 agent 使用)同样由 snapDOM 绘制,投屏缺失的内容截图里也会缺失。返回结果里会多一个 Chrome 没有的字段 `approximate: true` 来说明这一点。其余参数的行为和 Chrome 一致:`clip` 按页面坐标计算,图片按设备像素输出(`devicePixelRatio` 乘以 `clip.scale`),不带 `clip` 时设置 `captureBeyondViewport` 会截取整个页面。超出浏览器画布上限(单边 32767 像素,Safari 上 16384;面积 16384 × 16384)的图片会等比缩小,snapDOM 会在页面的控制台里给出警告。不支持编码 WebP 的浏览器(Safari)在请求 WebP 时返回 PNG。 - 投屏上的输入以 DOM 事件重放,这些事件不是可信事件(`isTrusted` 为 false):聚焦、点击、滚动和文本编辑都会执行,但检查 `isTrusted` 的页面会忽略它们。 - 跨域样式表通过重新下载的副本读取,需要对方返回 CORS 头;这些规则是只读的。 - 强制伪类(样式面板里的 `:hover`、`:focus` 等)通过伴随样式表模拟,层叠顺序保持不变;`@import` 引入的样式表和 `adoptedStyleSheets` 不在覆盖范围内。 diff --git a/packages/devtools-anywhere/src/cdp-agent/domains/page/page.conformance.test.ts b/packages/devtools-anywhere/src/cdp-agent/domains/page/page.conformance.test.ts index 25c05e7..2977cd7 100644 --- a/packages/devtools-anywhere/src/cdp-agent/domains/page/page.conformance.test.ts +++ b/packages/devtools-anywhere/src/cdp-agent/domains/page/page.conformance.test.ts @@ -251,13 +251,25 @@ test('captureScreenshot has device pixels, encodes as asked and says it is appro } }); -test('captureScreenshot scales a page too large for a canvas down', async () => { +test('captureScreenshot keeps a long page at full size while the canvas can hold it', async () => { const { s, ours, look } = await screenshotPage(); try { await s.page.evaluate(() => (document.body.style.height = '20000px')); const { width, height } = await look((await ours({ captureBeyondViewport: true })).data); - assert.ok(width * height <= 4096 * 4096, `${width} × ${height}`); - assert.ok(Math.abs(width / height - 2000 / 20000) < 0.01, `${width} × ${height}`); + assert.deepEqual([width, height], [2000, 20000]); + } finally { + await s.page.close(); + } +}); + +test('captureScreenshot scales a page too large for a canvas down to fit', async () => { + const { s, ours, look } = await screenshotPage(); + try { + // 200000 pixels high, past the longest side a canvas can have (32767 in Chrome). + await s.page.evaluate(() => (document.body.style.height = '200000px')); + const { width, height } = await look((await ours({ captureBeyondViewport: true })).data); + assert.ok(height <= 32767, `${width} × ${height}`); + assert.ok(Math.abs(width / height - 2000 / 200000) < 0.001, `${width} × ${height}`); } finally { await s.page.close(); } diff --git a/packages/devtools-anywhere/src/cdp-agent/domains/page/screenshot.ts b/packages/devtools-anywhere/src/cdp-agent/domains/page/screenshot.ts index 18f03c0..c62431e 100644 --- a/packages/devtools-anywhere/src/cdp-agent/domains/page/screenshot.ts +++ b/packages/devtools-anywhere/src/cdp-agent/domains/page/screenshot.ts @@ -4,10 +4,12 @@ import { CdpError } from '../../core/session'; import { encode, type Render } from './render'; /** - * The most pixels a screenshot has: Safari's limit on a canvas's area (4096 × 4096). A larger - * screenshot, of a long page in full, is scaled down to it. + * A canvas area small enough for any browser, older iOS Safari's limit (4096 × 4096). + * snapDOM already scales an image past the usual canvas limits down to fit (32767 pixels a + * side, 16384 on Safari, 16384 × 16384 in area); should drawing still fail, the screenshot is + * halved and tried again, and once it is no larger than this the failure is reported. */ -const MAX_PIXELS = 4096 * 4096; +const SAFE_PIXELS = 4096 * 4096; /** * Page.captureScreenshot's response, with `approximate`: the image is the page's DOM redrawn @@ -36,9 +38,17 @@ export function registerScreenshot({ session }: Context, render: Render | undefi const height = area?.height ?? innerHeight; if (!(width > 0 && height > 0)) throw new CdpError('Cannot take screenshot with 0 width or height'); const dpr = devicePixelRatio; - const scale = Math.min(clip?.scale ?? 1, Math.sqrt(MAX_PIXELS / (width * height)) / dpr); - const canvas = await render(area ?? 'viewport', scale, dpr); - return { data: encode(canvas, params.format ?? 'png', params.quality), approximate: true }; + for (let scale = clip?.scale ?? 1; ; scale /= 2) { + const safe = width * height * (scale * dpr) ** 2 <= SAFE_PIXELS; + try { + // A canvas too large to draw encodes as an empty data URL (Chrome) or throws. + const data = encode(await render(area ?? 'viewport', scale, dpr), params.format ?? 'png', params.quality); + if (data) return { data, approximate: true }; + if (safe) throw new CdpError('Unable to capture screenshot'); + } catch (e) { + if (safe) throw e; + } + } }, }); } From 3d57936d07c85a0ad12786ae53e4c9aef959b4a8 Mon Sep 17 00:00:00 2001 From: jerrychan7 Date: Sun, 1 Nov 2026 19:07:03 +0800 Subject: [PATCH 05/26] feat(cdp-agent): answer the emulation commands device mode sends Device mode sends Emulation.setSafeAreaInsetsOverride, resetPageScaleFactor and setUserAgentOverride, Network.setUserAgentOverride, and Overlay.setShowHinge and setShowDisplayCutout. A page cannot do any of them, and an error from one stops the frontend's device mode from updating, so they are answered with empty results like the other commands without an in-page equivalent. --- .../src/cdp-agent/domains/stubs.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/devtools-anywhere/src/cdp-agent/domains/stubs.ts b/packages/devtools-anywhere/src/cdp-agent/domains/stubs.ts index df5fa79..2e0af6b 100644 --- a/packages/devtools-anywhere/src/cdp-agent/domains/stubs.ts +++ b/packages/devtools-anywhere/src/cdp-agent/domains/stubs.ts @@ -19,7 +19,17 @@ const NOOPS = { Autofill: ['enable', 'setAddresses'], BackgroundService: ['startObserving'], CSS: ['trackComputedStyleUpdatesForNode'], - Emulation: ['setEmulatedMedia', 'setAutoDarkModeOverride', 'setEmulatedVisionDeficiency', 'setFocusEmulationEnabled'], + // Device mode's size is the shell's (shell-layout.ts); the rest a page cannot change: the user + // agent, safe areas, a hinge or cutout, the pinch zoom. + Emulation: [ + 'setEmulatedMedia', + 'setAutoDarkModeOverride', + 'setEmulatedVisionDeficiency', + 'setFocusEmulationEnabled', + 'setSafeAreaInsetsOverride', + 'resetPageScaleFactor', + 'setUserAgentOverride', + ], HeapProfiler: ['enable'], Inspector: ['enable'], Log: ['enable', 'clear', 'startViolationsReport'], @@ -31,6 +41,8 @@ const NOOPS = { 'setShowScrollSnapOverlays', 'setShowContainerQueryOverlays', 'setShowIsolatedElements', + 'setShowHinge', + 'setShowDisplayCutout', ], Network: [ 'overrideNetworkState', @@ -41,6 +53,7 @@ const NOOPS = { 'setBypassServiceWorker', 'setExtraHTTPHeaders', 'setBlockedURLs', + 'setUserAgentOverride', ], Preload: ['enable'], Profiler: ['enable'], From 0d9fd2ad03efc41198dc692c379d2a9d99b1e94a Mon Sep 17 00:00:00 2001 From: jerrychan7 Date: Sun, 1 Nov 2026 19:07:04 +0800 Subject: [PATCH 06/26] feat(shell): device mode in shell mode, with DevTools laying out the page as in Chrome In Chrome the device toolbar is part of DevTools' window: its app (AdvancedApp) lays out the page and DevTools, and tells the browser where the page goes (setInspectedPageBounds). In shell mode the frontend now does the same. The shell passes #?shell=1, the entrypoint sets can_emulate_device and loads the emulation module, and the frontend fills the shell. Its setInspectedPageBounds goes to the shell (page-bounds), which places the page iframe there on top, and the shell answers Emulation.setDeviceMetricsOverride and clearDeviceMetricsOverride by sizing and scaling the iframe (shell-layout.ts). The frontend fills the shell from the start: device mode does not recover from laying out in a small frame first. Hidden, the page fills the shell at its own size; shown again, device mode is back. On touch screens a 28px handle straddles the edge between the page and DevTools (the frontend reports its split size), and dragging it sets DevTools' size (sidebar-size notice). Undocked, Chrome draws device mode into device_mode_emulation_frame.html in the tab. The frontend asks the shell to show that page (toolbox) instead of opening a window; the shell shows it in an iframe under the floating panel, and the frontend finds it among its parent's frames and hands its document to AdvancedApp. The build keeps the page, bundled as one file. patches/device-mode-in-shell.patch adds the can_emulate_device condition to device mode, keeps the screencast out of shell mode, opens device mode's page next to anywhere_app.html, and drops the device pixel ratio and device type from the toolbar's menu, since a page cannot change either. Outside shell mode nothing changes: the simple app and the screencast stay. device-mode.test.ts runs the built frontend in shell mode: the page goes where the frontend's placeholder is, a device preset sizes and scales it, the screenshot command saves it at the device's size, hiding and showing, undocking into device mode's page, and the touch handle. --- packages/devtools-anywhere/src/connection.ts | 46 +++- packages/devtools-anywhere/src/element.ts | 28 +- packages/devtools-anywhere/src/embedder.ts | 33 ++- .../devtools-anywhere/src/shell-layout.ts | 188 +++++++++++++ .../patches/device-mode-in-shell.patch | 134 +++++++++ packages/devtools-frontend/scripts/build.ts | 8 +- packages/devtools-frontend/scripts/bundle.ts | 21 +- .../src/anywhere_app/BUILD.gn | 1 + .../src/anywhere_app/anywhere_app.ts | 102 ++++++- tests/e2e/device-mode.test.ts | 257 ++++++++++++++++++ tests/e2e/pages/device-mode.html | 20 ++ tests/e2e/real-frontend.test.ts | 8 + 12 files changed, 813 insertions(+), 33 deletions(-) create mode 100644 packages/devtools-anywhere/src/shell-layout.ts create mode 100644 packages/devtools-frontend/patches/device-mode-in-shell.patch create mode 100644 tests/e2e/device-mode.test.ts create mode 100644 tests/e2e/pages/device-mode.html diff --git a/packages/devtools-anywhere/src/connection.ts b/packages/devtools-anywhere/src/connection.ts index 65b4226..42e72c0 100644 --- a/packages/devtools-anywhere/src/connection.ts +++ b/packages/devtools-anywhere/src/connection.ts @@ -124,6 +124,13 @@ export function connectToShell(session: CdpSession): () => void { return () => connection.disconnect(); } +/** The page's size in device mode, in CSS pixels (0 for "as laid out"), and its scale. */ +export interface DeviceMetrics { + width: number; + height: number; + scale: number; +} + /** The shell's side of the relay core: one frontend, in an iframe; the page in another. */ class ShellRelay extends RelayCore { private frontendReady = false; @@ -135,7 +142,8 @@ class ShellRelay extends RelayCore { constructor( private readonly page: HTMLIFrameElement, private readonly devtools: HTMLIFrameElement, - private readonly frontendOrigin: string + private readonly frontendOrigin: string, + private readonly onMetrics: (metrics: DeviceMetrics | null) => void ) { super(); this.session = this.openSession({ send: message => this.postFrontend(message) }); @@ -168,6 +176,31 @@ class ShellRelay extends RelayCore { private onPagehide = () => this.pagehide(); + /** + * Device mode's page size is the shell's to set (shell-layout.ts): it answers + * Emulation.setDeviceMetricsOverride and clearDeviceMetricsOverride itself, as Chrome's browser + * does, rather than the page. + */ + private emulate(data: string): boolean { + if (!data.includes('DeviceMetricsOverride')) return false; + const message = parse(data); + const method = message?.method; + if (method !== 'Emulation.setDeviceMetricsOverride' && method !== 'Emulation.clearDeviceMetricsOverride') + return false; + const params: unknown = message?.params; + const number = (name: string) => { + const value: unknown = typeof params === 'object' && params !== null ? Reflect.get(params, name) : undefined; + return typeof value === 'number' && Number.isFinite(value) ? value : 0; + }; + this.onMetrics( + method === 'Emulation.clearDeviceMetricsOverride' + ? null + : { width: number('width'), height: number('height'), scale: number('scale') || 1 } + ); + this.postFrontend(JSON.stringify({ id: message?.id, result: {} })); + return true; + } + readonly onMessage = (event: MessageEvent) => { if (typeof event.data !== 'string') return; if (event.source === this.devtools.contentWindow && event.origin === this.frontendOrigin) { @@ -175,7 +208,7 @@ class ShellRelay extends RelayCore { this.frontendReady = true; this.toFrontend.splice(0).forEach(message => this.postFrontend(message)); } - this.clientMessage(this.session, event.data); + if (!this.emulate(event.data)) this.clientMessage(this.session, event.data); } else if (event.source === this.page.contentWindow && event.origin === location.origin) { if (event.data.startsWith(`{"method":"${HELLO_METHOD}"`)) { const params = parse(event.data)?.params; @@ -198,8 +231,13 @@ class ShellRelay extends RelayCore { * reloads (the relay core's work, shared with the remote server). Returns a function that * disconnects. */ -export function relayShell(page: HTMLIFrameElement, devtools: HTMLIFrameElement, frontendOrigin: string): () => void { - const relay = new ShellRelay(page, devtools, frontendOrigin); +export function relayShell( + page: HTMLIFrameElement, + devtools: HTMLIFrameElement, + frontendOrigin: string, + onMetrics: (metrics: DeviceMetrics | null) => void +): () => void { + const relay = new ShellRelay(page, devtools, frontendOrigin, onMetrics); window.addEventListener('message', relay.onMessage); page.contentWindow?.postMessage(PING, location.origin); return () => window.removeEventListener('message', relay.onMessage); diff --git a/packages/devtools-anywhere/src/element.ts b/packages/devtools-anywhere/src/element.ts index 63bccac..8df5489 100644 --- a/packages/devtools-anywhere/src/element.ts +++ b/packages/devtools-anywhere/src/element.ts @@ -17,6 +17,7 @@ import { } from './cdp-agent'; import { connectFrontend, relayShell } from './connection'; import { SHELL_HANDSHAKE } from './shell-messages'; +import { ShellLayout } from './shell-layout'; import type { GlobalElementConfig } from './api'; import type { DevtoolsAnywhereAttributes, TogglerAttributes } from './public-types'; import { @@ -93,6 +94,9 @@ const REQUEST_CHECKS: { [T in EmbedderRequest['type']]: (request: object) => boo close: () => true, 'keep-open': request => 'keep' in request && typeof request.keep === 'boolean', theme: request => ['toolbar', 'divider', 'icon'].every(key => typeof Reflect.get(request, key) === 'string'), + 'page-bounds': request => ['x', 'y', 'width', 'height'].every(key => finite(Reflect.get(request, key)) !== undefined), + split: request => 'size' in request && finite(request.size) !== undefined, + toolbox: request => 'url' in request && typeof request.url === 'string', }; const isRequestType = (type: unknown): type is EmbedderRequest['type'] => typeof type === 'string' && Object.hasOwn(REQUEST_CHECKS, type); @@ -216,6 +220,12 @@ iframe { flex: 1; min-width: 0; min-height: 0; border: 0; } :host([data-shell]) { display: flex; flex: none; } :host([data-shell]) .panel { position: static; flex: 1 1 auto; } :host([data-shell]) .panel[data-dock=undocked] { position: fixed; } +/* Laid out by the frontend (shell-layout.ts): it fills the shell, the page iframe on top. */ +:host([data-laid-out]) { position: fixed; inset: 0; z-index: 0; } +:host([data-laid-out]) .panel { + position: absolute; inset: 0; width: auto !important; height: auto !important; border: 0; +} +:host([data-laid-out]) .bar { display: none; } `; /** @@ -270,6 +280,8 @@ export class DevtoolsAnywhereElement extends HTMLElement { * (detach), as the page shows or hides it, or removes it (document.open, replaceChildren). */ private shellLink: { attach(): void; detach(): void } | null = null; + /** In the shell: places the page where the frontend lays it out (shell-layout.ts). */ + private shellLayout: ShellLayout | null = null; constructor() { super(); @@ -352,6 +364,14 @@ export class DevtoolsAnywhereElement extends HTMLElement { } } else if (request.type === 'close') { this.setOpen(false); + } else if (request.type === 'page-bounds') { + this.shellLayout?.pageBounds(request); + } else if (request.type === 'split') { + this.shellLayout?.splitSize(request.size); + } else if (request.type === 'toolbox') { + // Only a page of the frontend's own origin. + const url = new URL(request.url, this.loadedUrl); + if (url.origin === this.frontendOrigin) this.shellLayout?.showToolbox(url.href); } }; @@ -630,6 +650,10 @@ export class DevtoolsAnywhereElement extends HTMLElement { if (isShellElement(this)) { // Only for its styles: what makes it the shell's is its document. this.setAttribute('data-shell', ''); + const page = document.getElementById(SHELL_PAGE_ID); + if (page instanceof HTMLIFrameElement && !this.shellLayout) { + this.shellLayout = new ShellLayout(this, this.frame, page, () => this.frontendOrigin); + } this.watchShellPage(); this.registerShellWorker(); } else { @@ -712,6 +736,7 @@ export class DevtoolsAnywhereElement extends HTMLElement { // The shell document is ours: the page iframe comes first, then this element. if (isShellElement(this)) { document.body.style.flexDirection = dock === 'right' ? 'row' : dock === 'left' ? 'row-reverse' : 'column'; + this.shellLayout?.update(this.isOpen, dock); } } @@ -762,7 +787,8 @@ export class DevtoolsAnywhereElement extends HTMLElement { this.disconnect?.(); this.frontendOrigin = url.origin; if (this.session) this.disconnect = connectFrontend(this.session, this.frame, url.origin); - else if (page) this.disconnect = relayShell(page, this.frame, url.origin); + else if (page) + this.disconnect = relayShell(page, this.frame, url.origin, metrics => this.shellLayout?.setMetrics(metrics)); // Whether shell mode is available (it needs the bundle URL) and whether it is on. const shell = isShellElement(this); const hash = new URLSearchParams({ diff --git a/packages/devtools-anywhere/src/embedder.ts b/packages/devtools-anywhere/src/embedder.ts index c0d8bd6..75eb319 100644 --- a/packages/devtools-anywhere/src/embedder.ts +++ b/packages/devtools-anywhere/src/embedder.ts @@ -13,13 +13,31 @@ export type DockSide = (typeof DOCK_SIDES)[number]; /** * Requests from the frontend to the embedder: its dock side menu, its close button, keeping - * DevTools open across reloads (shell mode), and the theme's colors for the panel's bar. + * DevTools open across reloads (shell mode), the theme's colors for the panel's bar, and in shell + * mode where the page goes (`page-bounds`, the frontend's InspectedPagePlaceholder, in CSS pixels + * of the frontend's viewport, like Chrome's setInspectedPageBounds) and how much room DevTools + * takes next to it (`split`, in CSS pixels, for the shell's touch handle on the edge between them). + * Undocked, device mode draws into a page of the frontend's (`toolbox`, its URL), which Chrome + * opens in the tab under DevTools' window and the shell shows in an iframe under the panel, where + * the frontend finds it among its parent's frames; the page bounds are then in CSS pixels of that + * iframe. */ export type EmbedderRequest = | { type: 'dock'; side: DockSide } | { type: 'close' } | { type: 'keep-open'; keep: boolean } - | { type: 'theme'; toolbar: string; divider: string; icon: string }; + | { type: 'theme'; toolbar: string; divider: string; icon: string } + | ({ type: 'page-bounds' } & PageBounds) + | { type: 'split'; size: number } + | { type: 'toolbox'; url: string }; + +/** A rectangle in CSS pixels. */ +export interface PageBounds { + x: number; + y: number; + width: number; + height: number; +} /** * Tells the frontend, posted to it under {@link REQUEST_KEY} (`{ type: HIDDEN_NOTICE }`), that the @@ -27,10 +45,13 @@ export type EmbedderRequest = * highlights, which would otherwise keep taking the page over. */ export const HIDDEN_NOTICE = 'hidden'; -/** What the embedder posts to the frontend: {@link HIDDEN_NOTICE}. */ -export interface FrontendNotice { - type: typeof HIDDEN_NOTICE; -} +/** + * Tells the frontend, in shell mode, to give DevTools `size` CSS pixels next to the page: the + * shell's touch handle on the edge between them was dragged. + */ +export const SIDEBAR_SIZE_NOTICE = 'sidebar-size'; +/** What the embedder posts to the frontend: {@link HIDDEN_NOTICE} or {@link SIDEBAR_SIZE_NOTICE}. */ +export type FrontendNotice = { type: typeof HIDDEN_NOTICE } | { type: typeof SIDEBAR_SIZE_NOTICE; size: number }; /** * The key a request is posted under (`{ [REQUEST_KEY]: request }`): requests are objects, so diff --git a/packages/devtools-anywhere/src/shell-layout.ts b/packages/devtools-anywhere/src/shell-layout.ts new file mode 100644 index 0000000..7da1f7d --- /dev/null +++ b/packages/devtools-anywhere/src/shell-layout.ts @@ -0,0 +1,188 @@ +// Shell mode's layout, as in Chrome: DevTools' frontend fills the shell and lays out the page and +// itself (upstream's AdvancedApp), telling where the page goes (`page-bounds`, its +// InspectedPagePlaceholder); the shell places the page iframe there, on top. In device mode the +// frontend asks for the page's size through Emulation.setDeviceMetricsOverride, which the shell +// answers by sizing and scaling the page iframe. Undocked, device mode draws into a page of the +// frontend's (`toolbox`), shown here in an iframe under the floating panel; the bounds are then in +// that iframe. Until the frontend says where the page goes, or while DevTools is hidden, the page +// fills the shell. On touch screens a handle on the edge between the page and DevTools resizes +// them, since the frontend's own resizer is a few pixels wide. + +import type { DeviceMetrics } from './connection'; +import { type DockSide, type FrontendNotice, type PageBounds, REQUEST_KEY, SIDEBAR_SIZE_NOTICE } from './embedder'; + +/** Width of the touch handle, as the panel's bar on touch screens. */ +const HANDLE = 28; + +/** Sets the given inline styles, and clears the others this layout sets. */ +function place( + el: HTMLElement, + styles: Partial> +) { + const style = el.style; + const fixed = Object.keys(styles).length > 0; + style.position = fixed ? 'fixed' : ''; + style.zIndex = fixed ? '1' : ''; + style.left = styles.left ?? ''; + style.top = styles.top ?? ''; + style.width = styles.width ?? ''; + style.height = styles.height ?? ''; + style.flex = fixed ? 'none' : ''; + style.transform = styles.transform ?? ''; + style.transformOrigin = styles.transform ? '0 0' : ''; + style.clipPath = styles.clipPath ?? ''; +} + +export class ShellLayout { + private bounds: PageBounds | null = null; + private split: number | null = null; + private metrics: DeviceMetrics | null = null; + private toolbox: HTMLIFrameElement | null = null; + private open = false; + private dock: DockSide = 'bottom'; + private readonly handle: HTMLDivElement; + + /** + * @param host the shell's ``, which fills the shell while the frontend lays out + * @param frontend the frontend's iframe + * @param page the page's iframe + * @param frontendOrigin where notices to the frontend go + */ + constructor( + private readonly host: HTMLElement, + private readonly frontend: HTMLIFrameElement, + private readonly page: HTMLIFrameElement, + private readonly frontendOrigin: () => string + ) { + this.handle = document.createElement('div'); + this.handle.style.cssText = 'position:fixed;z-index:2;display:none;touch-action:none'; + document.body.append(this.handle); + this.setupHandle(); + addEventListener('resize', () => this.render()); + } + + /** DevTools was shown or hidden, or docked elsewhere. */ + update(open: boolean, dock: DockSide) { + if (dock !== this.dock) { + // The bounds were in the other side's layout (or in the toolbox): wait for new ones. + this.bounds = null; + if (dock !== 'undocked') this.removeToolbox(); + } + this.open = open; + this.dock = dock; + this.render(); + } + + pageBounds(bounds: PageBounds) { + this.bounds = bounds; + this.render(); + } + + splitSize(size: number) { + this.split = size; + this.render(); + } + + /** Emulation.setDeviceMetricsOverride, or (null) clearDeviceMetricsOverride. */ + setMetrics(metrics: DeviceMetrics | null) { + this.metrics = metrics; + this.render(); + } + + /** + * Shows device mode's page for the undocked panel. The frontend asks for it as it undocks, + * before it says it did. + */ + showToolbox(url: string) { + this.removeToolbox(); + const frame = document.createElement('iframe'); + frame.title = 'Device mode'; + frame.style.cssText = 'position:fixed;inset:0;width:100%;height:100%;border:0;z-index:0'; + frame.src = url; + document.body.append(frame); + this.toolbox = frame; + this.render(); + } + + private removeToolbox() { + this.toolbox?.remove(); + this.toolbox = null; + } + + /** Where the frontend lays out the page: its own iframe, or undocked the toolbox's. */ + private get origin(): HTMLIFrameElement | null { + return this.dock === 'undocked' ? this.toolbox : this.frontend; + } + + private render() { + const origin = this.origin; + const laidOut = this.open && !!this.bounds && !!origin; + // Docked, the frontend fills the shell from the start, so that it lays out at its full size + // (device mode does not recover from starting small); undocked, it stays a floating panel. + this.host.toggleAttribute('data-laid-out', this.open && this.dock !== 'undocked'); + if (this.toolbox) this.toolbox.style.display = this.open ? '' : 'none'; + this.renderHandle(); + // Until then the page fills the shell, on top. + if (!laidOut || !this.bounds || !origin) + return place(this.page, this.open ? { left: '0', top: '0', width: '100%', height: '100%' } : {}); + const frame = origin.getBoundingClientRect(); + const { x, y, width, height } = this.bounds; + const scale = this.metrics?.scale || 1; + // The page's own size; the bounds are what shows of it once scaled. + const pageWidth = this.metrics?.width || width / scale; + const pageHeight = this.metrics?.height || height / scale; + // What does not fit in the bounds is clipped; a rounding error of the bounds is not. + const hidden = (size: number) => (size >= 1 ? size : 0); + const hiddenRight = hidden(pageWidth - width / scale); + const hiddenBottom = hidden(pageHeight - height / scale); + place(this.page, { + left: `${frame.left + x}px`, + top: `${frame.top + y}px`, + width: `${pageWidth}px`, + height: `${pageHeight}px`, + transform: scale === 1 ? '' : `scale(${scale})`, + clipPath: hiddenRight || hiddenBottom ? `inset(0 ${hiddenRight}px ${hiddenBottom}px 0)` : '', + }); + } + + /** The touch handle straddles the edge between the page and DevTools, docked only. */ + private renderHandle() { + const style = this.handle.style; + const shown = this.open && this.dock !== 'undocked' && this.split !== null && !!this.bounds; + style.display = shown && matchMedia('(pointer: coarse)').matches ? 'block' : 'none'; + if (!shown || this.split === null) return; + const frame = this.frontend.getBoundingClientRect(); + const vertical = this.dock === 'bottom'; + style.cursor = vertical ? 'row-resize' : 'col-resize'; + style.left = `${vertical ? frame.left : this.dock === 'right' ? frame.right - this.split - HANDLE / 2 : frame.left + this.split - HANDLE / 2}px`; + style.top = `${vertical ? frame.bottom - this.split - HANDLE / 2 : frame.top}px`; + style.width = `${vertical ? frame.width : HANDLE}px`; + style.height = `${vertical ? HANDLE : frame.height}px`; + } + + private setupHandle() { + this.handle.addEventListener('pointerdown', down => { + down.preventDefault(); + this.handle.setPointerCapture(down.pointerId); + const move = (e: PointerEvent) => { + const frame = this.frontend.getBoundingClientRect(); + const size = + this.dock === 'bottom' + ? frame.bottom - e.clientY + : this.dock === 'right' + ? frame.right - e.clientX + : e.clientX - frame.left; + const notice: FrontendNotice = { type: SIDEBAR_SIZE_NOTICE, size: Math.round(size) }; + this.frontend.contentWindow?.postMessage({ [REQUEST_KEY]: notice }, this.frontendOrigin()); + }; + const up = () => { + this.handle.removeEventListener('pointermove', move); + this.handle.removeEventListener('pointerup', up); + this.handle.removeEventListener('pointercancel', up); + }; + this.handle.addEventListener('pointermove', move); + this.handle.addEventListener('pointerup', up); + this.handle.addEventListener('pointercancel', up); + }); + } +} diff --git a/packages/devtools-frontend/patches/device-mode-in-shell.patch b/packages/devtools-frontend/patches/device-mode-in-shell.patch new file mode 100644 index 0000000..8cd9679 --- /dev/null +++ b/packages/devtools-frontend/patches/device-mode-in-shell.patch @@ -0,0 +1,134 @@ +diff --git a/front_end/core/root/Runtime.ts b/front_end/core/root/Runtime.ts +index 0c94af63c6..156d0b7818 100644 +--- a/front_end/core/root/Runtime.ts ++++ b/front_end/core/root/Runtime.ts +@@ -640,6 +640,9 @@ export type Condition = (config?: HostConfig) => boolean; + + export const conditions: { + canDock: Condition, ++ canEmulateDevice: Condition, + } = { + canDock: () => Boolean(Runtime.queryParam('can_dock')), ++ // Device mode needs an embedder that can resize the inspected page (devtools-anywhere's shell). ++ canEmulateDevice: () => Boolean(Runtime.queryParam('can_emulate_device')), + }; +diff --git a/front_end/panels/emulation/AdvancedApp.ts b/front_end/panels/emulation/AdvancedApp.ts +index 199ae25afc..e47adfc537 100644 +--- a/front_end/panels/emulation/AdvancedApp.ts ++++ b/front_end/panels/emulation/AdvancedApp.ts +@@ -83,7 +83,8 @@ export class AdvancedApp implements UI.App.App { + return; + } + +- const url = window.location.href.replace('devtools_app.html', 'device_mode_emulation_frame.html'); ++ // Next to the app's page, whichever it is (devtools-anywhere's anywhere_app.html). ++ const url = new URL('device_mode_emulation_frame.html', window.location.href).href; + this.toolboxWindow = window.open(url, undefined); + } + +diff --git a/front_end/panels/emulation/DeviceModeToolbar.ts b/front_end/panels/emulation/DeviceModeToolbar.ts +index aca819ebfb..a10c9d0e6a 100644 +--- a/front_end/panels/emulation/DeviceModeToolbar.ts ++++ b/front_end/panels/emulation/DeviceModeToolbar.ts +@@ -801,12 +801,8 @@ export class DeviceModeToolbar extends UI.Widget.Widget { + appendToggleItem( + contextMenu.headerSection(), this.showRulersSetting, i18nString(UIStrings.hideRulers), + i18nString(UIStrings.showRulers), undefined, 'rulers'); +- appendToggleItem( +- contextMenu.defaultSection(), this.showDeviceScaleFactorSetting, i18nString(UIStrings.removeDevicePixelRatio), +- i18nString(UIStrings.addDevicePixelRatio), undefined, 'device-pixel-ratio'); +- appendToggleItem( +- contextMenu.defaultSection(), this.showUserAgentTypeSetting, i18nString(UIStrings.removeDeviceType), +- i18nString(UIStrings.addDeviceType), undefined, 'device-type'); ++ // No device pixel ratio or device type: a page cannot have its devicePixelRatio or its ++ // user agent changed (devtools-anywhere), so neither is offered. + contextMenu.appendItemsAtLocation('deviceModeMenu'); + contextMenu.footerSection().appendItem( + i18nString(UIStrings.resetToDefaults), this.reset.bind(this), {jslogContext: 'reset-to-defaults'}); +diff --git a/front_end/panels/emulation/emulation-meta.ts b/front_end/panels/emulation/emulation-meta.ts +index 40bd5bb1ca..ce94de9d03 100644 +--- a/front_end/panels/emulation/emulation-meta.ts ++++ b/front_end/panels/emulation/emulation-meta.ts +@@ -73,7 +73,7 @@ UI.ActionRegistration.registerActionExtension({ + const Emulation = await loadEmulationModule(); + return new Emulation.DeviceModeView.ActionDelegate(); + }, +- condition: Root.Runtime.conditions.canDock, ++ condition: Root.Runtime.conditions.canEmulateDevice, + title: i18nLazyString(UIStrings.toggleDeviceToolbar), + iconClass: UI.ActionRegistration.IconClass.LARGEICON_PHONE, + bindings: [ +@@ -95,7 +95,7 @@ UI.ActionRegistration.registerActionExtension({ + const Emulation = await loadEmulationModule(); + return new Emulation.DeviceModeView.ActionDelegate(); + }, +- condition: Root.Runtime.conditions.canDock, ++ condition: Root.Runtime.conditions.canEmulateDevice, + title: i18nLazyString(UIStrings.captureScreenshot), + }); + +@@ -106,7 +106,7 @@ UI.ActionRegistration.registerActionExtension({ + const Emulation = await loadEmulationModule(); + return new Emulation.DeviceModeView.ActionDelegate(); + }, +- condition: Root.Runtime.conditions.canDock, ++ condition: Root.Runtime.conditions.canEmulateDevice, + title: i18nLazyString(UIStrings.captureFullSizeScreenshot), + }); + +@@ -117,7 +117,7 @@ UI.ActionRegistration.registerActionExtension({ + const Emulation = await loadEmulationModule(); + return new Emulation.DeviceModeView.ActionDelegate(); + }, +- condition: Root.Runtime.conditions.canDock, ++ condition: Root.Runtime.conditions.canEmulateDevice, + title: i18nLazyString(UIStrings.captureNodeScreenshot), + }); + +@@ -159,7 +159,7 @@ Common.Settings.registerSettingExtension({ + + UI.Toolbar.registerToolbarItem({ + actionId: 'emulation.toggle-device-mode', +- condition: Root.Runtime.conditions.canDock, ++ condition: Root.Runtime.conditions.canEmulateDevice, + location: UI.Toolbar.ToolbarItemLocation.MAIN_TOOLBAR_LEFT, + order: 2, + }); +@@ -169,7 +169,7 @@ UI.AppProvider.registerAppProvider({ + const Emulation = await loadEmulationModule(); + return Emulation.AdvancedApp.AdvancedAppProvider.instance(); + }, +- condition: Root.Runtime.conditions.canDock, ++ condition: Root.Runtime.conditions.canEmulateDevice, + order: 0, + }); + +diff --git a/front_end/panels/screencast/screencast-meta.ts b/front_end/panels/screencast/screencast-meta.ts +index b8abbb9d6b..c499ab4f54 100644 +--- a/front_end/panels/screencast/screencast-meta.ts ++++ b/front_end/panels/screencast/screencast-meta.ts +@@ -2,6 +2,7 @@ + // Use of this source code is governed by a BSD-style license that can be + // found in the LICENSE file. + ++import * as Root from '../../core/root/root.js'; + import * as UI from '../../ui/legacy/legacy.js'; + + import type * as Screencast from './screencast.js'; +@@ -22,6 +23,8 @@ UI.Toolbar.registerToolbarItem({ + }, + order: 1, + location: UI.Toolbar.ToolbarItemLocation.MAIN_TOOLBAR_LEFT, ++ // Device mode shows the page itself, and takes the toolbar's device button. ++ condition: () => !Root.Runtime.conditions.canEmulateDevice(), + }); + + UI.AppProvider.registerAppProvider({ +@@ -29,6 +32,7 @@ UI.AppProvider.registerAppProvider({ + const Screencast = await loadScreencastModule(); + return Screencast.ScreencastApp.ScreencastAppProvider.instance(); + }, ++ condition: () => !Root.Runtime.conditions.canEmulateDevice(), + order: 1, + }); + diff --git a/packages/devtools-frontend/scripts/build.ts b/packages/devtools-frontend/scripts/build.ts index 3da32b1..e889c53 100644 --- a/packages/devtools-frontend/scripts/build.ts +++ b/packages/devtools-frontend/scripts/build.ts @@ -250,7 +250,8 @@ if (!args.has('--copy-only')) { // The release files (the ones Chrome ships, listed in devtools_resources.grd) serve every // DevTools app. Only what anywhere_app can load is kept: the modules reachable from its // entrypoint through imports and `new URL(…, import.meta.url)`, the assets next to them, -// and the UI strings of those modules. +// and the UI strings of those modules. Device mode, when undocked, draws its toolbar into the +// page device_mode_emulation_frame.html, which the shell shows in an iframe: it is kept too. const gen = path.join(out, 'gen/front_end'); const grd = fs.readFileSync(path.join(gen, 'devtools_resources.grd'), 'utf8'); const files = [...grd.matchAll(/]*file="([^"]+)"/g)] @@ -259,7 +260,8 @@ const files = [...grd.matchAll(/]*file="([^"]+)"/g)] const released = new Set(files); const reachable = new Set(); -const queue = ['entrypoints/anywhere_app/anywhere_app.js']; +const PAGES = ['anywhere_app.html', 'device_mode_emulation_frame.html']; +const queue = PAGES.map(page => `entrypoints/${page.replace('.html', '')}/${page.replace('.html', '.js')}`); for (let file; (file = queue.pop());) { if (reachable.has(file) || !released.has(file)) continue; reachable.add(file); @@ -292,7 +294,7 @@ const OWN_STRINGS = 'entrypoints/anywhere_app/anywhere_app.ts'; } const keep = (file: string) => { if (file.endsWith('.js')) return reachable.has(file); - if (file.endsWith('.html')) return file === 'anywhere_app.html'; + if (file.endsWith('.html')) return PAGES.includes(file); if (file.startsWith(LOCALES)) return false; // written below return !file.includes('/') || file.startsWith('Images/') || inModule(file); }; diff --git a/packages/devtools-frontend/scripts/bundle.ts b/packages/devtools-frontend/scripts/bundle.ts index 550810c..bf4696b 100644 --- a/packages/devtools-frontend/scripts/bundle.ts +++ b/packages/devtools-frontend/scripts/bundle.ts @@ -4,7 +4,8 @@ // - each module loaded with import() (a panel, mostly) is a chunk with what only it needs, // loaded when it is; the entry, what several of them share, and the ones too small to be // worth a request are the chunk DevTools loads when it opens; -// - each worker is a single file of its own; +// - each worker is a single file of its own, and so is device mode's page for when DevTools is +// undocked (device_mode_emulation_frame.html, which the shell shows in an iframe); // - the SVG icons are inlined into Images.js as data: URLs (each declared once, as the CSS // variable every use reads); // - every bundled file is named after a hash of its content, so it can be cached for good: @@ -22,6 +23,9 @@ import { transform } from 'esbuild'; const OUT_DIR = 'bundle'; const APP = 'entrypoints/anywhere_app/anywhere_app.js'; +/** Device mode's page for when DevTools is undocked, and the module it loads. */ +const TOOLBOX_PAGE = 'device_mode_emulation_frame.html'; +const TOOLBOX = 'entrypoints/device_mode_emulation_frame/device_mode_emulation_frame.js'; const WORKERS = [ 'entrypoints/formatter_worker/formatter_worker-entrypoint.js', 'entrypoints/wasmparser_worker/wasmparser_worker-entrypoint.js', @@ -213,6 +217,7 @@ export async function bundle(src: string, out: string): Promise { // The workers first: the app names their files. for (const worker of WORKERS) workerOutput.set(worker, `./${await write(worker, false)}`); const app = await write(APP, true); + const toolbox = await write(TOOLBOX, false); if (!legacyImport) throw new Error('bundle: Root.Runtime#loadLegacyModule not found (upstream changed?)'); // The text of the bundle and of the other text files, to tell whether an SVG is still named. @@ -239,9 +244,15 @@ export async function bundle(src: string, out: string): Promise { const dir = thirdPartyDir(name); if (dir) thirdParty.add(dir); } - const html = path.join(out, 'anywhere_app.html'); - const page = fs.readFileSync(html, 'utf8'); - if (!page.includes(`./${APP}`)) throw new Error(`bundle: anywhere_app.html does not load ./${APP}`); - fs.writeFileSync(html, page.replace(`./${APP}`, `./${OUT_DIR}/${app}`)); + const pages: [file: string, module: string, bundled: string][] = [ + ['anywhere_app.html', APP, app], + [TOOLBOX_PAGE, TOOLBOX, toolbox], + ]; + for (const [file, module, bundled] of pages) { + const html = path.join(out, file); + const page = fs.readFileSync(html, 'utf8'); + if (!page.includes(`./${module}`)) throw new Error(`bundle: ${file} does not load ./${module}`); + fs.writeFileSync(html, page.replace(`./${module}`, `./${OUT_DIR}/${bundled}`)); + } return [...thirdParty].sort(); } diff --git a/packages/devtools-frontend/src/anywhere_app/BUILD.gn b/packages/devtools-frontend/src/anywhere_app/BUILD.gn index 989e5ce..97a4190 100644 --- a/packages/devtools-frontend/src/anywhere_app/BUILD.gn +++ b/packages/devtools-frontend/src/anywhere_app/BUILD.gn @@ -26,6 +26,7 @@ devtools_entrypoint("entrypoint") { "../../panels/console:meta", "../../panels/console_counters:meta", "../../panels/elements:meta", + "../../panels/emulation:meta", "../../panels/mobile_throttling:meta", "../../panels/network:meta", "../../panels/screencast:meta", diff --git a/packages/devtools-frontend/src/anywhere_app/anywhere_app.ts b/packages/devtools-frontend/src/anywhere_app/anywhere_app.ts index eb56d99..5065bd4 100644 --- a/packages/devtools-frontend/src/anywhere_app/anywhere_app.ts +++ b/packages/devtools-frontend/src/anywhere_app/anywhere_app.ts @@ -19,6 +19,8 @@ import '../../panels/mobile_throttling/mobile_throttling-meta.js'; import '../../panels/application/application-meta.js'; // The page as an image, with input forwarded to it: for when the page is not in view. import '../../panels/screencast/screencast-meta.js'; +// Device mode, in shell mode only (`can_emulate_device`): there the screencast is left out. +import '../../panels/emulation/emulation-meta.js'; import '../../panels/settings/settings-meta.js'; import '../../ui/legacy/components/perf_ui/perf_ui-meta.js'; import '../../ui/legacy/components/quick_open/quick_open-meta.js'; @@ -38,6 +40,7 @@ import { type FrontendHash, HIDDEN_NOTICE, OWN_SCRIPTS, + SIDEBAR_SIZE_NOTICE, type RemoteFrontendHash, REQUEST_KEY, } from './embedder.js'; @@ -165,6 +168,65 @@ if (!remote) { Root.Runtime.Runtime.setQueryParamForTesting('can_pause', 'false'); const dock = param('dock'); +// Shell mode: the page is in the shell's other iframe, which the shell places where this frontend +// says (Chrome's setInspectedPageBounds), and resizes for device mode. DevTools then lays out the +// page and itself as in Chrome (upstream's AdvancedApp, which device mode needs). +if (!remote && param('shell') === '1') { + Root.Runtime.Runtime.setQueryParamForTesting('can_emulate_device', 'true'); +} +// Undocked, upstream's AdvancedApp opens device mode's page in a window (Chrome shows it in the +// tab, under DevTools' window), which hands it its document through window.opener. The shell +// shows the page in an iframe instead, a sibling of this frontend's (same origin): once loaded, +// its document is handed over from here. +const TOOLBOX_PAGE = 'device_mode_emulation_frame.html'; +const isToolbox = (url: string): boolean => new URL(url, location.href).pathname.endsWith(`/${TOOLBOX_PAGE}`); +/** The shell's iframe of device mode's page, once loaded. */ +function loadedToolbox(): Document|null { + for (let i = 0; i < window.parent.frames.length; ++i) { + try { + const frame = window.parent.frames[i]; + if (isToolbox(frame.location.href) && frame.document.readyState === 'complete') { + return frame.document; + } + } catch { + // Another origin: the page. + } + } + return null; +} +function handOverToolbox(deadline: number): void { + const toolbox = loadedToolbox(); + if (toolbox) { + useDefaultTheme(toolbox); + Reflect.get(globalThis, 'Emulation')?.AdvancedApp?.instance().deviceModeEmulationFrameLoaded(toolbox); + } else if (Date.now() < deadline) { + setTimeout(() => handOverToolbox(deadline), 100); + } +} +const open = window.open.bind(window); +window.open = (url?: string|URL, target?: string, features?: string) => { + if (!remote && url !== undefined && isToolbox(String(url))) { + notifyEmbedder({type: 'toolbox', url: new URL(url, location.href).href}); + handOverToolbox(Date.now() + 10000); + return null; + } + return open(url, target, features); +}; +let splitWatched = false; +host.setInspectedPageBounds = bounds => { + notifyEmbedder({type: 'page-bounds', ...bounds}); + const split = UI.InspectorView.InspectorView.maybeGetInspectorViewInstance()?.ownerSplit(); + if (!split) { + return; + } + notifyEmbedder({type: 'split', size: split.sidebarSize()}); + if (!splitWatched) { + splitWatched = true; + split.addEventListener( + UI.SplitWidget.Events.SIDEBAR_SIZE_CHANGED, event => notifyEmbedder({type: 'split', size: event.data})); + } +}; + // devtools-anywhere's own scripts (the probe, the bundle, snapDOM) are listed in Sources, since // they hook globals the page uses, but ignore-listed like extension scripts in Chrome: their stack // frames collapse, and "Hide ignore-listed sources" hides them. The rule is added once, so the @@ -326,8 +388,15 @@ window.addEventListener('message', event => { if (typeof event.data !== 'string') { const data: unknown = event.data; const notice: unknown = typeof data === 'object' && data !== null ? Reflect.get(data, REQUEST_KEY) : null; - if (typeof notice === 'object' && notice !== null && Reflect.get(notice, 'type') === HIDDEN_NOTICE) { + if (typeof notice !== 'object' || notice === null) { + return; + } + const type: unknown = Reflect.get(notice, 'type'); + const size: unknown = Reflect.get(notice, 'size'); + if (type === HIDDEN_NOTICE) { onHidden(); + } else if (type === SIDEBAR_SIZE_NOTICE && typeof size === 'number' && Number.isFinite(size)) { + UI.InspectorView.InspectorView.maybeGetInspectorViewInstance()?.ownerSplit()?.setSidebarSize(size); } return; } @@ -339,21 +408,26 @@ window.addEventListener('message', event => { // dynamic theme (blue-tinted surfaces). Here the page names Chrome's default theme itself and // the request is reported as loaded, which gives Chrome's default look: the palette's // fallbacks are its colors. -const defaultTheme = document.createElement('style'); -defaultTheme.textContent = ':root { --user-color-source: baseline-default; }'; -document.head.append(defaultTheme); -// The link is added once, at startup: watching stops then, not to run on every node DevTools adds. -const themeColorsObserver = new MutationObserver(records => { - for (const record of records) { - for (const node of record.addedNodes) { - if (node instanceof HTMLLinkElement && node.href.startsWith('devtools://theme/colors.css')) { - node.dispatchEvent(new Event('load')); - themeColorsObserver.disconnect(); +// Device mode's page for when DevTools is undocked gets the same. +function useDefaultTheme(doc: Document): void { + const defaultTheme = doc.createElement('style'); + defaultTheme.textContent = ':root { --user-color-source: baseline-default; }'; + doc.head.append(defaultTheme); + // The link is added once, at startup: watching stops then, not to run on every node DevTools adds. + const themeColorsObserver = new MutationObserver(records => { + for (const record of records) { + for (const node of record.addedNodes) { + if (node instanceof (doc.defaultView ?? window).HTMLLinkElement && + node.href.startsWith('devtools://theme/colors.css')) { + node.dispatchEvent(new Event('load')); + themeColorsObserver.disconnect(); + } } } - } -}); -themeColorsObserver.observe(document, {childList: true, subtree: true}); + }); + themeColorsObserver.observe(doc, {childList: true, subtree: true}); +} +useDefaultTheme(document); // The embedder's bar sits on top of the toolbar: it takes the toolbar's colors, sent whenever // the theme is applied (which sets classes on the root element). diff --git a/tests/e2e/device-mode.test.ts b/tests/e2e/device-mode.test.ts new file mode 100644 index 0000000..226ce88 --- /dev/null +++ b/tests/e2e/device-mode.test.ts @@ -0,0 +1,257 @@ +// Device mode in shell mode, with the DevTools frontend built from the official sources: the +// frontend lays out the page and itself as in Chrome (shell-layout.ts), and device mode sizes the +// page. Skipped when the frontend has not been built. +import { existsSync } from 'node:fs'; +import { after, before, test } from 'node:test'; +import assert from 'node:assert/strict'; +import type { Frame, Page } from 'puppeteer-core'; +import { launch } from '../support/browser.ts'; +import type { Env } from '../support/browser.ts'; + +const built = existsSync(new URL('../../packages/devtools-frontend/dist/anywhere_app.html', import.meta.url)); +const skip = built ? false : 'run `pnpm --filter @devtools-anywhere/devtools-frontend build:frontend` first'; + +let env: Env | undefined; +let page: Page; +/** Uncaught errors in the shell, the frontend and the toolbox. */ +const errors: string[] = []; + +before(async () => { + if (!built) return; + env = await launch(); + page = await env.browser.newPage(); + page.on('pageerror', error => errors.push(error instanceof Error ? error.message : String(error))); + await page.setViewport({ width: 1000, height: 800 }); + await page.goto(env.baseUrl + '/tests/e2e/pages/device-mode.html'); +}); +after(() => env?.close()); + +async function waitFor(get: () => T | Promise, timeout = 15000): Promise> { + const start = Date.now(); + for (;;) { + const value = await Promise.resolve() + .then(get) + .catch(() => null); + if (value) return value; + if (Date.now() - start > timeout) throw new Error('timed out'); + await new Promise(r => setTimeout(r, 100)); + } +} + +const frontend = () => waitFor(() => page.frames().find(f => f.url().includes('anywhere_app.html'))); + +/** Runs an action of the frontend's (UI.ActionRegistry), as its toolbar button or menu item would. */ +async function runAction(id: string) { + await ( + await frontend() + ).evaluate(async id => { + const script = document.querySelector('script[type=module]')?.src ?? ''; + // The built frontend has no type declarations; only this much of it is used. + const UI: { + ActionRegistry: { ActionRegistry: { instance(): { getAction(id: string): { execute(): Promise } } } }; + } = await (await import(script)).modules['ui/legacy/legacy.js'](); + await UI.ActionRegistry.ActionRegistry.instance().getAction(id).execute(); + }, id); +} + +/** Moves DevTools to a dock side, as the main menu's "Dock side" does. */ +async function dock(side: string) { + await ( + await frontend() + ).evaluate(async side => { + const script = document.querySelector('script[type=module]')?.src ?? ''; + const UI: { DockController: { DockController: { instance(): { setDockSide(side: string): void } } } } = await ( + await import(script) + ).modules['ui/legacy/legacy.js'](); + UI.DockController.DockController.instance().setDockSide(side); + }, side); +} + +/** Picks a device in device mode's toolbar, in `frame` (the frontend, or undocked the toolbox). */ +async function pickDevice(frame: Frame, name: string) { + await frame.evaluate(name => { + const find = (root: Document | ShadowRoot): HTMLSelectElement | null => { + const found = root.querySelector('select[aria-label="Device type"]'); + if (found) return found; + for (const el of root.querySelectorAll('*')) { + const inShadow = el.shadowRoot && find(el.shadowRoot); + if (inShadow) return inShadow; + } + return null; + }; + const select = find(document); + if (!select) throw new Error('no device select'); + select.selectedIndex = [...select.options].findIndex(o => o.text === name); + select.dispatchEvent(new Event('change', { bubbles: true })); + }, name); +} + +/** Where the page iframe is, and the page's viewport. */ +const pageState = () => + page.evaluate(() => { + const iframe = document.getElementById('devtools-anywhere-page'); + if (!(iframe instanceof HTMLIFrameElement) || !iframe.contentWindow) throw new Error('no page iframe'); + const { left, top, width, height } = iframe.getBoundingClientRect(); + return { + rect: { left, top, width, height }, + viewport: { width: iframe.contentWindow.innerWidth, height: iframe.contentWindow.innerHeight }, + }; + }); + +/** + * The frontend's InspectedPagePlaceholder, where it lays out the page, in the shell's coordinates: + * in `origin`, the frontend's frame or undocked the toolbox's. + */ +const placeholder = async (origin: Frame) => { + const rect = await ( + await frontend() + ).evaluate(() => { + // The built frontend has no type declarations: AdvancedApp's placeholder, a private field. + const app: unknown = Reflect.get(globalThis, 'Emulation')?.AdvancedApp?.instance(); + const el: unknown = app && Reflect.get(Reflect.get(app, 'inspectedPagePlaceholder'), 'element'); + if (!(el instanceof Element)) throw new Error('no placeholder'); + const { left, top, width, height } = el.getBoundingClientRect(); + return { left, top, width, height }; + }); + const offset = await ( + await origin.frameElement() + )?.evaluate(f => { + const { left, top } = f.getBoundingClientRect(); + return { left, top }; + }); + return { ...rect, left: rect.left + (offset?.left ?? 0), top: rect.top + (offset?.top ?? 0) }; +}; + +const near = (a: number, b: number, what: string) => assert.ok(Math.abs(a - b) <= 2, `${what}: ${a} vs ${b}`); + +test('in shell mode the frontend fills the shell and places the page, as in Chrome', { skip }, async () => { + const fe = await frontend(); + const frame = await waitFor(async () => { + const rect = await (await fe.frameElement())?.evaluate(f => f.getBoundingClientRect().toJSON()); + return rect && rect.width === 1000 && rect.height === 800 ? rect : null; + }); + assert.ok(frame); + // The page goes where the frontend's placeholder is, on top of it. + const where = await waitFor(async () => { + const [state, spot] = [await pageState(), await placeholder(fe)]; + return state.rect.height < 800 && Math.abs(state.rect.top - spot.top) <= 2 ? { state, spot } : null; + }); + near(where.state.rect.width, where.spot.width, 'width'); + near(where.state.rect.height, where.spot.height, 'height'); + assert.equal(where.state.viewport.width, Math.round(where.state.rect.width)); + // The toolbar's device button is device mode's, not the screencast's. + const labels = await fe.evaluate(() => { + const out: string[] = []; + const walk = (root: Document | ShadowRoot) => { + for (const el of root.querySelectorAll('[aria-label]')) out.push(el.getAttribute('aria-label') ?? ''); + for (const el of root.querySelectorAll('*')) if (el.shadowRoot) walk(el.shadowRoot); + }; + walk(document); + return out; + }); + assert.ok(labels.includes('Toggle device toolbar'), 'device mode button'); + assert.ok(!labels.includes('Toggle screencast'), 'no screencast button'); +}); + +test('device mode sizes the page as the device, scaled to fit, and takes its screenshot', { skip }, async () => { + await runAction('emulation.toggle-device-mode'); + const fe = await frontend(); + await waitFor(() => pickDevice(fe, 'iPhone SE').then(() => true)); + const state = await waitFor(async () => { + const s = await pageState(); + return s.viewport.width === 375 && s.viewport.height === 667 ? s : null; + }); + // Scaled to fit the room the frontend gives it. + assert.ok(state.rect.height < 667, `scaled: ${state.rect.height}`); + near(state.rect.width / state.rect.height, 375 / 667, 'aspect'); + // The screenshot command saves the page at the device's size (at the page's own pixel ratio): + // the image it draws, and the link it clicks to download it (not followed here). + await fe.evaluate(() => { + const saved: { name?: string; width?: number; height?: number } = {}; + Reflect.set(window, '__saved', saved); + const convert = OffscreenCanvas.prototype.convertToBlob; + OffscreenCanvas.prototype.convertToBlob = function (this: OffscreenCanvas, options) { + Object.assign(saved, { width: this.width, height: this.height }); + return convert.call(this, options); + }; + HTMLAnchorElement.prototype.click = function (this: HTMLAnchorElement) { + saved.name = this.download; + }; + }); + await runAction('emulation.capture-screenshot'); + const saved = await waitFor(() => + fe.evaluate(() => { + const value: { name?: string; width?: number; height?: number } = Reflect.get(window, '__saved'); + return value.name ? { name: value.name, width: value.width ?? 0, height: value.height ?? 0 } : null; + }) + ); + assert.match(saved.name, /\(iPhone SE\)\.png$/); + assert.equal(saved.width, 375); + near(saved.height, 667, 'screenshot height'); +}); + +test('hidden, the page fills the shell at its own size; shown again, device mode is back', { skip }, async () => { + const toggle = () => + page.evaluate(() => + document + .querySelector('devtools-anywhere') + ?.dispatchEvent(new CustomEvent('devtools-anywhere-control', { detail: { toggle: true } })) + ); + await toggle(); + const hidden = await waitFor(async () => { + const s = await pageState(); + return s.viewport.width === 1000 && s.viewport.height === 800 ? s : null; + }); + assert.deepEqual(hidden.rect, { left: 0, top: 0, width: 1000, height: 800 }); + await toggle(); + await waitFor(async () => (await pageState()).viewport.width === 375); +}); + +test('undocked, device mode draws into its own page under the floating panel', { skip }, async () => { + await dock('undocked'); + const toolbox = await waitFor(() => page.frames().find(f => f.url().includes('device_mode_emulation_frame.html'))); + // Its toolbar is there, and the page goes where it says. + await waitFor(() => pickDevice(toolbox, 'iPhone SE').then(() => true)); + const where = await waitFor(async () => { + const [state, spot] = [await pageState(), await placeholder(toolbox).catch(() => null)]; + return spot && state.viewport.width === 375 && Math.abs(state.rect.left - spot.left) <= 2 ? { state, spot } : null; + }); + near(where.state.rect.width, where.spot.width, 'width'); + await dock('bottom'); + await waitFor(async () => !page.frames().some(f => f.url().includes('device_mode_emulation_frame.html'))); + await waitFor(async () => (await pageState()).rect.height < 800); +}); + +test('on a touch screen, a handle on the edge between the page and DevTools resizes them', { skip }, async () => { + // A touch screen, as the shell sees it; a resize lays the shell out again. + await page.evaluate(() => { + const matchMedia = window.matchMedia.bind(window); + window.matchMedia = query => (query === '(pointer: coarse)' ? matchMedia('all') : matchMedia(query)); + window.dispatchEvent(new Event('resize')); + }); + /** The handle's center, once shown. */ + const handle = () => + page.evaluate(() => { + const el = [...document.body.children].find( + (e): e is HTMLElement => e instanceof HTMLDivElement && e.style.cursor === 'row-resize' + ); + if (!el || el.style.display === 'none') return null; + const { left, top, width, height } = el.getBoundingClientRect(); + return { x: left + width / 2, y: top + height / 2 }; + }); + const start = await waitFor(handle); + await page.mouse.move(start.x, start.y); + await page.mouse.down(); + await page.mouse.move(start.x, start.y - 150, { steps: 5 }); + await page.mouse.up(); + // DevTools took the room (as much as its minimum sizes allow): the edge, and the handle on it, + // moved up. + await waitFor(async () => { + const now = await handle(); + return now && now.y < start.y - 100; + }); +}); + +test('no uncaught errors', { skip }, () => { + assert.deepEqual(errors, []); +}); diff --git a/tests/e2e/pages/device-mode.html b/tests/e2e/pages/device-mode.html new file mode 100644 index 0000000..095f70a --- /dev/null +++ b/tests/e2e/pages/device-mode.html @@ -0,0 +1,20 @@ + + + + + device mode + + + + +

hello

+ + diff --git a/tests/e2e/real-frontend.test.ts b/tests/e2e/real-frontend.test.ts index 50f6ddb..e7f48b4 100644 --- a/tests/e2e/real-frontend.test.ts +++ b/tests/e2e/real-frontend.test.ts @@ -386,6 +386,14 @@ test('the screencast is off by default and shows the page when turned on', { ski // asElement() is typed as a handle to any Node; the page function returns an Element or null. const button = toggle.asElement() as ElementHandle | null; assert.ok(button, 'the toolbar offers the screencast'); + // Device mode needs the shell, which can resize the page (device-mode.test.ts). + const deviceMode = await frontend.evaluate(() => { + const find = (root: Document | ShadowRoot): boolean => + !!root.querySelector('[aria-label="Toggle device toolbar"]') || + [...root.querySelectorAll('*')].some(el => !!el.shadowRoot && find(el.shadowRoot)); + return find(document); + }); + assert.equal(deviceMode, false, 'no device mode outside the shell'); await button.click(); await waitFor(async () => ((await frames()) ?? 0) > 0); await button.click(); From a7a4049e93f909b7127cbad4f8b277cd99f14e7a Mon Sep 17 00:00:00 2001 From: jerrychan7 Date: Sun, 1 Nov 2026 19:07:12 +0800 Subject: [PATCH 07/26] docs: describe device mode and the frontend's layout in shell mode README: in shell mode DevTools lays out the page as in Chrome, with device mode on the toolbar's device button, undocked too; device mode sets the page's size only, not its pixel ratio, user agent or touch input. DEV: how the entrypoint, the shell and the patch fit together, and device mode's page bundled as one file. The changelog notes it under Unreleased. --- CHANGELOG.md | 1 + CHANGELOG.zh-CN.md | 1 + DEV.md | 3 ++- DEV.zh-CN.md | 3 ++- README.md | 6 ++++-- README.zh-CN.md | 6 ++++-- 6 files changed, 14 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d65c2ae..259f17d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ All packages share one version and are released together (see DEV.md, "Releasing ## Unreleased +- Device mode in shell mode, as in Chrome: DevTools lays out the page and itself, the toolbar's device button sizes the page as a device (scaled to fit) and its menu takes screenshots, docked or undocked. It sets the page's size only, not its pixel ratio, user agent or touch input. On touch screens a handle on the edge between the page and DevTools resizes them. Outside shell mode the button is still the screencast's. - `Page.captureScreenshot` and `Page.getLayoutMetrics`: screenshots of the viewport, a region or the whole page, rendered by snapDOM and marked `approximate: true`, for tools and agents that drive the page over CDP. ## 0.1.0 diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md index 077c319..a3a4f1b 100644 --- a/CHANGELOG.zh-CN.md +++ b/CHANGELOG.zh-CN.md @@ -4,6 +4,7 @@ ## Unreleased +- 外壳模式下新增设备模式,和 Chrome 一样:由 DevTools 排布页面和自己,工具栏上的设备按钮按设备尺寸设置页面(缩放到适合窗口),它的菜单可以截图,停靠和取消停靠时都能用。它只设置页面的尺寸,不改变像素比、UA 和触摸输入。触屏上,页面和 DevTools 的分界处有一条拖动条用来调整大小。非外壳模式下,这个按钮仍然是投屏。 - 新增 `Page.captureScreenshot` 和 `Page.getLayoutMetrics`:可以截取视口、指定区域或整个页面,由 snapDOM 绘制,并标注 `approximate: true`,供通过 CDP 操作页面的工具和 agent 使用。 ## 0.1.0 diff --git a/DEV.md b/DEV.md index 52e3568..0356247 100644 --- a/DEV.md +++ b/DEV.md @@ -147,8 +147,9 @@ A test sits next to what it tests, as `.test.ts`; only tests that span pac - **Own entrypoint** `anywhere_app` instead of `devtools_app`: it imports only the panels that work against the in-page CDP agent (Elements, Console, Sources, Network, Application) and the screencast, so the others are neither shown nor loaded. The only patch registers this entrypoint in the build (7 lines in 4 `BUILD.gn`/`.gni` files). - **Embedder through the official host API**: Chrome talks to DevTools through native `InspectorFrontendHost` bindings. The entrypoint keeps the hosted-mode stub and only replaces `sendMessageToBackend`/`isHostedMode`, so CDP messages travel through `postMessage` to the embedding window, which passes its origin as `#?embedded=`. No connection code is patched. +- **Device mode in shell mode**: the entrypoint loads the emulation module, whose app (upstream's `AdvancedApp`) lays out the page and DevTools as in Chrome, when the shell passes `#?shell=1` (`can_emulate_device`); elsewhere upstream's simple app and the screencast stay. `setInspectedPageBounds` goes to the shell (`page-bounds`), which places the page iframe and answers `Emulation.setDeviceMetricsOverride` by sizing it (`packages/devtools-anywhere/src/shell-layout.ts`). Undocked, device mode's page `device_mode_emulation_frame.html` is shown by the shell in an iframe instead of a window, and the entrypoint hands it its document. `patches/device-mode-in-shell.patch` adds the condition, keeps the screencast out of shell mode, opens device mode's page next to `anywhere_app.html`, and drops the device pixel ratio and device type from the toolbar's menu. - **Small screens**: upstream's responsive layouts (vertical split in Elements, overflowing tabs) are kept as is. -- **Few requests**: upstream ships one bundle per module, about 150 requests to open DevTools, which is slow over HTTP/1.1 or a flaky network. `scripts/bundle.ts` rebundles them with rollup (tree-shaken) into `dist/bundle/`: each lazily loaded module (a panel, mostly) is a chunk loaded when it is, and the entry chunk holds what the entry imports, what several panels share and the small ones; each worker is one file, and the SVG icons are inlined as data: URLs. Opening DevTools takes about 10 requests. Every bundled file is named after a hash of its content and can be cached for good; only `anywhere_app.html`, which names the entry, must be revalidated. Since modules are no files of their own any more, the end-to-end tests reach them through `modules` exported by the entrypoint. +- **Few requests**: upstream ships one bundle per module, about 150 requests to open DevTools, which is slow over HTTP/1.1 or a flaky network. `scripts/bundle.ts` rebundles them with rollup (tree-shaken) into `dist/bundle/`: each lazily loaded module (a panel, mostly) is a chunk loaded when it is, and the entry chunk holds what the entry imports, what several panels share and the small ones; each worker is one file, and so is device mode's page for undocked DevTools; the SVG icons are inlined as data: URLs. Opening DevTools takes about 10 requests. Every bundled file is named after a hash of its content and can be cached for good; only `anywhere_app.html`, which names the entry, must be revalidated. Since modules are no files of their own any more, the end-to-end tests reach them through `modules` exported by the entrypoint. `packages/devtools-frontend/` contains: diff --git a/DEV.zh-CN.md b/DEV.zh-CN.md index 33fdcd2..2e32eff 100644 --- a/DEV.zh-CN.md +++ b/DEV.zh-CN.md @@ -147,8 +147,9 @@ pnpm typecheck # 先构建,再检查所有包 - **自有入口** `anywhere_app` 代替 `devtools_app`:只引入能对接页面内 CDP agent的面板(元素、控制台、源代码、网络、应用)和投屏,其余面板既不显示也不加载。唯一的补丁就是在构建配置里注册这个入口(4 个 `BUILD.gn`/`.gni` 文件,共 7 行)。 - **通过官方宿主接口接入**:Chrome 通过原生的 `InspectorFrontendHost` 与 DevTools 通信。入口保留托管模式的 stub,只替换 `sendMessageToBackend` 和 `isHostedMode`,让 CDP 消息经 `postMessage` 与嵌入它的窗口往来;嵌入方通过 `#?embedded=` 告知自己的 origin。连接相关代码一行都不改。 +- **外壳模式下的设备模式**:外壳传入 `#?shell=1` 时(`can_emulate_device`),入口加载 emulation 模块,由它的应用(上游的 `AdvancedApp`)像 Chrome 一样排布页面和 DevTools;其他情况仍用上游的简单应用和投屏。`setInspectedPageBounds` 发给外壳(`page-bounds`),外壳据此摆放页面 iframe,并通过设置 iframe 的尺寸来响应 `Emulation.setDeviceMetricsOverride`(`packages/devtools-anywhere/src/shell-layout.ts`)。取消停靠时,设备模式的页面 `device_mode_emulation_frame.html` 由外壳放在 iframe 里显示,不另开窗口,由入口把这个页面的 document 交给设备模式。`patches/device-mode-in-shell.patch` 加入这个条件,让投屏不在外壳模式下出现,让设备模式的页面在 `anywhere_app.html` 旁边打开,并从工具栏菜单中去掉设备像素比和设备类型。 - **小屏**:沿用上游自带的响应式布局(元素面板窄屏时上下分栏、标签页溢出折叠)。 -- **少请求**:上游每个模块各打成一个包,打开 DevTools 要发约 150 个请求,在 HTTP/1.1 或网络不稳时很慢。`scripts/bundle.ts` 用 rollup 把它们重新打包(含摇树)到 `dist/bundle/`:每个懒加载的模块(主要是面板)各成一个 chunk,用到时才加载;入口 chunk 包含入口引入的模块、多个面板共用的模块和体积很小的模块;每个 worker 打成单个文件,SVG 图标内联成 data: URL。打开 DevTools 约 10 个请求。打包出的文件都以内容 hash 命名,可以永久缓存;只有指明入口的 `anywhere_app.html` 需要每次重新验证。由于模块不再是独立文件,端到端测试通过入口导出的 `modules` 访问它们。 +- **少请求**:上游每个模块各打成一个包,打开 DevTools 要发约 150 个请求,在 HTTP/1.1 或网络不稳时很慢。`scripts/bundle.ts` 用 rollup 把它们重新打包(含摇树)到 `dist/bundle/`:每个懒加载的模块(主要是面板)各成一个 chunk,用到时才加载;入口 chunk 包含入口引入的模块、多个面板共用的模块和体积很小的模块;每个 worker 打成单个文件,取消停靠时设备模式用的页面也一样;SVG 图标内联成 data: URL。打开 DevTools 约 10 个请求。打包出的文件都以内容 hash 命名,可以永久缓存;只有指明入口的 `anywhere_app.html` 需要每次重新验证。由于模块不再是独立文件,端到端测试通过入口导出的 `modules` 访问它们。 `packages/devtools-frontend/` 里有: diff --git a/README.md b/README.md index 1e8e729..13e6a65 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ This project depends on neither Chii nor chobitsu. Their source is read as a ref - WebSocket connections and their frames are shown, but not the handshake headers, which the page cannot see. - The screencast (the toolbar's device button, off by default) shows the viewport as [snapDOM](https://github.com/zumerlab/snapdom) redraws it from the DOM, not the pixels on screen: canvas, video and cross-origin images may be missing, and it updates about twice a second at most. snapDOM (about 90 KB gzip) is loaded only when the screencast starts or a screenshot is taken. - Screenshots (`Page.captureScreenshot`, for tools and agents that drive the page over CDP) are rendered by snapDOM the same way, so they miss what the screencast misses. The response says so with a field Chrome does not send, `approximate: true`. Otherwise the parameters behave as in Chrome: `clip` is in page coordinates, the image has device pixels (`devicePixelRatio` times `clip.scale`), and `captureBeyondViewport` without a clip takes the whole page. An image past the browser's canvas limits (32767 pixels a side, 16384 on Safari, 16384 × 16384 in area) is scaled down to fit, and snapDOM warns about it in the page's console. Browsers that cannot encode WebP (Safari) return PNG when WebP is asked for. + - Device mode (shell mode only, since it needs the page in an iframe it can resize) sets the page's size, as the viewport the page sees: media queries, `vw`/`vh` and `innerWidth` follow. It does not change `devicePixelRatio`, the user agent or touch input, which a page cannot change: the toolbar offers no device pixel ratio or device type, and a device preset sets only the size. Screenshots from its menu are snapDOM's, as above, at the page's own pixel ratio. - Input from the screencast is replayed as DOM events, which are not trusted (`isTrusted` is false): focus, clicks, scrolling and text editing are carried out, but pages that check `isTrusted` ignore them. - Cross-origin style sheets are read through a fetched copy, which needs CORS headers; their rules are read-only. - Forced pseudo-classes (`:hover`, `:focus`, … from the Styles pane) are emulated with companion style sheets that keep the cascade order; sheets from `@import` and `adoptedStyleSheets` are not covered. @@ -85,7 +86,8 @@ No standalone `devtools.html` is hosted, and there is no navigation to a blob UR 2. The probe, being the first script, sees the config in the top-level window. It writes an unclosed `` so the rest of the page becomes inert text (none of its scripts run), then replaces the document with the shell via `document.open()` / `document.write()`. The shell contains the page iframe (pointing to `location.href`) and `<devtools-anywhere>`, which knows it is the shell's element by the document it is in. (Chrome ignores `document.open()` after `window.stop()`, hence `<plaintext>`.) 3. Inside the iframe, the probe captures as usual and loads the bundle, which connects the page's CDP session to the shell. The shell relays messages between the page and the frontend. 4. When the page reloads or navigates, the new page says hello; the shell tells the frontend the page navigated and re-sends every `enable` command the frontend has sent, so the new page replays its own earliest output. The address bar and title follow the page, through loads, history traversals and same-document navigations (`pushState` where the Navigation API exists), so that a reload or a copied link opens the page shown. -5. The toolbar's × hides DevTools and stays in shell mode. The lock button again clears the config and reloads the page on its own; the choice is remembered in `localStorage`, and wins over the page's `data-default-shell-mode` from then on, unless the page sets `data-shell-mode-locked`. +5. DevTools fills the shell and lays out the page and itself, as in Chrome: the page iframe goes where the frontend's page area is, on top of it. Dock side and sizes are DevTools' own (its resizer; on touch screens, a wider handle on the edge between the page and DevTools). Undocked, DevTools is a window over the page. The toolbar's device button is device mode, as in Chrome, in place of the screencast: the page iframe is sized as the device and scaled to fit, and the toolbar's menu takes screenshots. Undocked, device mode's toolbar is drawn in a page of the frontend's (`device_mode_emulation_frame.html`, as Chrome does) shown under the window. +6. The toolbar's × hides DevTools and stays in shell mode; the page fills the shell at its own size until DevTools is shown again. The lock button again clears the config and reloads the page on its own; the choice is remembered in `localStorage`, and wins over the page's `data-default-shell-mode` from then on, unless the page sets `data-shell-mode-locked`. The stored URLs are only trusted when they have the same origin as the probe script; otherwise the config is cleared and the page loads normally. Shell mode therefore needs the probe, the bundle and the frontend served from the page's origin. @@ -157,7 +159,7 @@ Only `vite serve` is affected: the probe is injected as the first script of ever ### The element -A floating F12 button opens the panel; drag its bar to resize. Left of DevTools' settings button, the lock button switches shell mode on and off (a dialog explains it first, and an info bar shows while it is on) and the refresh button reloads the page; the toolbar's × closes the panel. DevTools' main menu (⋮) has "Dock side": bottom (the default), right, left, or undocked, a window over the page moved by its bar and resized from its corner. In shell mode the page shrinks to make room, as in Chrome; otherwise the panel covers the page. The side and sizes are remembered in localStorage, sizes as percentages of the viewport (`dvh`/`dvw`), so they fit any screen and orientation. Its one attribute, `devtools-frontend-url`, is optional: without it, the probe's `data-devtools-frontend-url` is used. The rest is the library's: the probe's options set the element up, and the user opens and closes it; it has no methods of its own. The page can restyle the F12 button with `devtools-anywhere::part(toggler)`, in shell mode too: there the button is in the page iframe, in a `<devtools-anywhere>` under the page's `<html>` that only holds it and opens and closes the shell's DevTools (the shell's own button shows only while the page in the iframe has no probe, such as another origin's page). The probe's element does not expose its iframe as a part. Keep the element in one place, since moving it reloads the iframe. +A floating F12 button opens the panel; drag its bar to resize (in shell mode, DevTools' own resizer, see [shell mode](#2-shell-mode-in-place-rewrite)). Left of DevTools' settings button, the lock button switches shell mode on and off (a dialog explains it first, and an info bar shows while it is on) and the refresh button reloads the page; the toolbar's × closes the panel. DevTools' main menu (⋮) has "Dock side": bottom (the default), right, left, or undocked, a window over the page moved by its bar and resized from its corner. In shell mode the page shrinks to make room, as in Chrome; otherwise the panel covers the page. The side and sizes are remembered in localStorage, sizes as percentages of the viewport (`dvh`/`dvw`), so they fit any screen and orientation. Its one attribute, `devtools-frontend-url`, is optional: without it, the probe's `data-devtools-frontend-url` is used. The rest is the library's: the probe's options set the element up, and the user opens and closes it; it has no methods of its own. The page can restyle the F12 button with `devtools-anywhere::part(toggler)`, in shell mode too: there the button is in the page iframe, in a `<devtools-anywhere>` under the page's `<html>` that only holds it and opens and closes the shell's DevTools (the shell's own button shows only while the page in the iframe has no probe, such as another origin's page). The probe's element does not expose its iframe as a part. Keep the element in one place, since moving it reloads the iframe. A page can also place `<devtools-anywhere>` itself, as a box where DevTools shows: a block in the page's flow (`display: block`), sized by the page's CSS, with DevTools loaded as soon as it is connected. The page shows and hides it with CSS or `hidden`; hidden, DevTools stays loaded. It has no F12 button, dock side or resizing, and never enters shell mode (DevTools offers no way to); inside the shell's page it works as on its own. `devtools-frontend-url` is its only attribute; others are ignored. DevTools' iframe in it can be styled with `devtools-anywhere::part(iframe)`. diff --git a/README.zh-CN.md b/README.zh-CN.md index ce5c246..8c93db1 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -42,6 +42,7 @@ - 能显示 WebSocket 连接和帧,但没有握手阶段的请求头和响应头,页面拿不到它们。 - 投屏(工具栏上的设备按钮,默认关闭)显示的是 [snapDOM](https://github.com/zumerlab/snapdom) 根据 DOM 重新绘制的视口,不是屏幕上的真实像素:canvas、视频和跨域图片可能缺失,每秒最多更新约两次。snapDOM(gzip 后约 90 KB)只在开始投屏或截图时加载。 - 截图(`Page.captureScreenshot`,供通过 CDP 操作页面的工具和 agent 使用)同样由 snapDOM 绘制,投屏缺失的内容截图里也会缺失。返回结果里会多一个 Chrome 没有的字段 `approximate: true` 来说明这一点。其余参数的行为和 Chrome 一致:`clip` 按页面坐标计算,图片按设备像素输出(`devicePixelRatio` 乘以 `clip.scale`),不带 `clip` 时设置 `captureBeyondViewport` 会截取整个页面。超出浏览器画布上限(单边 32767 像素,Safari 上 16384;面积 16384 × 16384)的图片会等比缩小,snapDOM 会在页面的控制台里给出警告。不支持编码 WebP 的浏览器(Safari)在请求 WebP 时返回 PNG。 + - 设备模式(只在外壳模式下提供,因为它要求页面在一个可以调整尺寸的 iframe 里)设置的是页面的尺寸,也就是页面看到的视口:媒体查询、`vw`/`vh` 和 `innerWidth` 都会随之变化。它不改变 `devicePixelRatio`、UA 和触摸输入,这些是页面改不了的:工具栏里没有设备像素比和设备类型,选择设备预设时只套用尺寸。它菜单里的截图同样由 snapDOM 绘制,按页面自己的像素比输出。 - 投屏上的输入以 DOM 事件重放,这些事件不是可信事件(`isTrusted` 为 false):聚焦、点击、滚动和文本编辑都会执行,但检查 `isTrusted` 的页面会忽略它们。 - 跨域样式表通过重新下载的副本读取,需要对方返回 CORS 头;这些规则是只读的。 - 强制伪类(样式面板里的 `:hover`、`:focus` 等)通过伴随样式表模拟,层叠顺序保持不变;`@import` 引入的样式表和 `adoptedStyleSheets` 不在覆盖范围内。 @@ -85,7 +86,8 @@ 2. 探针作为第一个脚本,在顶层窗口检测到配置后,先写入一个不闭合的 `<plaintext>`,让页面剩余的内容变成纯文本(页面脚本都不会执行),再用 `document.open()` / `document.write()` 把文档替换成外壳。外壳中有指向 `location.href` 的用户页面 iframe,以及 `<devtools-anywhere>`,它凭所在的文档认出自己是外壳的元素。(Chrome 在 `window.stop()` 之后会忽略 `document.open()`,所以改用 `<plaintext>`。) 3. 在 iframe 中,探针照常捕获,并加载 bundle,把页面的 CDP 会话连接到外壳。外壳在页面和前端之间转发消息。 4. 页面刷新或跳转后,新页面会发出 hello;外壳通知前端页面已导航,并把前端发过的所有 `enable` 命令重新发给新页面,新页面就会回放它自己最早的输出。地址栏和标题跟随页面变化,包括加载、前进后退和同文档导航(有 Navigation API 时也包括 `pushState`),因此刷新或复制链接打开的都是当前显示的页面。 -5. 点工具栏上的 × 只隐藏 devtools,仍留在外壳模式。再点一次锁形按钮会清除配置,并在普通模式下重新加载页面;这个选择会记在 `localStorage` 里,之后优先于页面的 `data-default-shell-mode`,除非页面设置了 `data-shell-mode-locked`。 +5. DevTools 铺满外壳,由它来排布页面和自己,和 Chrome 一样:页面 iframe 放在前端的页面区域上,叠在上层。停靠位置和尺寸由 DevTools 自己管理(用它的分隔条;触屏上,页面和 DevTools 的分界处有一条更宽的拖动条)。取消停靠时,DevTools 是页面上方的浮动窗口。工具栏上的设备按钮是设备模式,和 Chrome 一样,取代了投屏:页面 iframe 按设备尺寸设置并缩放到适合窗口,工具栏的菜单可以截图。取消停靠时,设备模式的工具栏画在前端的一个页面(`device_mode_emulation_frame.html`,Chrome 也是这么做的)里,显示在浮动窗口下方。 +6. 点工具栏上的 × 只隐藏 devtools,仍留在外壳模式;在重新显示 devtools 之前,页面按自己的尺寸铺满外壳。再点一次锁形按钮会清除配置,并在普通模式下重新加载页面;这个选择会记在 `localStorage` 里,之后优先于页面的 `data-default-shell-mode`,除非页面设置了 `data-shell-mode-locked`。 只有与探针脚本同源的地址才会被采信,否则清除配置、页面照常加载。因此外壳模式要求探针、bundle 和前端都由页面所在的源提供。 @@ -178,7 +180,7 @@ npx -p @devtools-anywhere/remote-server devtools-anywhere-remote --port 8080 ### 组件 -点击右下角的 F12 悬浮按钮打开面板;拖动面板的横条调整大小。DevTools 设置按钮左边,锁形按钮开关外壳模式(开启前有对话框说明,开启后顶部有提示条),刷新按钮重新加载页面;工具栏上的 × 关闭面板。DevTools 主菜单(⋮)里有“停靠位置”:底部(默认)、右侧、左侧,或取消停靠,变成页面上方的浮动窗口,拖动横条移动、拖动右下角调整大小。外壳模式下页面会让出空间,和 Chrome 一样;普通模式下面板盖在页面上。停靠位置和尺寸会记在 localStorage 里;尺寸记的是占视口的百分比(`dvh`/`dvw`),换屏幕或横竖屏切换后依然合适。唯一的属性 `devtools-frontend-url` 是可选的,不写时沿用探针的 `data-devtools-frontend-url`。其余状态都由库维护:探针的参数决定组件的配置,打开和关闭由用户操作;组件没有自己的方法。页面可以用 `devtools-anywhere::part(toggler)` 修改 F12 按钮的样式,外壳模式下同样有效:此时按钮在页面 iframe 里,由页面 `<html>` 下一个只承载按钮的 `<devtools-anywhere>` 显示,点击开关外壳的 DevTools(只有 iframe 里的页面没有探针时,例如其他源的页面,外壳才显示自己的按钮)。探针挂载的组件不把它的 iframe 暴露为部件。组件要固定挂在一个位置,在 DOM 中移动它会导致 iframe 重新加载。 +点击右下角的 F12 悬浮按钮打开面板;拖动面板的横条调整大小(外壳模式下用 DevTools 自己的分隔条,见[外壳模式](#2-外壳模式原地改写))。DevTools 设置按钮左边,锁形按钮开关外壳模式(开启前有对话框说明,开启后顶部有提示条),刷新按钮重新加载页面;工具栏上的 × 关闭面板。DevTools 主菜单(⋮)里有“停靠位置”:底部(默认)、右侧、左侧,或取消停靠,变成页面上方的浮动窗口,拖动横条移动、拖动右下角调整大小。外壳模式下页面会让出空间,和 Chrome 一样;普通模式下面板盖在页面上。停靠位置和尺寸会记在 localStorage 里;尺寸记的是占视口的百分比(`dvh`/`dvw`),换屏幕或横竖屏切换后依然合适。唯一的属性 `devtools-frontend-url` 是可选的,不写时沿用探针的 `data-devtools-frontend-url`。其余状态都由库维护:探针的参数决定组件的配置,打开和关闭由用户操作;组件没有自己的方法。页面可以用 `devtools-anywhere::part(toggler)` 修改 F12 按钮的样式,外壳模式下同样有效:此时按钮在页面 iframe 里,由页面 `<html>` 下一个只承载按钮的 `<devtools-anywhere>` 显示,点击开关外壳的 DevTools(只有 iframe 里的页面没有探针时,例如其他源的页面,外壳才显示自己的按钮)。探针挂载的组件不把它的 iframe 暴露为部件。组件要固定挂在一个位置,在 DOM 中移动它会导致 iframe 重新加载。 页面也可以自己放置 `<devtools-anywhere>`,作为显示 DevTools 的一个区域:它是页面文档流里的块级盒子(`display: block`),尺寸由页面的 CSS 决定,一连接就加载 DevTools。页面用 CSS 或 `hidden` 控制它的显示和隐藏;隐藏时 DevTools 不会卸载。它没有 F12 按钮,不能停靠或拖拽调整大小,也不会进入外壳模式(DevTools 里没有入口);在外壳里的页面中照常工作。它只有 `devtools-frontend-url` 一个属性,其他属性一律忽略。里面的 DevTools iframe 可以用 `devtools-anywhere::part(iframe)` 设置样式。 From b79026676dbb08795a47415732b6ad89a5cb2b36 Mon Sep 17 00:00:00 2001 From: jerrychan7 <czy-ken@163.com> Date: Sun, 1 Nov 2026 20:13:36 +0800 Subject: [PATCH 08/26] ci(release): require npm 11.21.0, which npm dist-tag needs under trusted publishing npm dist-tag works with trusted publishing once each package's trusted publisher allows it, from npm 11.21.0. The publish job installed npm@11, which happens to resolve to a later release today; it now asks for ^11.21.0 so that the Chrome milestone tag cannot fall back to an older npm that has no credentials for it. --- .github/workflows/release.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9bca866..fb90b58 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -142,8 +142,9 @@ jobs: registry-url: https://registry.npmjs.org - name: Update npm - # Trusted publishing needs npm 11.5.1 or later; Node 22 comes with npm 10. - run: npm install -g npm@11 + # Trusted publishing needs npm 11.5.1 or later, and 11.21.0 for npm dist-tag; Node 22 comes + # with npm 10. + run: npm install -g npm@^11.21.0 - name: Publish to npm run: | From c514fa29076383f89a0a9b825521b3e25778b885 Mon Sep 17 00:00:00 2001 From: jerrychan7 <czy-ken@163.com> Date: Sun, 1 Nov 2026 20:13:37 +0800 Subject: [PATCH 09/26] fix(shell): clear device mode with the frontend and tidy the shell layout - A frontend loaded in place of another (its URL changed) no longer inherits the previous one's device metrics: the page iframe is no longer sized, scaled and clipped for a device mode the new frontend never turned on. Chrome clears the override with the frontend too. - The shell layout is disposed when its element leaves the shell: its resize listener, touch handle and device mode page are removed, so that an element replaced and connected again does not stack them up. - A toolbox request whose URL does not parse is ignored instead of throwing in the message handler. - The frontend looks for device mode's page for its latest request only, so undocking twice in a row no longer hands the page over twice; it waits up to 60 seconds instead of 10 for a slow load, and says in the console when the page never loaded. --- packages/devtools-anywhere/src/element.ts | 14 ++++++++++++-- packages/devtools-anywhere/src/shell-layout.ts | 11 ++++++++++- .../src/anywhere_app/anywhere_app.ts | 13 ++++++++++--- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/packages/devtools-anywhere/src/element.ts b/packages/devtools-anywhere/src/element.ts index 8df5489..865d2b3 100644 --- a/packages/devtools-anywhere/src/element.ts +++ b/packages/devtools-anywhere/src/element.ts @@ -370,7 +370,12 @@ export class DevtoolsAnywhereElement extends HTMLElement { this.shellLayout?.splitSize(request.size); } else if (request.type === 'toolbox') { // Only a page of the frontend's own origin. - const url = new URL(request.url, this.loadedUrl); + let url: URL; + try { + url = new URL(request.url, this.loadedUrl); + } catch { + return; + } if (url.origin === this.frontendOrigin) this.shellLayout?.showToolbox(url.href); } }; @@ -671,6 +676,8 @@ export class DevtoolsAnywhereElement extends HTMLElement { this.disconnect?.(); this.disconnect = null; this.loadedUrl = ''; + this.shellLayout?.dispose(); + this.shellLayout = null; window.removeEventListener('message', this.onFrontendRequest); window.removeEventListener('pageshow', this.onShellPageshow); // The togglers without a value may point to another element now. @@ -787,8 +794,11 @@ export class DevtoolsAnywhereElement extends HTMLElement { this.disconnect?.(); this.frontendOrigin = url.origin; if (this.session) this.disconnect = connectFrontend(this.session, this.frame, url.origin); - else if (page) + else if (page) { + // Device mode was the previous frontend's: Chrome clears it with the frontend. + this.shellLayout?.setMetrics(null); this.disconnect = relayShell(page, this.frame, url.origin, metrics => this.shellLayout?.setMetrics(metrics)); + } // Whether shell mode is available (it needs the bundle URL) and whether it is on. const shell = isShellElement(this); const hash = new URLSearchParams({ diff --git a/packages/devtools-anywhere/src/shell-layout.ts b/packages/devtools-anywhere/src/shell-layout.ts index 7da1f7d..7ba4961 100644 --- a/packages/devtools-anywhere/src/shell-layout.ts +++ b/packages/devtools-anywhere/src/shell-layout.ts @@ -58,7 +58,16 @@ export class ShellLayout { this.handle.style.cssText = 'position:fixed;z-index:2;display:none;touch-action:none'; document.body.append(this.handle); this.setupHandle(); - addEventListener('resize', () => this.render()); + addEventListener('resize', this.onResize); + } + + private readonly onResize = () => this.render(); + + /** The element left the shell: stops listening and removes what this layout added. */ + dispose() { + removeEventListener('resize', this.onResize); + this.handle.remove(); + this.removeToolbox(); } /** DevTools was shown or hidden, or docked elsewhere. */ diff --git a/packages/devtools-frontend/src/anywhere_app/anywhere_app.ts b/packages/devtools-frontend/src/anywhere_app/anywhere_app.ts index 5065bd4..08ed821 100644 --- a/packages/devtools-frontend/src/anywhere_app/anywhere_app.ts +++ b/packages/devtools-frontend/src/anywhere_app/anywhere_app.ts @@ -194,20 +194,27 @@ function loadedToolbox(): Document|null { } return null; } -function handOverToolbox(deadline: number): void { +/** The latest request for the toolbox: an earlier one stops looking for it. */ +let toolboxRequest = 0; +function handOverToolbox(request: number, deadline: number): void { + if (request !== toolboxRequest) { + return; + } const toolbox = loadedToolbox(); if (toolbox) { useDefaultTheme(toolbox); Reflect.get(globalThis, 'Emulation')?.AdvancedApp?.instance().deviceModeEmulationFrameLoaded(toolbox); } else if (Date.now() < deadline) { - setTimeout(() => handOverToolbox(deadline), 100); + setTimeout(() => handOverToolbox(request, deadline), 100); + } else { + console.warn(`Device mode's page (${TOOLBOX_PAGE}) did not load in the shell`); } } const open = window.open.bind(window); window.open = (url?: string|URL, target?: string, features?: string) => { if (!remote && url !== undefined && isToolbox(String(url))) { notifyEmbedder({type: 'toolbox', url: new URL(url, location.href).href}); - handOverToolbox(Date.now() + 10000); + handOverToolbox(++toolboxRequest, Date.now() + 60000); return null; } return open(url, target, features); From 07ec6655db55510eeb084e6248645b0702539564 Mon Sep 17 00:00:00 2001 From: jerrychan7 <czy-ken@163.com> Date: Sun, 1 Nov 2026 20:13:45 +0800 Subject: [PATCH 10/26] fix(cdp-agent): retry a screenshot smaller only when drawing fails Page.captureScreenshot halved the image and tried again on any error, until it was small enough to report the failure, so a snapDOM that failed to load was loaded again several times before the error came back. Failures before drawing (loading snapDOM, reading the page) are now a CaptureError and reported at once; only a failure to draw the canvas is tried again at half the size. --- .../src/cdp-agent/domains/page/render.ts | 43 +++++++++++++------ .../src/cdp-agent/domains/page/screenshot.ts | 8 ++-- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/packages/devtools-anywhere/src/cdp-agent/domains/page/render.ts b/packages/devtools-anywhere/src/cdp-agent/domains/page/render.ts index 64142d6..35652e1 100644 --- a/packages/devtools-anywhere/src/cdp-agent/domains/page/render.ts +++ b/packages/devtools-anywhere/src/cdp-agent/domains/page/render.ts @@ -37,24 +37,39 @@ function background() { return '#fff'; } +/** + * A failure before drawing: loading snapDOM or reading the page. Drawing at a smaller size would + * not help, unlike a failure to draw. + */ +export class CaptureError extends Error { + constructor(cause: unknown) { + super(cause instanceof Error ? cause.message : String(cause), { cause }); + } +} + /** A Render on snapDOM, loaded on first use. */ export function createRender(load: SnapdomLoader): Render { let snapdom: Promise<Snapdom> | null = null; return async (clip, scale, dpr) => { - // A failed load is not kept, so that the next render loads snapDOM again. - const capture = await (snapdom ??= load().then( - m => m.snapdom, - (e: unknown) => { - snapdom = null; - throw e; - } - )); - const result = await capture(document.documentElement, { - clip, - exclude: [isOwn], - excludeMode: 'hide', - backgroundColor: background(), - }); + let result; + try { + // A failed load is not kept, so that the next render loads snapDOM again. + const capture = await (snapdom ??= load().then( + m => m.snapdom, + (e: unknown) => { + snapdom = null; + throw e; + } + )); + result = await capture(document.documentElement, { + clip, + exclude: [isOwn], + excludeMode: 'hide', + backgroundColor: background(), + }); + } catch (e) { + throw new CaptureError(e); + } return result.toCanvas({ scale, dpr }); }; } diff --git a/packages/devtools-anywhere/src/cdp-agent/domains/page/screenshot.ts b/packages/devtools-anywhere/src/cdp-agent/domains/page/screenshot.ts index c62431e..0b5af21 100644 --- a/packages/devtools-anywhere/src/cdp-agent/domains/page/screenshot.ts +++ b/packages/devtools-anywhere/src/cdp-agent/domains/page/screenshot.ts @@ -1,13 +1,14 @@ import type { Protocol } from 'devtools-protocol'; import type { Context } from '../../core/context'; import { CdpError } from '../../core/session'; -import { encode, type Render } from './render'; +import { CaptureError, encode, type Render } from './render'; /** * A canvas area small enough for any browser, older iOS Safari's limit (4096 × 4096). * snapDOM already scales an image past the usual canvas limits down to fit (32767 pixels a * side, 16384 on Safari, 16384 × 16384 in area); should drawing still fail, the screenshot is - * halved and tried again, and once it is no larger than this the failure is reported. + * halved and tried again, and once it is no larger than this the failure is reported. A failure + * before drawing (CaptureError) is reported at once. */ const SAFE_PIXELS = 4096 * 4096; @@ -46,7 +47,8 @@ export function registerScreenshot({ session }: Context, render: Render | undefi if (data) return { data, approximate: true }; if (safe) throw new CdpError('Unable to capture screenshot'); } catch (e) { - if (safe) throw e; + // Only a failure to draw is worth trying again smaller. + if (safe || e instanceof CaptureError) throw e; } } }, From 82df152b123eccd6acd6e17d8e7c97b7a6c92f03 Mon Sep 17 00:00:00 2001 From: jerrychan7 <czy-ken@163.com> Date: Sun, 1 Nov 2026 20:13:46 +0800 Subject: [PATCH 11/26] fix(cdp-agent): report the deprecated layout metrics in device pixels as Chrome does Page.getLayoutMetrics returned CSS pixels in the deprecated layoutViewport, visualViewport and contentSize as well as in the css* fields, which only matches Chrome on a screen with a scale factor of 1. Measured in Chrome on a screen scaled by 2: the layout viewport's position and size, the visual viewport's size and the content size are in device pixels; the visual viewport's position stays in CSS pixels. Ours now multiplies the same fields by devicePixelRatio. The conformance tests compare both on a screen scaled by 2, for which launch() can now leave pages Chrome's own viewport (defaultViewport: null) rather than Puppeteer's 800 x 600 at a scale of 1. --- .../src/cdp-agent/domains/page/page.ts | 19 +++++++++++++++---- tests/support/browser.ts | 16 +++++++++++++--- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/packages/devtools-anywhere/src/cdp-agent/domains/page/page.ts b/packages/devtools-anywhere/src/cdp-agent/domains/page/page.ts index 27a8a6a..8bd8e4d 100644 --- a/packages/devtools-anywhere/src/cdp-agent/domains/page/page.ts +++ b/packages/devtools-anywhere/src/cdp-agent/domains/page/page.ts @@ -63,7 +63,11 @@ export function registerPage({ session, probe }: Context) { return { url: link.href, errors: [], data: await res.text(), manifest: {} }; }, - /** Chrome reports CSS pixels in both sets of fields while the page is not zoomed. */ + /** + * The css* fields are in CSS pixels; the deprecated ones are mostly in device pixels, as + * Chrome reports them: times the screen's scale factor, devicePixelRatio as page scripts + * see it. + */ getLayoutMetrics(): Protocol.Page.GetLayoutMetricsResponse { const root = document.documentElement; const layout = { pageX: scrollX, pageY: scrollY, clientWidth: root.clientWidth, clientHeight: root.clientHeight }; @@ -79,10 +83,17 @@ export function registerPage({ session, probe }: Context) { zoom: 1, }; const content = { x: 0, y: 0, width: root.scrollWidth, height: root.scrollHeight }; + const dpr = devicePixelRatio; return { - layoutViewport: layout, - visualViewport: visual, - contentSize: content, + layoutViewport: { + pageX: layout.pageX * dpr, + pageY: layout.pageY * dpr, + clientWidth: layout.clientWidth * dpr, + clientHeight: layout.clientHeight * dpr, + }, + // Chrome leaves the visual viewport's position in CSS pixels. + visualViewport: { ...visual, clientWidth: visual.clientWidth * dpr, clientHeight: visual.clientHeight * dpr }, + contentSize: { ...content, width: content.width * dpr, height: content.height * dpr }, cssLayoutViewport: layout, cssVisualViewport: visual, cssContentSize: content, diff --git a/tests/support/browser.ts b/tests/support/browser.ts index 037487d..4cce5b5 100644 --- a/tests/support/browser.ts +++ b/tests/support/browser.ts @@ -10,13 +10,23 @@ export interface Env { close(): Promise<void>; } -/** `args` are extra Chrome flags, e.g. to turn on an experimental feature. */ -export async function launch(args: string[] = []): Promise<Env> { +/** + * `args` are extra Chrome flags, e.g. to turn on an experimental feature. `defaultViewport: null` + * leaves pages the window's size and the screen's scale factor, rather than Puppeteer's 800 × 600 + * at 1. + */ +export async function launch(args: string[] = [], options: { defaultViewport?: null } = {}): Promise<Env> { const server = await startServer(0); let browser: Browser; try { // A call that hangs fails after 30s rather than Puppeteer's default 180s. - browser = await puppeteer.launch({ executablePath: CHROME, headless: true, args, protocolTimeout: 30_000 }); + browser = await puppeteer.launch({ + executablePath: CHROME, + headless: true, + args, + protocolTimeout: 30_000, + ...options, + }); } catch (e) { // Otherwise the listening server keeps the worker alive and hides the launch error. server.close(); From 342f887293bcfa0e81624b86c22431d66f00b091 Mon Sep 17 00:00:00 2001 From: jerrychan7 <czy-ken@163.com> Date: Sun, 1 Nov 2026 20:13:47 +0800 Subject: [PATCH 12/26] test(cdp-agent): cover the screenshot retry and the layout metrics on a scaled screen - captureScreenshot with a stand-in snapDOM that fails to draw until a quarter of the size: it draws at 1, 0.5 and 0.25; and with a snapDOM that fails to load: it loads once and reports that error. - getLayoutMetrics matches Chrome on a screen with a scale factor of 2, scrolled. --- .../domains/page/page.conformance.test.ts | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/packages/devtools-anywhere/src/cdp-agent/domains/page/page.conformance.test.ts b/packages/devtools-anywhere/src/cdp-agent/domains/page/page.conformance.test.ts index 2977cd7..8eb4699 100644 --- a/packages/devtools-anywhere/src/cdp-agent/domains/page/page.conformance.test.ts +++ b/packages/devtools-anywhere/src/cdp-agent/domains/page/page.conformance.test.ts @@ -287,3 +287,59 @@ test('getLayoutMetrics matches Chrome on a scrolled page', async () => { await s.page.close(); } }); + +test('getLayoutMetrics matches Chrome on a screen with a scale factor of 2', async () => { + // The deprecated fields are in device pixels, the css* ones in CSS pixels. + const hiDpi = await launch(['--force-device-scale-factor=2', '--window-size=800,600'], { defaultViewport: null }); + try { + const s = await openPage(hiDpi, []); + await s.page.evaluate(() => { + document.body.style.cssText = 'margin:0;width:1500px;height:3000px'; + scrollTo(30, 500); + }); + assert.equal(await s.page.evaluate(() => devicePixelRatio), 2); + const [ours, chrome] = await Promise.all([ + s.ours.send('Page.getLayoutMetrics'), + s.chrome.send('Page.getLayoutMetrics'), + ]); + assert.deepEqual(ours, chrome); + } finally { + await hiDpi.close(); + } +}); + +test('captureScreenshot draws again at half the size when drawing fails, not when loading does', async () => { + const result = await t.page.evaluate(async () => { + const scales: number[] = []; + let loads = 0; + const drawing = window.DevtoolsAnywhere.createCdpSession({ + loadSnapdom: async () => ({ + snapdom: async () => ({ + async toCanvas({ scale }) { + scales.push(scale); + // Too large to draw until a quarter of the size. + if (scale > 0.25) throw new Error('canvas too large'); + return Object.assign(document.createElement('canvas'), { width: 10, height: 10 }); + }, + }), + }), + }); + await drawing.call('Page.captureScreenshot', { clip: { x: 0, y: 0, width: 20000, height: 20000, scale: 1 } }); + const loading = window.DevtoolsAnywhere.createCdpSession({ + loadSnapdom: () => { + loads++; + return Promise.reject(new Error('offline')); + }, + }); + const error = await loading + .call('Page.captureScreenshot', { clip: { x: 0, y: 0, width: 20000, height: 20000, scale: 1 } }) + .then( + () => '', + (e: unknown) => String(e) + ); + return { scales, loads, error }; + }); + assert.deepEqual(result.scales, [1, 0.5, 0.25]); + assert.equal(result.loads, 1); + assert.match(result.error, /offline/); +}); From 6e670320b95d42dd6a7409b7ad9162219ba1562b Mon Sep 17 00:00:00 2001 From: jerrychan7 <czy-ken@163.com> Date: Sun, 1 Nov 2026 20:13:47 +0800 Subject: [PATCH 13/26] test(e2e): make the device mode tests check what they claim - Picking a device that is not in the list now fails: the undocked test picked "iPhone SE" again in the toolbox (the page kept its size from the previous test, so a broken toolbox toolbar would have passed); it now picks "iPhone 16", which only that toolbar can set. - The stand-ins for convertToBlob, anchors' click and matchMedia are restored once used. - waitFor says what it waited for when it times out. - New: docked to the right, the page goes where the frontend says; leaving device mode gives the page the room the frontend lays out for it. - The file says that its tests run in order, from where the previous one left the page. --- tests/e2e/device-mode.test.ts | 78 ++++++++++++++++++++++++++++------- 1 file changed, 63 insertions(+), 15 deletions(-) diff --git a/tests/e2e/device-mode.test.ts b/tests/e2e/device-mode.test.ts index 226ce88..d197803 100644 --- a/tests/e2e/device-mode.test.ts +++ b/tests/e2e/device-mode.test.ts @@ -1,6 +1,7 @@ // Device mode in shell mode, with the DevTools frontend built from the official sources: the // frontend lays out the page and itself as in Chrome (shell-layout.ts), and device mode sizes the -// page. Skipped when the frontend has not been built. +// page. Skipped when the frontend has not been built. The tests run in order, each from where the +// previous one left the page. import { existsSync } from 'node:fs'; import { after, before, test } from 'node:test'; import assert from 'node:assert/strict'; @@ -26,19 +27,19 @@ before(async () => { }); after(() => env?.close()); -async function waitFor<T>(get: () => T | Promise<T>, timeout = 15000): Promise<NonNullable<T>> { +async function waitFor<T>(get: () => T | Promise<T>, what = 'condition', timeout = 15000): Promise<NonNullable<T>> { const start = Date.now(); for (;;) { const value = await Promise.resolve() .then(get) .catch(() => null); if (value) return value; - if (Date.now() - start > timeout) throw new Error('timed out'); + if (Date.now() - start > timeout) throw new Error(`timed out waiting for ${what}`); await new Promise(r => setTimeout(r, 100)); } } -const frontend = () => waitFor(() => page.frames().find(f => f.url().includes('anywhere_app.html'))); +const frontend = () => waitFor(() => page.frames().find(f => f.url().includes('anywhere_app.html')), 'the frontend'); /** Runs an action of the frontend's (UI.ActionRegistry), as its toolbar button or menu item would. */ async function runAction(id: string) { @@ -81,7 +82,9 @@ async function pickDevice(frame: Frame, name: string) { }; const select = find(document); if (!select) throw new Error('no device select'); - select.selectedIndex = [...select.options].findIndex(o => o.text === name); + const index = [...select.options].findIndex(o => o.text === name); + if (index < 0) throw new Error(`no device ${name}`); + select.selectedIndex = index; select.dispatchEvent(new Event('change', { bubbles: true })); }, name); } @@ -168,8 +171,13 @@ test('device mode sizes the page as the device, scaled to fit, and takes its scr // the image it draws, and the link it clicks to download it (not followed here). await fe.evaluate(() => { const saved: { name?: string; width?: number; height?: number } = {}; - Reflect.set(window, '__saved', saved); const convert = OffscreenCanvas.prototype.convertToBlob; + const click = HTMLAnchorElement.prototype.click; + Reflect.set(window, '__saved', saved); + Reflect.set(window, '__restore', () => { + OffscreenCanvas.prototype.convertToBlob = convert; + HTMLAnchorElement.prototype.click = click; + }); OffscreenCanvas.prototype.convertToBlob = function (this: OffscreenCanvas, options) { Object.assign(saved, { width: this.width, height: this.height }); return convert.call(this, options); @@ -179,12 +187,18 @@ test('device mode sizes the page as the device, scaled to fit, and takes its scr }; }); await runAction('emulation.capture-screenshot'); - const saved = await waitFor(() => - fe.evaluate(() => { - const value: { name?: string; width?: number; height?: number } = Reflect.get(window, '__saved'); - return value.name ? { name: value.name, width: value.width ?? 0, height: value.height ?? 0 } : null; - }) + const saved = await waitFor( + () => + fe.evaluate(() => { + const value: { name?: string; width?: number; height?: number } = Reflect.get(window, '__saved'); + return value.name ? { name: value.name, width: value.width ?? 0, height: value.height ?? 0 } : null; + }), + 'the screenshot' ); + await fe.evaluate(() => { + const restore: unknown = Reflect.get(window, '__restore'); + if (typeof restore === 'function') restore(); + }); assert.match(saved.name, /\(iPhone SE\)\.png$/); assert.equal(saved.width, 375); near(saved.height, 667, 'screenshot height'); @@ -211,21 +225,50 @@ test('undocked, device mode draws into its own page under the floating panel', { await dock('undocked'); const toolbox = await waitFor(() => page.frames().find(f => f.url().includes('device_mode_emulation_frame.html'))); // Its toolbar is there, and the page goes where it says. - await waitFor(() => pickDevice(toolbox, 'iPhone SE').then(() => true)); + // Another device than before, so that the page's size comes from this toolbar. + await waitFor(() => pickDevice(toolbox, 'iPhone 16').then(() => true), "the toolbox's toolbar"); const where = await waitFor(async () => { const [state, spot] = [await pageState(), await placeholder(toolbox).catch(() => null)]; - return spot && state.viewport.width === 375 && Math.abs(state.rect.left - spot.left) <= 2 ? { state, spot } : null; - }); + return spot && state.viewport.width === 393 && Math.abs(state.rect.left - spot.left) <= 2 ? { state, spot } : null; + }, 'the page in the toolbox'); near(where.state.rect.width, where.spot.width, 'width'); await dock('bottom'); await waitFor(async () => !page.frames().some(f => f.url().includes('device_mode_emulation_frame.html'))); await waitFor(async () => (await pageState()).rect.height < 800); }); +test('docked to the right, the page goes where the frontend says', { skip }, async () => { + await dock('right'); + const fe = await frontend(); + // The page left of DevTools, where the placeholder is. + await waitFor(async () => { + const [state, spot] = [await pageState(), await placeholder(fe)]; + return ( + state.viewport.width === 393 && + spot.left < 500 && + Math.abs(state.rect.left - spot.left) <= 2 && + Math.abs(state.rect.top - spot.top) <= 2 + ); + }, 'the page docked right'); + await dock('bottom'); +}); + +test('leaving device mode gives the page the room the frontend lays out for it', { skip }, async () => { + await runAction('emulation.toggle-device-mode'); + const fe = await frontend(); + const where = await waitFor(async () => { + const [state, spot] = [await pageState(), await placeholder(fe)]; + return state.viewport.width !== 393 && Math.abs(state.rect.width - spot.width) <= 2 ? { state, spot } : null; + }, 'the page at its own size'); + near(where.state.rect.height, where.spot.height, 'height'); + assert.equal(where.state.viewport.width, Math.round(where.state.rect.width)); +}); + test('on a touch screen, a handle on the edge between the page and DevTools resizes them', { skip }, async () => { // A touch screen, as the shell sees it; a resize lays the shell out again. await page.evaluate(() => { const matchMedia = window.matchMedia.bind(window); + Reflect.set(window, '__matchMedia', window.matchMedia); window.matchMedia = query => (query === '(pointer: coarse)' ? matchMedia('all') : matchMedia(query)); window.dispatchEvent(new Event('resize')); }); @@ -239,7 +282,7 @@ test('on a touch screen, a handle on the edge between the page and DevTools resi const { left, top, width, height } = el.getBoundingClientRect(); return { x: left + width / 2, y: top + height / 2 }; }); - const start = await waitFor(handle); + const start = await waitFor(handle, 'the touch handle'); await page.mouse.move(start.x, start.y); await page.mouse.down(); await page.mouse.move(start.x, start.y - 150, { steps: 5 }); @@ -249,6 +292,11 @@ test('on a touch screen, a handle on the edge between the page and DevTools resi await waitFor(async () => { const now = await handle(); return now && now.y < start.y - 100; + }, 'the handle to move up'); + await page.evaluate(() => { + const original: unknown = Reflect.get(window, '__matchMedia'); + if (original instanceof Function) window.matchMedia = query => Reflect.apply(original, window, [query]); + window.dispatchEvent(new Event('resize')); }); }); From bd2ae421fa3d85e0e78ca0247e6cc7c6356fb859 Mon Sep 17 00:00:00 2001 From: jerrychan7 <czy-ken@163.com> Date: Sun, 1 Nov 2026 21:08:00 +0800 Subject: [PATCH 14/26] docs: describe the branch model and the development and release flow DEV.md and DEV.zh-CN.md now say how work reaches main and how releases are cut from it: - main is the only long-lived branch, changed through pull requests with CI passing; there is no develop branch, since merging releases nothing and main itself holds what is merged and not yet released. - Short-lived branches are named after the commit type (feat/..., fix/..., ...), and a change users notice adds its changelog line in the same pull request. - A release is prepared on release/x.y.z through a pull request, then tagged on main; releases are cut when there is something worth releasing, not on every merge. A new feature is a minor. - An older minor is patched on release/x.y, branched from its tag, with fixes merged into main first and cherry-picked; what that still needs (CI on release/** and publishing without moving latest) is listed as not set up yet. - ci is listed among the commit types. - The API report paragraph no longer says the repository has no CI: GitHub Actions sets CI=true, so CI fails on a report left uncommitted. --- DEV.md | 39 ++++++++++++++++++++++++++++++--------- DEV.zh-CN.md | 39 ++++++++++++++++++++++++++++++--------- 2 files changed, 60 insertions(+), 18 deletions(-) diff --git a/DEV.md b/DEV.md index 0356247..94ec6c9 100644 --- a/DEV.md +++ b/DEV.md @@ -196,7 +196,7 @@ pnpm test # with the frontend built, so the real-frontend tests run The pre-commit hook (husky + lint-staged) runs on the staged files only: it formats them with oxfmt and adds the result to the commit, then lints them with oxlint and stops the commit on any error; it also runs `scripts/check-contracts.ts` (see "Where contracts live"), and two size guards on what the commit adds or changes: `scripts/check-file-size.ts` warns on a source file (`.ts`, `.js` and their variants) past 1100 lines and rejects the commit past 1500; `scripts/check-dir-size.ts` warns on a directory the commit adds files to that holds more than 20 files directly and rejects it past 30. After a warning, make the next commit the one that splits the file or sorts the directory into subdirectories by concern. Both skip the code that goes into the devtools-frontend checkout, which follows Chromium's style (`packages/devtools-frontend/src` is copied in, `patches` are applied to it) and `experiments/`. `pnpm lint` runs both on every tracked file (`--all`), so a commit that skipped the hook is caught there. `git commit --no-verify` skips the hook once. Lint rules: oxlint's correctness rules, no `any`, no non-null assertions (`!`). Style: 2-space indentation, semicolons, single quotes, 120 columns. `packages/devtools-frontend/src` follows Chromium's style instead and is not linted or formatted. -The build rolls each package's type declarations up into one file per entry point (`dist/index.d.ts`) with API Extractor, and writes the package's API to `packages/<package>/etc/`: `*.public.api.md` for the public API (what is promised), `*.api.md` for everything exported, internal included (`scripts/api-extract.ts`, configured by `api-extractor.base.json`; it copies `declare global` blocks into the rolled-up file, which API Extractor leaves out). `devtools-anywhere` has no runtime entry: its `dist/index.d.ts` is types only, and its scripts are built as IIFEs. Commit a changed report with the change that caused it: it is how a change to the public API shows in review. With `CI=true` (for a CI, which the repository does not have yet) a report that no longer matches fails the build. Every export of an entry point needs a release tag: `@public` only for what the README documents (remote-server's `startRemoteServer` / `createRemoteHandler`, the Vite plugin), `@internal` for everything else, which is used only between this repository's packages, built and released together, and may change at any time; when in doubt, `@internal`, and export nothing an entry point does not need. Doc comments are TSDoc: put package names such as `@devtools-anywhere/...` in backticks and code examples in fenced blocks. +The build rolls each package's type declarations up into one file per entry point (`dist/index.d.ts`) with API Extractor, and writes the package's API to `packages/<package>/etc/`: `*.public.api.md` for the public API (what is promised), `*.api.md` for everything exported, internal included (`scripts/api-extract.ts`, configured by `api-extractor.base.json`; it copies `declare global` blocks into the rolled-up file, which API Extractor leaves out). `devtools-anywhere` has no runtime entry: its `dist/index.d.ts` is types only, and its scripts are built as IIFEs. Commit a changed report with the change that caused it: it is how a change to the public API shows in review. With `CI=true`, which GitHub Actions sets, a report that no longer matches fails the build, so CI catches a report left uncommitted. Every export of an entry point needs a release tag: `@public` only for what the README documents (remote-server's `startRemoteServer` / `createRemoteHandler`, the Vite plugin), `@internal` for everything else, which is used only between this repository's packages, built and released together, and may change at any time; when in doubt, `@internal`, and export nothing an entry point does not need. Doc comments are TSDoc: put package names such as `@devtools-anywhere/...` in backticks and code examples in fenced blocks. Also update both `README.md` and `README.zh-CN.md` (and this guide in both languages) when behavior, usage or limitations change, and add a line for users under `Unreleased` in both `CHANGELOG.md` and `CHANGELOG.zh-CN.md` (moving the frontend to a new Chrome milestone also changes its `Based on Chrome` line). @@ -205,25 +205,46 @@ When a dependency that ends up in a build output is added or upgraded, or code i ### Commits - One logical step per commit. -- Message format: `type(scope): summary`, then a blank line and an English body explaining what changed and why. Types: `feat`, `fix`, `docs`, `test`, `build`, `refactor`, `chore`. Scopes are package or area names, e.g. `cdp-agent`, `network`, `devtools-frontend`, `service-worker`, `readme`. +- Message format: `type(scope): summary`, then a blank line and an English body explaining what changed and why. Types: `feat`, `fix`, `docs`, `test`, `build`, `ci`, `refactor`, `chore`. Scopes are package or area names, e.g. `cdp-agent`, `network`, `devtools-frontend`, `service-worker`, `readme`. -### Pull requests +### Branches and pull requests + +`main` is the only long-lived branch: it is where all work lands and what releases are tagged from, and it should always be releasable. There is no `develop` branch: merging into `main` does not release anything (only a pushed `vx.y.z` tag does, see "Releasing to npm"), so `main` itself holds what is merged and not yet released. + +| Branch | For | Rules | +| --- | --- | --- | +| `main` | All development; releases are tagged on it | Changed through pull requests only, CI must pass, no force pushes | +| `feat/…`, `fix/…`, `docs/…`, `ci/…`, … | One change each, short-lived; the prefix is the commit type | Branched from `main`, merged back through a pull request and deleted | +| `release/x.y.z` | The pull request that prepares release x.y.z (version and changelog) | Merged into `main`, then the tag goes on `main` | +| `release/x.y` | Patches to an older minor, only when needed (see "Patching an older release") | Branched from the tag `vx.y.0`, kept while that minor is patched | + +A fix is not a branch type of its own: it goes through a `fix/…` branch into `main` like any change, and is released as a patch. CI (`.github/workflows/ci.yml`) runs on every pull request and every push to `main`: lint, typecheck, the frontend build and all the tests. ```bash -git switch -c my-change +git switch -c feat/my-change main # … commits … -git push -u origin my-change +git push -u origin feat/my-change gh pr create --fill ``` -Describe in the PR what was verified, in particular what was checked by hand in a browser and what was not (Safari, mobile devices). +Describe in the PR what was verified, in particular what was checked by hand in a browser and what was not (Safari, mobile devices). A change users notice also adds its line under `Unreleased` in both changelogs in the same pull request, so that the changelog is ready when a release is cut. ### Releasing to npm All packages share one version number and are released together, by `.github/workflows/release.yml` when a tag `vx.y.z` is pushed. npm trusts that workflow (trusted publishing, set on each package on npmjs.com for this repository, `release.yml` and the `npm` environment), so it publishes without a stored token and with provenance; renaming the file or the environment breaks that. Approving the publish takes a reviewer of the repository's `npm` environment. Each package's trusted publisher must allow `npm publish` and `npm dist-tag`, not only `npm stage publish`. -Versions are semver for the library itself, starting at `0.1.0`: a fix is a patch, and moving the frontend to a new Chrome milestone is a minor (its UI changes); before 1.0 a minor may also break. What is promised is what the README documents (`@public`). The Chrome version is not in the version number, where npm would drop it (`+chrome156`) or take it for a prerelease (`-chrome156`): each published package's `package.json` has it as `chrome`, which `update` writes, and each release says it first (`Based on Chrome 156`). Each release also gets the dist-tag `chrome-<milestone>`, so that `npm i devtools-anywhere@chrome-156` installs the last release on that Chrome. +Versions are semver for the library itself, starting at `0.1.0`: a fix is a patch; a new feature, or moving the frontend to a new Chrome milestone (its UI changes), is a minor; before 1.0 a minor may also break. Releases are cut when there is something worth releasing (features that add up to a minor, a fix users are waiting for, a new Chrome milestone), not on every merge. What is promised is what the README documents (`@public`). The Chrome version is not in the version number, where npm would drop it (`+chrome156`) or take it for a prerelease (`-chrome156`): each published package's `package.json` has it as `chrome`, which `update` writes, and each release says it first (`Based on Chrome 156`). Each release also gets the dist-tag `chrome-<milestone>`, so that `npm i devtools-anywhere@chrome-156` installs the last release on that Chrome. -1. Set the same `version` in every `packages/*/package.json`, move the `Unreleased` notes in `CHANGELOG.md` and `CHANGELOG.zh-CN.md` under `## x.y.z`, and commit (`build: release x.y.z`). `node scripts/release.ts check x.y.z` checks both. -2. Tag and push: `git tag vx.y.z && git push origin main vx.y.z`. +1. On a branch `release/x.y.z` from `main`, set the same `version` in every `packages/*/package.json`, move the `Unreleased` notes in `CHANGELOG.md` and `CHANGELOG.zh-CN.md` under `## x.y.z`, and commit (`build: release x.y.z`). `node scripts/release.ts check x.y.z` checks both. Open a pull request and merge it once CI passes. +2. Tag the merged commit on `main` and push the tag: `git switch main && git pull && git tag vx.y.z && git push origin vx.y.z`. 3. The workflow checks the version, builds the frontend, runs the checks and tests as CI does and packs the packages (pnpm replaces `workspace:*` with the real version). Then approve its `publish` job in the Actions tab: it publishes the packages to npm, tags them `chrome-<milestone>`, and publishes the GitHub release with that version's section of `CHANGELOG.md` followed by that of `CHANGELOG.zh-CN.md`. + +#### Patching an older release + +Usually a fix is released as the next patch from `main`. Only when `main` already holds changes that should not ship yet (a new minor in progress) and an older release needs the fix: + +1. Create `release/x.y` from the tag `vx.y.0` once, if it does not exist yet: `git switch -c release/x.y vx.y.0 && git push -u origin release/x.y`. +2. Merge the fix into `main` first, then cherry-pick it onto `release/x.y` through a pull request (`git cherry-pick -x <commit>`), with its changelog line. +3. Release from `release/x.y` as above, with the tag on that branch. + +Not set up yet, to do before the first such release: CI does not run on `release/**` branches, and the publish job moves npm's `latest` to whatever it publishes, so a patch to an older minor would replace the newer release as `latest`; it has to publish with `--tag` (or move `latest` back) instead. diff --git a/DEV.zh-CN.md b/DEV.zh-CN.md index 2e32eff..0fc8279 100644 --- a/DEV.zh-CN.md +++ b/DEV.zh-CN.md @@ -196,7 +196,7 @@ pnpm test # 前端需已构建,真实前端测试才会运行 pre-commit 钩子(husky + lint-staged)只处理暂存的文件:先用 oxfmt 格式化并把结果加入这次提交,再用 oxlint 检查,有错误就中止提交;还会运行 `scripts/check-contracts.ts`(见「约定放在哪里」),以及两道针对本次新增或改动内容的规模守卫:`scripts/check-file-size.ts` 对超过 1100 行的源码文件(`.ts`、`.js` 及其变体)发出警告,超过 1500 行拒绝提交;`scripts/check-dir-size.ts` 对本次有新增文件的目录,直属文件超过 20 个时警告,超过 30 个拒绝提交。出现警告后,下一次提交就应拆分该文件,或把目录按关注点分进子目录。两者都跳过要放进 devtools-frontend 检出目录、沿用 Chromium 风格的代码(`packages/devtools-frontend/src` 会被复制进去,`patches` 会打到它上面)和 `experiments/`。`pnpm lint` 会对所有已跟踪文件运行这两道守卫(`--all`),绕过钩子的提交也能在那里查出来。`git commit --no-verify` 可以跳过一次钩子。lint 规则:oxlint 的 correctness 规则,禁止 `any`,禁止非空断言(`!`)。代码风格:2 空格缩进、加分号、单引号、行宽 120。`packages/devtools-frontend/src` 沿用 Chromium 的风格,不做 lint 和格式化。 -构建时用 API Extractor 把各包的类型声明按入口合并成单个文件(`dist/index.d.ts`),并把包的 API 写进 `packages/<包>/etc/`:`*.public.api.md` 是公开 API(对外的承诺),`*.api.md` 是全部导出(含内部)(`scripts/api-extract.ts`,配置见 `api-extractor.base.json`;API Extractor 会丢掉 `declare global` 块,脚本会把它们补进合并后的文件)。`devtools-anywhere` 没有运行时入口:它的 `dist/index.d.ts` 只有类型,脚本都构建成 IIFE。报告有变化时与引起它的改动一起提交:公开 API 的变化靠它在评审中看到。设置 `CI=true` 时(供 CI 使用,仓库目前还没有 CI),报告与 API 不一致会让构建失败。入口的每个导出都要有发布标签:`@public` 只给 README 写明的用法(remote-server 的 `startRemoteServer` / `createRemoteHandler`、Vite 插件),其余一律 `@internal`,只供本仓库各包之间使用(一起构建、一起发布),随时可改;拿不准就标 `@internal`,入口用不到的就不导出。文档注释按 TSDoc 写:`@devtools-anywhere/...` 这类包名放进反引号,代码示例放进围栏代码块。 +构建时用 API Extractor 把各包的类型声明按入口合并成单个文件(`dist/index.d.ts`),并把包的 API 写进 `packages/<包>/etc/`:`*.public.api.md` 是公开 API(对外的承诺),`*.api.md` 是全部导出(含内部)(`scripts/api-extract.ts`,配置见 `api-extractor.base.json`;API Extractor 会丢掉 `declare global` 块,脚本会把它们补进合并后的文件)。`devtools-anywhere` 没有运行时入口:它的 `dist/index.d.ts` 只有类型,脚本都构建成 IIFE。报告有变化时与引起它的改动一起提交:公开 API 的变化靠它在评审中看到。设置 `CI=true` 时(GitHub Actions 会自动设置),报告与 API 不一致会让构建失败,所以忘了提交的报告会被 CI 拦下。入口的每个导出都要有发布标签:`@public` 只给 README 写明的用法(remote-server 的 `startRemoteServer` / `createRemoteHandler`、Vite 插件),其余一律 `@internal`,只供本仓库各包之间使用(一起构建、一起发布),随时可改;拿不准就标 `@internal`,入口用不到的就不导出。文档注释按 TSDoc 写:`@devtools-anywhere/...` 这类包名放进反引号,代码示例放进围栏代码块。 行为、用法或限制有变化时,同时更新 `README.md` 和 `README.zh-CN.md`(以及本指南的两种语言版本),并在 `CHANGELOG.md` 和 `CHANGELOG.zh-CN.md` 的 `Unreleased` 下各给用户写一行说明(前端升级到新的 Chrome 里程碑时,还要改它的 `Based on Chrome` 那一行)。 @@ -205,25 +205,46 @@ pre-commit 钩子(husky + lint-staged)只处理暂存的文件:先用 oxfm ### 提交 - 每个提交只做一步。 -- 提交信息格式:`type(scope): 概要`,空一行后用英文写正文,说明改了什么、为什么改。type 有 `feat`、`fix`、`docs`、`test`、`build`、`refactor`、`chore`;scope 用包名或模块名,例如 `cdp-agent`、`network`、`devtools-frontend`、`service-worker`、`readme`。 +- 提交信息格式:`type(scope): 概要`,空一行后用英文写正文,说明改了什么、为什么改。type 有 `feat`、`fix`、`docs`、`test`、`build`、`ci`、`refactor`、`chore`;scope 用包名或模块名,例如 `cdp-agent`、`network`、`devtools-frontend`、`service-worker`、`readme`。 -### 提 PR +### 分支与 PR + +`main` 是唯一的长期分支:所有改动都合进这里,发布也在这里打 tag,它应当随时可以发布。不设 `develop` 分支:合进 `main` 不会发布任何东西(只有推送 `vx.y.z` tag 才会,见「发布到 npm」),`main` 本身就存放着已合并、尚未发布的改动。 + +| 分支 | 用途 | 规则 | +| --- | --- | --- | +| `main` | 所有开发;发布在它上面打 tag | 只能通过 PR 修改,CI 必须通过,禁止强推 | +| `feat/…`、`fix/…`、`docs/…`、`ci/…` 等 | 每个分支只做一件事,短期存在;前缀与提交类型一致 | 从 `main` 拉出,通过 PR 合回后删除 | +| `release/x.y.z` | 准备 x.y.z 发布的 PR(版本号和 changelog) | 合进 `main`,然后在 `main` 上打 tag | +| `release/x.y` | 只在需要时用来给旧的 minor 打补丁(见「给旧版本打补丁」) | 从 tag `vx.y.0` 拉出,在该 minor 需要补丁期间保留 | + +修复不单独设一类分支:和其它改动一样走 `fix/…` 分支合进 `main`,作为 patch 发布。CI(`.github/workflows/ci.yml`)在每个 PR 和每次推送到 `main` 时运行:lint、类型检查、构建前端、跑全部测试。 ```bash -git switch -c my-change +git switch -c feat/my-change main # … 提交 … -git push -u origin my-change +git push -u origin feat/my-change gh pr create --fill ``` -在 PR 里写明验证过什么,尤其是哪些在浏览器里手动确认过、哪些没有(Safari、真机)。 +在 PR 里写明验证过什么,尤其是哪些在浏览器里手动确认过、哪些没有(Safari、真机)。用户能感知的改动,在同一个 PR 里往两份 changelog 的 `Unreleased` 下各加一行,这样发布时 changelog 已经备好。 ### 发布到 npm 所有包使用同一个版本号,一起发布:推送 tag `vx.y.z` 后由 `.github/workflows/release.yml` 发布。npm 信任这个 workflow(可信发布,在 npmjs.com 上为每个包配置了本仓库、`release.yml` 和 `npm` 环境),所以发布时不需要保存令牌,并且带出处证明(provenance);改了文件名或环境名就会失效。批准发布需要仓库 `npm` 环境的审批人。每个包的可信发布方都要勾选允许 `npm publish` 和 `npm dist-tag`,不能只允许 `npm stage publish`。 -版本号按 semver 表示库本身的变化,从 `0.1.0` 开始:修 bug 升 patch;前端升级到新的 Chrome 里程碑升 minor(界面会变);1.0 之前 minor 也可以有破坏性变更。承诺的范围是 README 写明的用法(`@public`)。Chrome 版本不放进版本号:写成 `+chrome156` 会被 npm 丢掉,写成 `-chrome156` 会被当成预发布版。它记在每个发布包 `package.json` 的 `chrome` 字段里(由 `update` 写入),每次发布的说明也在开头写明(「基于 Chrome 156」)。每次发布还会打上 dist-tag `chrome-<里程碑>`,`npm i devtools-anywhere@chrome-156` 就能装到基于该版 Chrome 的最后一个版本。 +版本号按 semver 表示库本身的变化,从 `0.1.0` 开始:修 bug 升 patch;新功能、或前端升级到新的 Chrome 里程碑(界面会变)升 minor;1.0 之前 minor 也可以有破坏性变更。有值得发布的内容时才发版(攒够一个 minor 的功能、用户在等的修复、新的 Chrome 里程碑),不是每次合并都发。承诺的范围是 README 写明的用法(`@public`)。Chrome 版本不放进版本号:写成 `+chrome156` 会被 npm 丢掉,写成 `-chrome156` 会被当成预发布版。它记在每个发布包 `package.json` 的 `chrome` 字段里(由 `update` 写入),每次发布的说明也在开头写明(「基于 Chrome 156」)。每次发布还会打上 dist-tag `chrome-<里程碑>`,`npm i devtools-anywhere@chrome-156` 就能装到基于该版 Chrome 的最后一个版本。 -1. 把所有 `packages/*/package.json` 的 `version` 改成同一个版本,把 `CHANGELOG.md` 和 `CHANGELOG.zh-CN.md` 里 `Unreleased` 下的内容挪到 `## x.y.z` 下,然后提交(`build: release x.y.z`)。`node scripts/release.ts check x.y.z` 会检查这两项。 -2. 打 tag 并推送:`git tag vx.y.z && git push origin main vx.y.z`。 +1. 从 `main` 拉出 `release/x.y.z` 分支,把所有 `packages/*/package.json` 的 `version` 改成同一个版本,把 `CHANGELOG.md` 和 `CHANGELOG.zh-CN.md` 里 `Unreleased` 下的内容挪到 `## x.y.z` 下,然后提交(`build: release x.y.z`)。`node scripts/release.ts check x.y.z` 会检查这两项。开 PR,CI 通过后合并。 +2. 在 `main` 上给合并后的提交打 tag 并推送这个 tag:`git switch main && git pull && git tag vx.y.z && git push origin vx.y.z`。 3. workflow 会检查版本号、构建前端、像 CI 一样跑检查和测试,并打包(pnpm 会把 `workspace:*` 替换成实际版本)。然后在 Actions 页面批准它的 `publish` 任务:它把各包发布到 npm,打上 dist-tag `chrome-<里程碑>`,并发布 GitHub Release,说明是 `CHANGELOG.md` 中该版本的那一节,后面接上 `CHANGELOG.zh-CN.md` 的对应一节。 + +#### 给旧版本打补丁 + +通常修复直接从 `main` 作为下一个 patch 发布。只有当 `main` 上已经有暂时不该发布的改动(正在做的新 minor),而旧版本又需要这个修复时,才这样做: + +1. 如果还没有 `release/x.y`,从 tag `vx.y.0` 建一次:`git switch -c release/x.y vx.y.0 && git push -u origin release/x.y`。 +2. 先把修复合进 `main`,再通过 PR 把它 cherry-pick 到 `release/x.y`(`git cherry-pick -x <commit>`),连同它的 changelog 条目。 +3. 按上面的步骤从 `release/x.y` 发布,tag 打在这个分支上。 + +尚未就绪,第一次这样发布前要补上:CI 不在 `release/**` 分支上运行;publish 任务会把 npm 的 `latest` 移到它发布的版本,给旧 minor 打的补丁会顶替更新的版本成为 `latest`,需要改成用 `--tag` 发布(或之后把 `latest` 移回去)。 From bb00df2a211d26a4a1dc79f584526598db700ec8 Mon Sep 17 00:00:00 2001 From: jerrychan7 <czy-ken@163.com> Date: Sun, 1 Nov 2026 21:15:59 +0800 Subject: [PATCH 15/26] docs: move the design out of the README into docs/design.md The README spent most of its length on how the project works before saying how to use it. It now answers what the project is, why, how to use it, what it cannot do and which packages there are, in that order; the design moves to docs/design.md and docs/design.zh-CN.md: - the technology choices and the relation to Chii and chobitsu; - the architecture: the probe, shell mode and injection; - why the packages are split as they are (the package table stays in the README). The text is moved, not rewritten. Links into the moved sections (shell mode, injection) now point at the design document, and its links to DEV.md and experiments/ are relative to docs/. DEV.md and DEV.zh-CN.md point to the design document, list docs/ in the repository layout and ask for it to be kept up to date with the README. --- DEV.md | 5 +- DEV.zh-CN.md | 5 +- README.md | 139 ++++++++++++------------------------------- README.zh-CN.md | 139 ++++++++++++------------------------------- docs/design.md | 76 +++++++++++++++++++++++ docs/design.zh-CN.md | 76 +++++++++++++++++++++++ 6 files changed, 232 insertions(+), 208 deletions(-) create mode 100644 docs/design.md create mode 100644 docs/design.zh-CN.md diff --git a/DEV.md b/DEV.md index 94ec6c9..ab51f4a 100644 --- a/DEV.md +++ b/DEV.md @@ -2,7 +2,7 @@ English | [简体中文](DEV.zh-CN.md) -How to set up a development environment, work on the packages with live rebuilds, and get changes merged or released. For what the project does and how it is designed, see the [README](README.md). +How to set up a development environment, work on the packages with live rebuilds, and get changes merged or released. For what the project does, see the [README](README.md); for how it works and why, the [design](docs/design.md). ## 1. First-time setup @@ -88,6 +88,7 @@ tests/ tests that span packages e2e/ probe → CDP → frontend support/ launches Chrome with the static server playground/ pages and server for development +docs/ design (design.md, design.zh-CN.md) experiments/ validation records ``` @@ -198,7 +199,7 @@ The pre-commit hook (husky + lint-staged) runs on the staged files only: it form The build rolls each package's type declarations up into one file per entry point (`dist/index.d.ts`) with API Extractor, and writes the package's API to `packages/<package>/etc/`: `*.public.api.md` for the public API (what is promised), `*.api.md` for everything exported, internal included (`scripts/api-extract.ts`, configured by `api-extractor.base.json`; it copies `declare global` blocks into the rolled-up file, which API Extractor leaves out). `devtools-anywhere` has no runtime entry: its `dist/index.d.ts` is types only, and its scripts are built as IIFEs. Commit a changed report with the change that caused it: it is how a change to the public API shows in review. With `CI=true`, which GitHub Actions sets, a report that no longer matches fails the build, so CI catches a report left uncommitted. Every export of an entry point needs a release tag: `@public` only for what the README documents (remote-server's `startRemoteServer` / `createRemoteHandler`, the Vite plugin), `@internal` for everything else, which is used only between this repository's packages, built and released together, and may change at any time; when in doubt, `@internal`, and export nothing an entry point does not need. Doc comments are TSDoc: put package names such as `@devtools-anywhere/...` in backticks and code examples in fenced blocks. -Also update both `README.md` and `README.zh-CN.md` (and this guide in both languages) when behavior, usage or limitations change, and add a line for users under `Unreleased` in both `CHANGELOG.md` and `CHANGELOG.zh-CN.md` (moving the frontend to a new Chrome milestone also changes its `Based on Chrome` line). +Also update both `README.md` and `README.zh-CN.md` (and this guide and `docs/design.md` in both languages) when behavior, usage or limitations change, and add a line for users under `Unreleased` in both `CHANGELOG.md` and `CHANGELOG.zh-CN.md` (moving the frontend to a new Chrome milestone also changes its `Based on Chrome` line). When a dependency that ends up in a build output is added or upgraded, or code is ported from another project (including moving the DevTools frontend to a new `REVISION`), run `pnpm licenses:update` to regenerate `THIRD_PARTY_LICENSES.md` at the root and in the packages (for a new library, add it to the list in `scripts/third-party-licenses.ts` first). It needs the frontend built: the build writes the `front_end/third_party` libraries that end up in `dist/` to `packages/devtools-frontend/dist-third-party.ignore.json` (next to `dist/`, not published), which the script checks its list against; `build:frontend --copy-only` regenerates it from the last build. diff --git a/DEV.zh-CN.md b/DEV.zh-CN.md index 0fc8279..a900334 100644 --- a/DEV.zh-CN.md +++ b/DEV.zh-CN.md @@ -2,7 +2,7 @@ [English](DEV.md) | 简体中文 -介绍如何搭建开发环境、在实时重新构建下开发各个包,以及改动如何合入或发布。项目做什么、怎么设计的,见 [README](README.zh-CN.md)。 +介绍如何搭建开发环境、在实时重新构建下开发各个包,以及改动如何合入或发布。项目做什么见 [README](README.zh-CN.md),工作原理和设计取舍见[设计文档](docs/design.zh-CN.md)。 ## 1. 第一次开发:环境配置 @@ -88,6 +88,7 @@ tests/ 跨包的测试 e2e/ 探针 → CDP → 前端的完整流程 support/ 启动 Chrome 和静态服务器 playground/ 开发调试用的页面和服务器 +docs/ 设计文档(design.md、design.zh-CN.md) experiments/ 验证记录 ``` @@ -198,7 +199,7 @@ pre-commit 钩子(husky + lint-staged)只处理暂存的文件:先用 oxfm 构建时用 API Extractor 把各包的类型声明按入口合并成单个文件(`dist/index.d.ts`),并把包的 API 写进 `packages/<包>/etc/`:`*.public.api.md` 是公开 API(对外的承诺),`*.api.md` 是全部导出(含内部)(`scripts/api-extract.ts`,配置见 `api-extractor.base.json`;API Extractor 会丢掉 `declare global` 块,脚本会把它们补进合并后的文件)。`devtools-anywhere` 没有运行时入口:它的 `dist/index.d.ts` 只有类型,脚本都构建成 IIFE。报告有变化时与引起它的改动一起提交:公开 API 的变化靠它在评审中看到。设置 `CI=true` 时(GitHub Actions 会自动设置),报告与 API 不一致会让构建失败,所以忘了提交的报告会被 CI 拦下。入口的每个导出都要有发布标签:`@public` 只给 README 写明的用法(remote-server 的 `startRemoteServer` / `createRemoteHandler`、Vite 插件),其余一律 `@internal`,只供本仓库各包之间使用(一起构建、一起发布),随时可改;拿不准就标 `@internal`,入口用不到的就不导出。文档注释按 TSDoc 写:`@devtools-anywhere/...` 这类包名放进反引号,代码示例放进围栏代码块。 -行为、用法或限制有变化时,同时更新 `README.md` 和 `README.zh-CN.md`(以及本指南的两种语言版本),并在 `CHANGELOG.md` 和 `CHANGELOG.zh-CN.md` 的 `Unreleased` 下各给用户写一行说明(前端升级到新的 Chrome 里程碑时,还要改它的 `Based on Chrome` 那一行)。 +行为、用法或限制有变化时,同时更新 `README.md` 和 `README.zh-CN.md`(以及本指南和 `docs/design.md` 的两种语言版本),并在 `CHANGELOG.md` 和 `CHANGELOG.zh-CN.md` 的 `Unreleased` 下各给用户写一行说明(前端升级到新的 Chrome 里程碑时,还要改它的 `Based on Chrome` 那一行)。 新增或升级会进入产物的依赖、移植其它项目的代码(包括把 DevTools 前端换到新的 `REVISION`)时,运行 `pnpm licenses:update` 重新生成根目录和各包的 `THIRD_PARTY_LICENSES.md`(新增的库先加进 `scripts/third-party-licenses.ts` 的清单)。 diff --git a/README.md b/README.md index 13e6a65..c5ba14d 100644 --- a/README.md +++ b/README.md @@ -21,104 +21,6 @@ Goals: 3. **DevTools survive reloads**: DevTools stay open across page reloads and navigations, with logs preserved. 4. **Keep up with the latest Chrome DevTools**: maintain a patch set on top of the official devtools-frontend and upgrade regularly instead of staying on an old version. -## Design - -### Technology choices - -| Part | Choice | Rationale | -|---|---|---| -| CDP implementation | Written in this project, using [chobitsu](https://github.com/liriliri/chobitsu) as a reference | In-page debugging can only collect data through JS APIs, so it has to be written in JS/TS. Owning it lets the probe and the CDP domains share the same hooks (no replay tricks), fixes chobitsu's known issues, and tracks the latest protocol through [`devtools-protocol`](https://github.com/ChromeDevTools/devtools-protocol) types | -| Frontend | Official devtools-frontend, built from source with a small patch set | The build pins an official revision and keeps patches as `.patch` files that are replayed on upgrade, instead of maintaining a long-lived fork branch. The patches only register an own entrypoint (`anywhere_app`); everything else is done without touching upstream code (see [Working on the frontend](DEV.md#working-on-the-frontend)) | -| Isolation | iframe | Keeps DevTools' own DOM, logs and requests out of the inspected page, and prevents the two global environments from interfering with each other | - -This project depends on neither Chii nor chobitsu. Their source is read as a reference for edge cases, but no code is copied; real Chrome's CDP output and the protocol definition are the source of truth. - -### Limitations - -- An in-page CDP implementation can only use JS APIs, so the following are **not available**: breakpoint debugging, Profiler, Memory, and real network timing. These panels are not included in the frontend rather than shown and found broken. A `debugger;` statement in page scripts cannot pause either; in shell mode, each one hit in a same-origin script logs a warning with its stack in the Console instead. Real breakpoints need the browser's own debugger (`chrome://inspect` for Android, Safari's Web Inspector for iOS); [remote debugging](#remote-debugging) does not change this, since the page still runs the same in-page CDP agent. The Sources panel's Page tab lists the page's documents, external scripts and style sheets, images and fonts (inline scripts are not listed, since their position in the document is not known); snippets work. -- Where page scripts lack an API, the gap is filled exactly or not at all; approximations are not shown as facts: - - User-agent styles are not shown: browsers do not expose their default style sheet, and a bundled copy could differ from the browser's. - - Event listeners are recorded by the probe from the moment it runs (it must be the first script); their source locations are not available. - - WebSocket connections and their frames are shown, but not the handshake headers, which the page cannot see. - - The screencast (the toolbar's device button, off by default) shows the viewport as [snapDOM](https://github.com/zumerlab/snapdom) redraws it from the DOM, not the pixels on screen: canvas, video and cross-origin images may be missing, and it updates about twice a second at most. snapDOM (about 90 KB gzip) is loaded only when the screencast starts or a screenshot is taken. - - Screenshots (`Page.captureScreenshot`, for tools and agents that drive the page over CDP) are rendered by snapDOM the same way, so they miss what the screencast misses. The response says so with a field Chrome does not send, `approximate: true`. Otherwise the parameters behave as in Chrome: `clip` is in page coordinates, the image has device pixels (`devicePixelRatio` times `clip.scale`), and `captureBeyondViewport` without a clip takes the whole page. An image past the browser's canvas limits (32767 pixels a side, 16384 on Safari, 16384 × 16384 in area) is scaled down to fit, and snapDOM warns about it in the page's console. Browsers that cannot encode WebP (Safari) return PNG when WebP is asked for. - - Device mode (shell mode only, since it needs the page in an iframe it can resize) sets the page's size, as the viewport the page sees: media queries, `vw`/`vh` and `innerWidth` follow. It does not change `devicePixelRatio`, the user agent or touch input, which a page cannot change: the toolbar offers no device pixel ratio or device type, and a device preset sets only the size. Screenshots from its menu are snapDOM's, as above, at the page's own pixel ratio. - - Input from the screencast is replayed as DOM events, which are not trusted (`isTrusted` is false): focus, clicks, scrolling and text editing are carried out, but pages that check `isTrusted` ignore them. - - Cross-origin style sheets are read through a fetched copy, which needs CORS headers; their rules are read-only. - - Forced pseudo-classes (`:hover`, `:focus`, … from the Styles pane) are emulated with companion style sheets that keep the cascade order; sheets from `@import` and `adoptedStyleSheets` are not covered. -- While the page has an element fullscreen (the Fullscreen API), DevTools and its F12 button cannot be used: the browser lets nothing outside the fullscreen element take input, in shell mode too. Leave fullscreen (Esc) to use them again. -- Host the frontend on a different origin from the page. On the same origin, DevTools' own settings share the page's `localStorage` and show up in the Application panel. -- The page side uses only standard web APIs and works in any modern browser. The DevTools UI is developed and tested by Google for Chrome; Firefox and Safari are not verified yet, and newer frontend versions may need extra compatibility patches. - -### Architecture - -``` -┌────────── Top-level window (shell) ──────────┐ -│ ┌────────────────────────────────────────┐ │ -│ │ Page iframe │ │ -│ │ Probe (first sync script in <head>) │ │ -│ │ └─ buffer → CDP runtime (lazy) │ │ -│ └───────────────────┬────────────────────┘ │ -│ postMessage (origin-checked) │ -│ ┌───────────────────┴────────────────────┐ │ -│ │ DevTools iframe │ │ -│ └────────────────────────────────────────┘ │ -└──────────────────────────────────────────────┘ -``` - -#### 1. Probe - -A synchronous script of about 6.2 KB (min+gzip, including shell mode, auto-mounting and remote debugging) that must be the first script executed in `<head>`. - -- Immediately hooks `console`, `window.onerror`, `unhandledrejection`, `fetch`, `XHR` and `WebSocket`, and writes events with timestamps into a buffer. WebSocket frames are kept as given; binary payloads are only converted to base64 by the CDP agent. Until a CDP session mounts, each list (console messages, requests, WebSockets, frames per WebSocket) keeps its last 10,000 entries, a bound for pages that never load one; the session then sets its own: it keeps the last 1000 console messages like V8, and every request and WebSocket like Chrome, dropping only the oldest response bodies past the buffer sizes given to `Network.enable` (Chrome's defaults when none are). -- Requests that cannot be hooked (the document, style sheets, scripts, images, fonts, …) are recorded from Resource Timing; a buffered `PerformanceObserver` also delivers those from before the probe ran. They have URL, type, timing, size and (where the browser exposes it) status and MIME type, but no headers; their bodies are loaded again from the HTTP cache on demand. In shell mode with the Service Worker, it adds the request and response headers of same-origin ones. Hooks stay first because they work from the very start, keep call stacks, and need no deployment; a Service Worker only controls pages after it is installed, conflicts with a site's own, and would have to proxy requests to see responses. -- The CDP runtime and the frontend are loaded lazily. The runtime's Runtime and Network domains consume the probe's buffer directly and subscribe to new events, so every API is hooked only once. - -Nothing outside a page can observe its output; only a script that runs before the page's own code can capture the earliest output. The full CDP runtime is too large to load synchronously, so only the minimal capture logic goes first. - -This approach has been validated: see [experiments/early-capture/RESULTS.md](experiments/early-capture/RESULTS.md). With a ~1.4 KB probe and chobitsu loaded lazily, every early console call, exception and request reached DevTools, with expandable objects and original timestamps and stacks. - -#### 2. Shell mode (in-place rewrite) - -No standalone `devtools.html` is hosted, and there is no navigation to a blob URL, since blob URLs break on reload and cannot be shared. Instead: - -1. With the zero-config probe (`data-devtools-frontend-url`, or the Vite plugin), the top-level page enters shell mode by default on its first load: the probe stores the frontend and bundle URLs in `sessionStorage` itself, before the page's scripts run, so the page is not reloaded. DevTools starts hidden unless `data-open-immediately` is set. Otherwise, the lock button in DevTools' toolbar, "Keep DevTools open across reloads", stores them and reloads the page. -2. The probe, being the first script, sees the config in the top-level window. It writes an unclosed `<plaintext>` so the rest of the page becomes inert text (none of its scripts run), then replaces the document with the shell via `document.open()` / `document.write()`. The shell contains the page iframe (pointing to `location.href`) and `<devtools-anywhere>`, which knows it is the shell's element by the document it is in. (Chrome ignores `document.open()` after `window.stop()`, hence `<plaintext>`.) -3. Inside the iframe, the probe captures as usual and loads the bundle, which connects the page's CDP session to the shell. The shell relays messages between the page and the frontend. -4. When the page reloads or navigates, the new page says hello; the shell tells the frontend the page navigated and re-sends every `enable` command the frontend has sent, so the new page replays its own earliest output. The address bar and title follow the page, through loads, history traversals and same-document navigations (`pushState` where the Navigation API exists), so that a reload or a copied link opens the page shown. -5. DevTools fills the shell and lays out the page and itself, as in Chrome: the page iframe goes where the frontend's page area is, on top of it. Dock side and sizes are DevTools' own (its resizer; on touch screens, a wider handle on the edge between the page and DevTools). Undocked, DevTools is a window over the page. The toolbar's device button is device mode, as in Chrome, in place of the screencast: the page iframe is sized as the device and scaled to fit, and the toolbar's menu takes screenshots. Undocked, device mode's toolbar is drawn in a page of the frontend's (`device_mode_emulation_frame.html`, as Chrome does) shown under the window. -6. The toolbar's × hides DevTools and stays in shell mode; the page fills the shell at its own size until DevTools is shown again. The lock button again clears the config and reloads the page on its own; the choice is remembered in `localStorage`, and wins over the page's `data-default-shell-mode` from then on, unless the page sets `data-shell-mode-locked`. - -The stored URLs are only trusted when they have the same origin as the probe script; otherwise the config is cleared and the page loads normally. Shell mode therefore needs the probe, the bundle and the frontend served from the page's origin. - -Inside an iframe, `viewport`, `100vh`, `position: fixed`, `window.top` and others may behave differently from a top-level page; the page's HTML is requested twice (by the top-level window, then by the iframe), and a page returned for a form POST is requested again with GET. Turn shell mode off with `data-default-shell-mode="off"` (the Vite plugin's `defaultShellMode: 'off'`), and DevTools is embedded directly in the page. A page that refuses to be framed (`X-Frame-Options`, CSP `frame-ancestors`), or that keeps navigating the shell away, is shown on its own for the rest of the session. - -#### 3. Injection - -| Method | When to use | -|---|---| -| Add a `<script>` manually (must be the first one in `<head>`) | Any page | -| `@devtools-anywhere/vite-plugin`: injects in development only (`vite serve`) | Projects using Vite (a webpack plugin may follow) | -| `devtools-anywhere-service-worker.js`: in shell mode, injects the probe into same-origin pages loaded in the page iframe that do not include it | Secure context (HTTPS or localhost), a scope covering the pages (`Service-Worker-Allowed: /`), and no Service Worker of the site's own | - -All methods share the same probe. - -### Packaging - -An umbrella package plus scoped packages under the [`@devtools-anywhere`](https://www.npmjs.com/org/devtools-anywhere) npm organization: - -| Package | Contents | -|---|---| -| `devtools-anywhere` | Umbrella package: `probe.iife.js` (probe plus auto-mounting), `devtools-anywhere.iife.js` (the `<devtools-anywhere>` element, which the probe loads), `devtools-anywhere-service-worker.js`, `snapdom.js` (the renderer of the screencast and of screenshots, loaded on demand), and TypeScript types for the element, its events and the script and toggler attributes. There is no module to import: the scripts are the runtime | -| `@devtools-anywhere/devtools-frontend` | Prebuilt frontend static assets with English and Chinese UI, self-hostable or served from a CDN | -| `@devtools-anywhere/devtools-frontend-locales` | Optional: the other 80 UI languages, at the same paths as in the frontend package. Serve both from one directory (the Vite plugin does this when it is installed); a missing language falls back to English | -| `@devtools-anywhere/vite-plugin` | Vite plugin | -| `@devtools-anywhere/remote-server` | Remote debugging server (Node.js) and its CLI, `devtools-anywhere-remote` | - -- The in-page CDP agent and its probe are not a separate package: the probe and the domains share the same hooks and buffers, and only `devtools-anywhere` uses them. They live in `devtools-anywhere` (`src/cdp-agent/`), which rules out version mismatches; the probe is still built as a separate tiny file, `probe.iife.js`. -- The frontend is separate because it is large (about 16 MB) and follows Chrome's release cadence. Its other languages (about 42 MB) are another package, so that only those who need them install them. -- All packages share one version number and are released together, so matching versions always work together. - ## Usage (preview) The packages are on npm (`npm i devtools-anywhere @devtools-anywhere/devtools-frontend`); the paths below refer to their `dist/` directories, which a build of this repository makes too. `data-devtools-frontend-url` points at `anywhere_app.html` of a `@devtools-anywhere/devtools-frontend` build. Its `bundle/` files are named after their content and can be cached for good; serve `anywhere_app.html` itself with revalidation (`Cache-Control: no-cache`), so that an update reaches the browser. @@ -133,7 +35,7 @@ The packages are on npm (`npm i devtools-anywhere @devtools-anywhere/devtools-fr </head> ``` -From a CDN: jsDelivr and unpkg serve every npm package, so nothing needs hosting. Pin an exact version, the same for both packages. Only English and Chinese are available this way, since the other languages are in a separate package that has to be served from the frontend's directory. Shell mode is not available either, as it needs the probe, the bundle and the frontend from the page's origin (see [shell mode](#2-shell-mode-in-place-rewrite)): `data-default-shell-mode="off"` embeds DevTools directly in the page. +From a CDN: jsDelivr and unpkg serve every npm package, so nothing needs hosting. Pin an exact version, the same for both packages. Only English and Chinese are available this way, since the other languages are in a separate package that has to be served from the frontend's directory. Shell mode is not available either, as it needs the probe, the bundle and the frontend from the page's origin (see [shell mode](docs/design.md#2-shell-mode-in-place-rewrite)): `data-default-shell-mode="off"` embeds DevTools directly in the page. ```html <!-- jsDelivr --> @@ -142,7 +44,7 @@ From a CDN: jsDelivr and unpkg serve every npm package, so nothing needs hosting <script src="https://unpkg.com/devtools-anywhere@0.1.0/dist/probe.iife.js" data-devtools-frontend-url="https://unpkg.com/@devtools-anywhere/devtools-frontend@0.1.0/dist/anywhere_app.html" data-default-shell-mode="off"></script> ``` -After the page loads, the probe loads `devtools-anywhere.iife.js` from next to itself (or `data-bundle`) and mounts its own `<devtools-anywhere>` under `<html>`, next to `<body>`, whatever `<devtools-anywhere>` elements the page has; there is one such global element per page, and another one only logs a warning. Optional attributes: `data-open-immediately` opens DevTools right away; `data-default-shell-mode="off"` stays out of shell mode, which the top-level page enters by default (`on`); `data-shell-mode-locked` keeps shell mode as `data-default-shell-mode` says, ignoring the user's choice (which stays stored) and hiding DevTools' switch, though a page that refuses to be framed is still shown on its own; `data-no-toggler` hides the F12 button, also in shell mode; `data-service-worker` enables the Service Worker in shell mode, which injects the probe into same-origin pages in the page iframe that do not include it and adds the request and response headers of same-origin requests (documents, stylesheets, scripts, images and so on); it is off by default, see its [requirements](#3-injection). +After the page loads, the probe loads `devtools-anywhere.iife.js` from next to itself (or `data-bundle`) and mounts its own `<devtools-anywhere>` under `<html>`, next to `<body>`, whatever `<devtools-anywhere>` elements the page has; there is one such global element per page, and another one only logs a warning. Optional attributes: `data-open-immediately` opens DevTools right away; `data-default-shell-mode="off"` stays out of shell mode, which the top-level page enters by default (`on`); `data-shell-mode-locked` keeps shell mode as `data-default-shell-mode` says, ignoring the user's choice (which stays stored) and hiding DevTools' switch, though a page that refuses to be framed is still shown on its own; `data-no-toggler` hides the F12 button, also in shell mode; `data-service-worker` enables the Service Worker in shell mode, which injects the probe into same-origin pages in the page iframe that do not include it and adds the request and response headers of same-origin requests (documents, stylesheets, scripts, images and so on); it is off by default, see its [requirements](docs/design.md#3-injection). ### Vite @@ -155,11 +57,11 @@ export default { } ``` -Only `vite serve` is affected: the probe is injected as the first script of every page, and the bundle, the Service Worker and the frontend are served under `/@devtools-anywhere/`. The frontend comes from `@devtools-anywhere/devtools-frontend`. Options: `devtoolsFrontendPath` (the entry page of another frontend build on disk, such as `…/anywhere_app.html`; its directory is served), `devtoolsFrontendUrl` (a frontend hosted elsewhere, served in place of it), `openImmediately`, `defaultShellMode` (`'on'` or `'off'`), `shellModeLocked`, `noToggler` (hides the F12 button), `serviceWorker` (see its [requirements](#3-injection); the plugin sends `Service-Worker-Allowed` itself), which match the probe's attributes, `remote` (the URL of a [remote debugging](#remote-debugging) server to connect pages to, alongside the element), with `idInQuery` (`data-id-in-query`, for a server with `idInQuery`). +Only `vite serve` is affected: the probe is injected as the first script of every page, and the bundle, the Service Worker and the frontend are served under `/@devtools-anywhere/`. The frontend comes from `@devtools-anywhere/devtools-frontend`. Options: `devtoolsFrontendPath` (the entry page of another frontend build on disk, such as `…/anywhere_app.html`; its directory is served), `devtoolsFrontendUrl` (a frontend hosted elsewhere, served in place of it), `openImmediately`, `defaultShellMode` (`'on'` or `'off'`), `shellModeLocked`, `noToggler` (hides the F12 button), `serviceWorker` (see its [requirements](docs/design.md#3-injection); the plugin sends `Service-Worker-Allowed` itself), which match the probe's attributes, `remote` (the URL of a [remote debugging](#remote-debugging) server to connect pages to, alongside the element), with `idInQuery` (`data-id-in-query`, for a server with `idInQuery`). ### The element -A floating F12 button opens the panel; drag its bar to resize (in shell mode, DevTools' own resizer, see [shell mode](#2-shell-mode-in-place-rewrite)). Left of DevTools' settings button, the lock button switches shell mode on and off (a dialog explains it first, and an info bar shows while it is on) and the refresh button reloads the page; the toolbar's × closes the panel. DevTools' main menu (⋮) has "Dock side": bottom (the default), right, left, or undocked, a window over the page moved by its bar and resized from its corner. In shell mode the page shrinks to make room, as in Chrome; otherwise the panel covers the page. The side and sizes are remembered in localStorage, sizes as percentages of the viewport (`dvh`/`dvw`), so they fit any screen and orientation. Its one attribute, `devtools-frontend-url`, is optional: without it, the probe's `data-devtools-frontend-url` is used. The rest is the library's: the probe's options set the element up, and the user opens and closes it; it has no methods of its own. The page can restyle the F12 button with `devtools-anywhere::part(toggler)`, in shell mode too: there the button is in the page iframe, in a `<devtools-anywhere>` under the page's `<html>` that only holds it and opens and closes the shell's DevTools (the shell's own button shows only while the page in the iframe has no probe, such as another origin's page). The probe's element does not expose its iframe as a part. Keep the element in one place, since moving it reloads the iframe. +A floating F12 button opens the panel; drag its bar to resize (in shell mode, DevTools' own resizer, see [shell mode](docs/design.md#2-shell-mode-in-place-rewrite)). Left of DevTools' settings button, the lock button switches shell mode on and off (a dialog explains it first, and an info bar shows while it is on) and the refresh button reloads the page; the toolbar's × closes the panel. DevTools' main menu (⋮) has "Dock side": bottom (the default), right, left, or undocked, a window over the page moved by its bar and resized from its corner. In shell mode the page shrinks to make room, as in Chrome; otherwise the panel covers the page. The side and sizes are remembered in localStorage, sizes as percentages of the viewport (`dvh`/`dvw`), so they fit any screen and orientation. Its one attribute, `devtools-frontend-url`, is optional: without it, the probe's `data-devtools-frontend-url` is used. The rest is the library's: the probe's options set the element up, and the user opens and closes it; it has no methods of its own. The page can restyle the F12 button with `devtools-anywhere::part(toggler)`, in shell mode too: there the button is in the page iframe, in a `<devtools-anywhere>` under the page's `<html>` that only holds it and opens and closes the shell's DevTools (the shell's own button shows only while the page in the iframe has no probe, such as another origin's page). The probe's element does not expose its iframe as a part. Keep the element in one place, since moving it reloads the iframe. A page can also place `<devtools-anywhere>` itself, as a box where DevTools shows: a block in the page's flow (`display: block`), sized by the page's CSS, with DevTools loaded as soon as it is connected. The page shows and hides it with CSS or `hidden`; hidden, DevTools stays loaded. It has no F12 button, dock side or resizing, and never enters shell mode (DevTools offers no way to); inside the shell's page it works as on its own. `devtools-frontend-url` is its only attribute; others are ignored. DevTools' iframe in it can be styled with `devtools-anywhere::part(iframe)`. @@ -192,6 +94,39 @@ The page connects after it loads (early output is captured as usual), and shows - An HTTPS page can only connect to an HTTPS server: `--cert` and `--key`. Other options: `--host`, `--base-path`, `--id-in-query` (see `idInQuery` below), `--devtools-frontend-path`, and `--devtools-frontend-url` to open a frontend hosted elsewhere (a CDN; an HTTPS one needs the server over HTTPS too; its links carry `ws=`/`wss=` and the token in the URL fragment, so they are not sent to the CDN). The same is available from Node.js as `startRemoteServer(options)`, and `createRemoteHandler(options)` gives its request and upgrade handlers, to mount under a base path in a server of one's own (Express, Koa, …): each returns false for what is not its own. Pages connect to `<base>target/<id>` and DevTools to `<base>client/<id>`; with `idInQuery: true`, for a host that routes WebSocket upgrades by exact path only, both connect to the base path itself (`<base>?target=<id>`, `<base>?client=<id>`): the target list and the printed script tag use that form, whose script tag carries `data-id-in-query`, and register the base path, with its trailing slash, for upgrades. Both forms are always accepted. A frontend opened by itself connects to any target as `anywhere_app.html?ws=<host:port/path>` (`wss=` over HTTPS; also read from the fragment, `#?ws=`), the WebSocket URL without its scheme, as in Chrome's `devtools://devtools/bundled/devtools_app.html?ws=`. - `/json/list` (with the token) lists the targets in the shape of Chrome's. +## Limitations + +- An in-page CDP implementation can only use JS APIs, so the following are **not available**: breakpoint debugging, Profiler, Memory, and real network timing. These panels are not included in the frontend rather than shown and found broken. A `debugger;` statement in page scripts cannot pause either; in shell mode, each one hit in a same-origin script logs a warning with its stack in the Console instead. Real breakpoints need the browser's own debugger (`chrome://inspect` for Android, Safari's Web Inspector for iOS); [remote debugging](#remote-debugging) does not change this, since the page still runs the same in-page CDP agent. The Sources panel's Page tab lists the page's documents, external scripts and style sheets, images and fonts (inline scripts are not listed, since their position in the document is not known); snippets work. +- Where page scripts lack an API, the gap is filled exactly or not at all; approximations are not shown as facts: + - User-agent styles are not shown: browsers do not expose their default style sheet, and a bundled copy could differ from the browser's. + - Event listeners are recorded by the probe from the moment it runs (it must be the first script); their source locations are not available. + - WebSocket connections and their frames are shown, but not the handshake headers, which the page cannot see. + - The screencast (the toolbar's device button, off by default) shows the viewport as [snapDOM](https://github.com/zumerlab/snapdom) redraws it from the DOM, not the pixels on screen: canvas, video and cross-origin images may be missing, and it updates about twice a second at most. snapDOM (about 90 KB gzip) is loaded only when the screencast starts or a screenshot is taken. + - Screenshots (`Page.captureScreenshot`, for tools and agents that drive the page over CDP) are rendered by snapDOM the same way, so they miss what the screencast misses. The response says so with a field Chrome does not send, `approximate: true`. Otherwise the parameters behave as in Chrome: `clip` is in page coordinates, the image has device pixels (`devicePixelRatio` times `clip.scale`), and `captureBeyondViewport` without a clip takes the whole page. An image past the browser's canvas limits (32767 pixels a side, 16384 on Safari, 16384 × 16384 in area) is scaled down to fit, and snapDOM warns about it in the page's console. Browsers that cannot encode WebP (Safari) return PNG when WebP is asked for. + - Device mode (shell mode only, since it needs the page in an iframe it can resize) sets the page's size, as the viewport the page sees: media queries, `vw`/`vh` and `innerWidth` follow. It does not change `devicePixelRatio`, the user agent or touch input, which a page cannot change: the toolbar offers no device pixel ratio or device type, and a device preset sets only the size. Screenshots from its menu are snapDOM's, as above, at the page's own pixel ratio. + - Input from the screencast is replayed as DOM events, which are not trusted (`isTrusted` is false): focus, clicks, scrolling and text editing are carried out, but pages that check `isTrusted` ignore them. + - Cross-origin style sheets are read through a fetched copy, which needs CORS headers; their rules are read-only. + - Forced pseudo-classes (`:hover`, `:focus`, … from the Styles pane) are emulated with companion style sheets that keep the cascade order; sheets from `@import` and `adoptedStyleSheets` are not covered. +- While the page has an element fullscreen (the Fullscreen API), DevTools and its F12 button cannot be used: the browser lets nothing outside the fullscreen element take input, in shell mode too. Leave fullscreen (Esc) to use them again. +- Host the frontend on a different origin from the page. On the same origin, DevTools' own settings share the page's `localStorage` and show up in the Application panel. +- The page side uses only standard web APIs and works in any modern browser. The DevTools UI is developed and tested by Google for Chrome; Firefox and Safari are not verified yet, and newer frontend versions may need extra compatibility patches. + +## Packaging + +An umbrella package plus scoped packages under the [`@devtools-anywhere`](https://www.npmjs.com/org/devtools-anywhere) npm organization: + +| Package | Contents | +|---|---| +| `devtools-anywhere` | Umbrella package: `probe.iife.js` (probe plus auto-mounting), `devtools-anywhere.iife.js` (the `<devtools-anywhere>` element, which the probe loads), `devtools-anywhere-service-worker.js`, `snapdom.js` (the renderer of the screencast and of screenshots, loaded on demand), and TypeScript types for the element, its events and the script and toggler attributes. There is no module to import: the scripts are the runtime | +| `@devtools-anywhere/devtools-frontend` | Prebuilt frontend static assets with English and Chinese UI, self-hostable or served from a CDN | +| `@devtools-anywhere/devtools-frontend-locales` | Optional: the other 80 UI languages, at the same paths as in the frontend package. Serve both from one directory (the Vite plugin does this when it is installed); a missing language falls back to English | +| `@devtools-anywhere/vite-plugin` | Vite plugin | +| `@devtools-anywhere/remote-server` | Remote debugging server (Node.js) and its CLI, `devtools-anywhere-remote` | + +## Design + +How it works, and why it is built this way: the probe, shell mode, injection, the technology choices and the packaging are described in [docs/design.md](docs/design.md). + ## Development See the [development guide](DEV.md) for setting up the environment, the repository layout and implementation rules, live rebuilds, tests, building the frontend, and releasing. diff --git a/README.zh-CN.md b/README.zh-CN.md index 8c93db1..6238f89 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -21,104 +21,6 @@ 3. **刷新不丢 devtools**:页面刷新或跳转时,devtools 保持打开,日志可以持续保留。 4. **持续跟进新版 Chrome DevTools**:基于官方 devtools-frontend 维护一组补丁,定期升级,不长期停留在旧版本。 -## 设计 - -### 选型 - -| 部分 | 选择 | 理由 | -|---|---|---| -| CDP 实现 | 在本项目中自行实现,参考 [chobitsu](https://github.com/liriliri/chobitsu) | 在页面内调试只能通过 JS API 采集数据,所以只能用 JS/TS 实现。自行实现可以让探针和各个 CDP 域共用同一套 hook(不再需要回放技巧),修掉 chobitsu 的已知问题,并通过 [`devtools-protocol`](https://github.com/ChromeDevTools/devtools-protocol) 的类型跟进最新协议 | -| 前端 | 官方 devtools-frontend,从源码构建,加一个很小的补丁集 | 构建时钉住官方的某个版本,补丁以 `.patch` 文件维护,升级时逐个重放,不维护长期 fork 分支。补丁只负责注册我们自己的入口(`anywhere_app`),其余定制都不改上游代码(见[修改前端](DEV.zh-CN.md#修改前端)) | -| 隔离 | iframe | 隔离 devtools 自身的 DOM、日志和请求,避免它们出现在被调试页面中,也避免与页面的全局环境互相干扰 | - -本项目不依赖 Chii 和 chobitsu。遇到边界情况时会阅读它们的源码作为参考,但不复制代码;以真实 Chrome 的 CDP 输出和协议定义为准。 - -### 能力边界 - -- 页面内的 CDP 实现只能基于 JS API,因此以下能力**无法提供**:断点调试、Profiler、Memory、真实的网络时序。这些面板不打包进前端,而不是展示给用户后再让他发现不能用。页面脚本里的 `debugger;` 语句同样无法暂停;在外壳模式下,同源脚本中执行到的每个 `debugger;` 会改为在控制台输出一条带堆栈的警告。真正的断点要靠浏览器自带的调试器(Android 用 `chrome://inspect`,iOS 用 Safari 的 Web Inspector);[远程调试](#远程调试)改变不了这一点,因为页面里运行的仍是同一个页面内 CDP agent。源代码面板的“网页”标签页能列出页面的文档、外链脚本和样式表、图片和字体(内联脚本不列出,因为拿不到它们在文档中的位置);代码段可以用。 -- 页面脚本缺少相应 API 的地方,要么精确补上,要么不做;不会把近似结果当作事实展示: - - 不显示浏览器默认样式(user-agent styles):浏览器不开放它的默认样式表,内置一份副本又可能和浏览器实际使用的不一致。 - - 事件监听器由探针从它执行的那一刻起记录(探针必须是第一个脚本);拿不到处理函数的源码位置。 - - 能显示 WebSocket 连接和帧,但没有握手阶段的请求头和响应头,页面拿不到它们。 - - 投屏(工具栏上的设备按钮,默认关闭)显示的是 [snapDOM](https://github.com/zumerlab/snapdom) 根据 DOM 重新绘制的视口,不是屏幕上的真实像素:canvas、视频和跨域图片可能缺失,每秒最多更新约两次。snapDOM(gzip 后约 90 KB)只在开始投屏或截图时加载。 - - 截图(`Page.captureScreenshot`,供通过 CDP 操作页面的工具和 agent 使用)同样由 snapDOM 绘制,投屏缺失的内容截图里也会缺失。返回结果里会多一个 Chrome 没有的字段 `approximate: true` 来说明这一点。其余参数的行为和 Chrome 一致:`clip` 按页面坐标计算,图片按设备像素输出(`devicePixelRatio` 乘以 `clip.scale`),不带 `clip` 时设置 `captureBeyondViewport` 会截取整个页面。超出浏览器画布上限(单边 32767 像素,Safari 上 16384;面积 16384 × 16384)的图片会等比缩小,snapDOM 会在页面的控制台里给出警告。不支持编码 WebP 的浏览器(Safari)在请求 WebP 时返回 PNG。 - - 设备模式(只在外壳模式下提供,因为它要求页面在一个可以调整尺寸的 iframe 里)设置的是页面的尺寸,也就是页面看到的视口:媒体查询、`vw`/`vh` 和 `innerWidth` 都会随之变化。它不改变 `devicePixelRatio`、UA 和触摸输入,这些是页面改不了的:工具栏里没有设备像素比和设备类型,选择设备预设时只套用尺寸。它菜单里的截图同样由 snapDOM 绘制,按页面自己的像素比输出。 - - 投屏上的输入以 DOM 事件重放,这些事件不是可信事件(`isTrusted` 为 false):聚焦、点击、滚动和文本编辑都会执行,但检查 `isTrusted` 的页面会忽略它们。 - - 跨域样式表通过重新下载的副本读取,需要对方返回 CORS 头;这些规则是只读的。 - - 强制伪类(样式面板里的 `:hover`、`:focus` 等)通过伴随样式表模拟,层叠顺序保持不变;`@import` 引入的样式表和 `adoptedStyleSheets` 不在覆盖范围内。 -- 页面让某个元素全屏(Fullscreen API)期间,devtools 和它的 F12 按钮都无法使用:浏览器不让全屏元素以外的任何内容接收输入,外壳模式下也一样。退出全屏(Esc)后即可恢复。 -- 前端要部署在和页面不同的源上。如果同源,devtools 自己的设置会写进页面的 `localStorage`,并出现在 Application 面板里。 -- 被调试页面一侧只用标准 Web API,可以运行在任何现代浏览器中。devtools 界面由 Google 针对 Chrome 开发和测试,Firefox 和 Safari 尚未验证;新版前端可能需要额外的兼容补丁。 - -### 架构 - -``` -┌──────────── 顶层窗口(外壳)─────────────┐ -│ ┌──────────────────────────────────┐ │ -│ │ 用户页面 iframe │ │ -│ │ 探针(<head> 首个同步脚本) │ │ -│ │ └─ 缓冲 → CDP 运行时(延迟加载) │ │ -│ └──────────────┬───────────────────┘ │ -│ postMessage(校验 origin) │ -│ ┌──────────────┴───────────────────┐ │ -│ │ devtools iframe │ │ -│ └──────────────────────────────────┘ │ -└────────────────────────────────────────┘ -``` - -#### 1. 探针 - -压缩后约 6.2 KB(min+gzip,包含外壳模式、自动挂载和远程调试)的同步脚本,必须是 `<head>` 中第一个执行的脚本。 - -- 立即 hook `console`、`window.onerror`、`unhandledrejection`、`fetch`、`XHR` 和 `WebSocket`,把事件连同时间戳写入缓冲。WebSocket 帧按原样保存,二进制负载到 CDP agent 才转成 base64。CDP 会话挂载之前,每类记录(控制台消息、请求、WebSocket、每个 WebSocket 的帧)各保留最近 1 万条,作为从不加载 CDP agent 的页面的兜底;CDP 会话挂载后改由它设定上限,控制台消息与 V8 一样保留最近 1000 条。 -- 无法 hook 的请求(文档、样式表、脚本、图片、字体等)通过 Resource Timing 记录;带 `buffered` 的 `PerformanceObserver` 还能拿到探针运行之前的请求。这些记录有 URL、类型、时序、大小,以及浏览器公开时的状态码和 MIME 类型,但没有请求头和响应头;响应体在需要时从 HTTP 缓存重新读取。外壳模式下启用了 Service Worker 时,它会为同源请求补上请求头和响应头。之所以仍以 hook 为主:hook 从最开始就生效、带调用栈、无需部署;Service Worker 要安装后才能接管页面,会和网站自己的 Service Worker 冲突,而且要代理请求才能看到响应。 -- CDP 运行时和前端延迟加载。运行时的 Runtime、Network 域直接读取探针的缓冲并订阅新事件,因此每个 API 只被 hook 一次。 - -页面外没有任何途径能观察页面内的输出,只有比业务代码更早执行的脚本才能捕获最早的输出。完整的 CDP 运行时体积太大,不适合同步加载,所以只把最小的捕获逻辑放在最前面。 - -这个方案已经验证可行,见 [experiments/early-capture/RESULTS.md](experiments/early-capture/RESULTS.md):用约 1.4 KB 的探针加延迟加载的 chobitsu,所有早期的 console 调用、异常和请求都进入了 devtools,对象可以展开,时间戳和调用栈都是原始的。 - -#### 2. 外壳模式(原地改写) - -不托管独立的 `devtools.html`,也不跳转到 blob URL,因为 blob 会导致刷新失效、地址无法分享。做法是: - -1. 使用零配置探针(`data-devtools-frontend-url`,或 Vite 插件)时,顶层页面首次加载就默认进入外壳模式:探针在页面脚本运行前自己把前端地址和 bundle 地址写入 `sessionStorage`,不需要再刷新一次。除非设置了 `data-open-immediately`,devtools 一开始是隐藏的。其他情况下,点击 DevTools 工具栏上的锁形按钮“Keep DevTools open across reloads”,写入这些地址后刷新页面。 -2. 探针作为第一个脚本,在顶层窗口检测到配置后,先写入一个不闭合的 `<plaintext>`,让页面剩余的内容变成纯文本(页面脚本都不会执行),再用 `document.open()` / `document.write()` 把文档替换成外壳。外壳中有指向 `location.href` 的用户页面 iframe,以及 `<devtools-anywhere>`,它凭所在的文档认出自己是外壳的元素。(Chrome 在 `window.stop()` 之后会忽略 `document.open()`,所以改用 `<plaintext>`。) -3. 在 iframe 中,探针照常捕获,并加载 bundle,把页面的 CDP 会话连接到外壳。外壳在页面和前端之间转发消息。 -4. 页面刷新或跳转后,新页面会发出 hello;外壳通知前端页面已导航,并把前端发过的所有 `enable` 命令重新发给新页面,新页面就会回放它自己最早的输出。地址栏和标题跟随页面变化,包括加载、前进后退和同文档导航(有 Navigation API 时也包括 `pushState`),因此刷新或复制链接打开的都是当前显示的页面。 -5. DevTools 铺满外壳,由它来排布页面和自己,和 Chrome 一样:页面 iframe 放在前端的页面区域上,叠在上层。停靠位置和尺寸由 DevTools 自己管理(用它的分隔条;触屏上,页面和 DevTools 的分界处有一条更宽的拖动条)。取消停靠时,DevTools 是页面上方的浮动窗口。工具栏上的设备按钮是设备模式,和 Chrome 一样,取代了投屏:页面 iframe 按设备尺寸设置并缩放到适合窗口,工具栏的菜单可以截图。取消停靠时,设备模式的工具栏画在前端的一个页面(`device_mode_emulation_frame.html`,Chrome 也是这么做的)里,显示在浮动窗口下方。 -6. 点工具栏上的 × 只隐藏 devtools,仍留在外壳模式;在重新显示 devtools 之前,页面按自己的尺寸铺满外壳。再点一次锁形按钮会清除配置,并在普通模式下重新加载页面;这个选择会记在 `localStorage` 里,之后优先于页面的 `data-default-shell-mode`,除非页面设置了 `data-shell-mode-locked`。 - -只有与探针脚本同源的地址才会被采信,否则清除配置、页面照常加载。因此外壳模式要求探针、bundle 和前端都由页面所在的源提供。 - -页面放进 iframe 后,`viewport`、`100vh`、`position: fixed`、`window.top` 等的行为可能和直接打开时不同;页面的 HTML 会被请求两次(顶层窗口一次,iframe 一次),表单 POST 返回的页面会被用 GET 再请求一次。用 `data-default-shell-mode="off"`(Vite 插件的 `defaultShellMode: 'off'`)可以关闭外壳模式,改为把 devtools 直接嵌在页面中。拒绝被嵌入(`X-Frame-Options`、CSP `frame-ancestors`)或反复把外壳跳走的页面,在本会话剩下的时间里会单独显示。 - -#### 3. 注入方式 - -| 方式 | 适用场景 | -|---|---| -| 页面手动引入 `<script>`(必须放在 `<head>` 第一个) | 任意页面 | -| `@devtools-anywhere/vite-plugin`:只在开发模式(`vite serve`)下注入 | 使用 Vite 的项目(之后可能补充 webpack 插件) | -| `devtools-anywhere-service-worker.js`:外壳模式下,给页面 iframe 中加载的、没有引入探针的同源页面注入探针 | 安全上下文(HTTPS 或 localhost)、作用域覆盖这些页面(`Service-Worker-Allowed: /`),并且站点没有自己的 Service Worker | - -所有方式共用同一份探针。 - -### 发布形式 - -发布一个总包,以及 npm 组织 [`@devtools-anywhere`](https://www.npmjs.com/org/devtools-anywhere) 下的几个子包: - -| 包 | 内容 | -|---|---| -| `devtools-anywhere` | 总包:`probe.iife.js`(探针加自动挂载)、`devtools-anywhere.iife.js`(`<devtools-anywhere>` 组件,由探针加载)、`devtools-anywhere-service-worker.js`、`snapdom.js`(投屏和截图的绘制器,按需加载),以及组件、事件、探针脚本和切换器属性的 TypeScript 类型。没有可以 import 的模块,运行时只有这些脚本 | -| `@devtools-anywhere/devtools-frontend` | 构建好的前端静态资源,界面带英文和中文,可以自托管,也可以从 CDN 引用 | -| `@devtools-anywhere/devtools-frontend-locales` | 可选:其余 80 种界面语言,文件路径与前端包一致。两个包放在同一目录下提供即可(装了它的话 Vite 插件会自动这样做);缺少的语言会退回英文 | -| `@devtools-anywhere/vite-plugin` | Vite 插件 | -| `@devtools-anywhere/remote-server` | 远程调试服务端(Node.js)及其命令行工具 `devtools-anywhere-remote` | - -- 页面内的 CDP agent 和探针不单独成包:探针和各个域共用同一套 hook 和缓冲,而且只有 `devtools-anywhere` 使用它们。它们放在 `devtools-anywhere` 中(`src/cdp-agent/`),不会出现版本不匹配;探针仍单独构建成一个很小的文件 `probe.iife.js`。 -- 前端单独成包,因为它体积大(约 16 MB),更新节奏跟随 Chrome 版本。其他语言(约 42 MB)再拆成一个包,需要的人才装。 -- 所有包使用同一个版本号、一起发布,版本号相同的包一定能配合使用。 - ## 用法(预览) 各个包已发布到 npm(`npm i devtools-anywhere @devtools-anywhere/devtools-frontend`),下面的路径指它们的 `dist/` 目录,本仓库构建出的文件也一样。`data-devtools-frontend-url` 指向 `@devtools-anywhere/devtools-frontend` 构建产物里的 `anywhere_app.html`。产物里 `bundle/` 下的文件以内容命名,可以永久缓存;`anywhere_app.html` 本身要带重新验证提供(`Cache-Control: no-cache`),更新才能到达浏览器。 @@ -133,7 +35,7 @@ </head> ``` -也可以直接用 CDN:jsDelivr 和 unpkg 会提供所有 npm 包,不用自己部署。请固定到确切的版本号,两个包用同一个版本。这种方式只有英文和中文界面,因为其他语言在单独的包里,必须和前端放在同一目录下提供。外壳模式也用不了,因为它要求探针、bundle 和前端都与页面同源(见[外壳模式](#2-外壳模式原地改写)):用 `data-default-shell-mode="off"` 把 devtools 直接嵌在页面里。 +也可以直接用 CDN:jsDelivr 和 unpkg 会提供所有 npm 包,不用自己部署。请固定到确切的版本号,两个包用同一个版本。这种方式只有英文和中文界面,因为其他语言在单独的包里,必须和前端放在同一目录下提供。外壳模式也用不了,因为它要求探针、bundle 和前端都与页面同源(见[外壳模式](docs/design.zh-CN.md#2-外壳模式原地改写)):用 `data-default-shell-mode="off"` 把 devtools 直接嵌在页面里。 ```html <!-- jsDelivr --> @@ -142,7 +44,7 @@ <script src="https://unpkg.com/devtools-anywhere@0.1.0/dist/probe.iife.js" data-devtools-frontend-url="https://unpkg.com/@devtools-anywhere/devtools-frontend@0.1.0/dist/anywhere_app.html" data-default-shell-mode="off"></script> ``` -页面加载完成后,探针会从同目录(或 `data-bundle` 指定的地址)加载 `devtools-anywhere.iife.js`,并在 `<html>` 下、与 `<body>` 同级挂载它自己的 `<devtools-anywhere>`,不管页面里有没有 `<devtools-anywhere>`;每个页面只有一个这样的全局元素,再出现的只在控制台警告。可选属性:`data-open-immediately` 立即打开 devtools;`data-default-shell-mode="off"` 不进入外壳模式(顶层页面默认 `on`,即进入);`data-shell-mode-locked` 锁定外壳模式,始终按 `data-default-shell-mode` 执行,忽略用户的选择(已存的选择保留)并隐藏 DevTools 里的开关,但页面禁止被嵌入 iframe 时仍会退回普通模式;`data-no-toggler` 隐藏 F12 按钮,外壳模式下同样生效;`data-service-worker` 在外壳模式下启用 Service Worker,它给页面 iframe 中没有引入探针的同源页面注入探针,并为同源的文档、样式表、脚本、图片等请求补上请求头和响应头;默认关闭,使用条件见[注入方式](#3-注入方式)。 +页面加载完成后,探针会从同目录(或 `data-bundle` 指定的地址)加载 `devtools-anywhere.iife.js`,并在 `<html>` 下、与 `<body>` 同级挂载它自己的 `<devtools-anywhere>`,不管页面里有没有 `<devtools-anywhere>`;每个页面只有一个这样的全局元素,再出现的只在控制台警告。可选属性:`data-open-immediately` 立即打开 devtools;`data-default-shell-mode="off"` 不进入外壳模式(顶层页面默认 `on`,即进入);`data-shell-mode-locked` 锁定外壳模式,始终按 `data-default-shell-mode` 执行,忽略用户的选择(已存的选择保留)并隐藏 DevTools 里的开关,但页面禁止被嵌入 iframe 时仍会退回普通模式;`data-no-toggler` 隐藏 F12 按钮,外壳模式下同样生效;`data-service-worker` 在外壳模式下启用 Service Worker,它给页面 iframe 中没有引入探针的同源页面注入探针,并为同源的文档、样式表、脚本、图片等请求补上请求头和响应头;默认关闭,使用条件见[注入方式](docs/design.zh-CN.md#3-注入方式)。 ### Vite @@ -155,7 +57,7 @@ export default { } ``` -只影响 `vite serve`:探针作为第一个脚本注入每个页面,bundle、Service Worker 和前端都在 `/@devtools-anywhere/` 下提供。前端默认来自 `@devtools-anywhere/devtools-frontend`。选项:`devtoolsFrontendPath`(本地另一份前端构建的入口文件路径,如 `…/anywhere_app.html`,托管它所在的目录)、`devtoolsFrontendUrl`(托管在别处的前端,设置后不再托管本地文件)、`openImmediately`、`defaultShellMode`(`'on'` 或 `'off'`)、`shellModeLocked`、`noToggler`(隐藏 F12 按钮)、`serviceWorker`(使用条件见[注入方式](#3-注入方式),`Service-Worker-Allowed` 头由插件返回),与探针的属性一一对应;`remote`([远程调试](#远程调试)服务端的地址,页面同时连接到它,组件照常挂载),以及配合它的 `idInQuery`(`data-id-in-query`,对应服务端的 `idInQuery`)。 +只影响 `vite serve`:探针作为第一个脚本注入每个页面,bundle、Service Worker 和前端都在 `/@devtools-anywhere/` 下提供。前端默认来自 `@devtools-anywhere/devtools-frontend`。选项:`devtoolsFrontendPath`(本地另一份前端构建的入口文件路径,如 `…/anywhere_app.html`,托管它所在的目录)、`devtoolsFrontendUrl`(托管在别处的前端,设置后不再托管本地文件)、`openImmediately`、`defaultShellMode`(`'on'` 或 `'off'`)、`shellModeLocked`、`noToggler`(隐藏 F12 按钮)、`serviceWorker`(使用条件见[注入方式](docs/design.zh-CN.md#3-注入方式),`Service-Worker-Allowed` 头由插件返回),与探针的属性一一对应;`remote`([远程调试](#远程调试)服务端的地址,页面同时连接到它,组件照常挂载),以及配合它的 `idInQuery`(`data-id-in-query`,对应服务端的 `idInQuery`)。 ### 远程调试 @@ -180,7 +82,7 @@ npx -p @devtools-anywhere/remote-server devtools-anywhere-remote --port 8080 ### 组件 -点击右下角的 F12 悬浮按钮打开面板;拖动面板的横条调整大小(外壳模式下用 DevTools 自己的分隔条,见[外壳模式](#2-外壳模式原地改写))。DevTools 设置按钮左边,锁形按钮开关外壳模式(开启前有对话框说明,开启后顶部有提示条),刷新按钮重新加载页面;工具栏上的 × 关闭面板。DevTools 主菜单(⋮)里有“停靠位置”:底部(默认)、右侧、左侧,或取消停靠,变成页面上方的浮动窗口,拖动横条移动、拖动右下角调整大小。外壳模式下页面会让出空间,和 Chrome 一样;普通模式下面板盖在页面上。停靠位置和尺寸会记在 localStorage 里;尺寸记的是占视口的百分比(`dvh`/`dvw`),换屏幕或横竖屏切换后依然合适。唯一的属性 `devtools-frontend-url` 是可选的,不写时沿用探针的 `data-devtools-frontend-url`。其余状态都由库维护:探针的参数决定组件的配置,打开和关闭由用户操作;组件没有自己的方法。页面可以用 `devtools-anywhere::part(toggler)` 修改 F12 按钮的样式,外壳模式下同样有效:此时按钮在页面 iframe 里,由页面 `<html>` 下一个只承载按钮的 `<devtools-anywhere>` 显示,点击开关外壳的 DevTools(只有 iframe 里的页面没有探针时,例如其他源的页面,外壳才显示自己的按钮)。探针挂载的组件不把它的 iframe 暴露为部件。组件要固定挂在一个位置,在 DOM 中移动它会导致 iframe 重新加载。 +点击右下角的 F12 悬浮按钮打开面板;拖动面板的横条调整大小(外壳模式下用 DevTools 自己的分隔条,见[外壳模式](docs/design.zh-CN.md#2-外壳模式原地改写))。DevTools 设置按钮左边,锁形按钮开关外壳模式(开启前有对话框说明,开启后顶部有提示条),刷新按钮重新加载页面;工具栏上的 × 关闭面板。DevTools 主菜单(⋮)里有“停靠位置”:底部(默认)、右侧、左侧,或取消停靠,变成页面上方的浮动窗口,拖动横条移动、拖动右下角调整大小。外壳模式下页面会让出空间,和 Chrome 一样;普通模式下面板盖在页面上。停靠位置和尺寸会记在 localStorage 里;尺寸记的是占视口的百分比(`dvh`/`dvw`),换屏幕或横竖屏切换后依然合适。唯一的属性 `devtools-frontend-url` 是可选的,不写时沿用探针的 `data-devtools-frontend-url`。其余状态都由库维护:探针的参数决定组件的配置,打开和关闭由用户操作;组件没有自己的方法。页面可以用 `devtools-anywhere::part(toggler)` 修改 F12 按钮的样式,外壳模式下同样有效:此时按钮在页面 iframe 里,由页面 `<html>` 下一个只承载按钮的 `<devtools-anywhere>` 显示,点击开关外壳的 DevTools(只有 iframe 里的页面没有探针时,例如其他源的页面,外壳才显示自己的按钮)。探针挂载的组件不把它的 iframe 暴露为部件。组件要固定挂在一个位置,在 DOM 中移动它会导致 iframe 重新加载。 页面也可以自己放置 `<devtools-anywhere>`,作为显示 DevTools 的一个区域:它是页面文档流里的块级盒子(`display: block`),尺寸由页面的 CSS 决定,一连接就加载 DevTools。页面用 CSS 或 `hidden` 控制它的显示和隐藏;隐藏时 DevTools 不会卸载。它没有 F12 按钮,不能停靠或拖拽调整大小,也不会进入外壳模式(DevTools 里没有入口);在外壳里的页面中照常工作。它只有 `devtools-frontend-url` 一个属性,其他属性一律忽略。里面的 DevTools iframe 可以用 `devtools-anywhere::part(iframe)` 设置样式。 @@ -192,6 +94,39 @@ npx -p @devtools-anywhere/remote-server devtools-anywhere-remote --port 8080 切换器在探针加载 `devtools-anywhere.iife.js`(由它注册组件)后才生效,放在 shadow root 里不起作用。外壳模式下,外壳里那个页面的切换器一律控制外壳的面板,忽略属性值。与外壳不同源的页面(如跨源 iframe)里的切换器不起作用。 +## 能力边界 + +- 页面内的 CDP 实现只能基于 JS API,因此以下能力**无法提供**:断点调试、Profiler、Memory、真实的网络时序。这些面板不打包进前端,而不是展示给用户后再让他发现不能用。页面脚本里的 `debugger;` 语句同样无法暂停;在外壳模式下,同源脚本中执行到的每个 `debugger;` 会改为在控制台输出一条带堆栈的警告。真正的断点要靠浏览器自带的调试器(Android 用 `chrome://inspect`,iOS 用 Safari 的 Web Inspector);[远程调试](#远程调试)改变不了这一点,因为页面里运行的仍是同一个页面内 CDP agent。源代码面板的“网页”标签页能列出页面的文档、外链脚本和样式表、图片和字体(内联脚本不列出,因为拿不到它们在文档中的位置);代码段可以用。 +- 页面脚本缺少相应 API 的地方,要么精确补上,要么不做;不会把近似结果当作事实展示: + - 不显示浏览器默认样式(user-agent styles):浏览器不开放它的默认样式表,内置一份副本又可能和浏览器实际使用的不一致。 + - 事件监听器由探针从它执行的那一刻起记录(探针必须是第一个脚本);拿不到处理函数的源码位置。 + - 能显示 WebSocket 连接和帧,但没有握手阶段的请求头和响应头,页面拿不到它们。 + - 投屏(工具栏上的设备按钮,默认关闭)显示的是 [snapDOM](https://github.com/zumerlab/snapdom) 根据 DOM 重新绘制的视口,不是屏幕上的真实像素:canvas、视频和跨域图片可能缺失,每秒最多更新约两次。snapDOM(gzip 后约 90 KB)只在开始投屏或截图时加载。 + - 截图(`Page.captureScreenshot`,供通过 CDP 操作页面的工具和 agent 使用)同样由 snapDOM 绘制,投屏缺失的内容截图里也会缺失。返回结果里会多一个 Chrome 没有的字段 `approximate: true` 来说明这一点。其余参数的行为和 Chrome 一致:`clip` 按页面坐标计算,图片按设备像素输出(`devicePixelRatio` 乘以 `clip.scale`),不带 `clip` 时设置 `captureBeyondViewport` 会截取整个页面。超出浏览器画布上限(单边 32767 像素,Safari 上 16384;面积 16384 × 16384)的图片会等比缩小,snapDOM 会在页面的控制台里给出警告。不支持编码 WebP 的浏览器(Safari)在请求 WebP 时返回 PNG。 + - 设备模式(只在外壳模式下提供,因为它要求页面在一个可以调整尺寸的 iframe 里)设置的是页面的尺寸,也就是页面看到的视口:媒体查询、`vw`/`vh` 和 `innerWidth` 都会随之变化。它不改变 `devicePixelRatio`、UA 和触摸输入,这些是页面改不了的:工具栏里没有设备像素比和设备类型,选择设备预设时只套用尺寸。它菜单里的截图同样由 snapDOM 绘制,按页面自己的像素比输出。 + - 投屏上的输入以 DOM 事件重放,这些事件不是可信事件(`isTrusted` 为 false):聚焦、点击、滚动和文本编辑都会执行,但检查 `isTrusted` 的页面会忽略它们。 + - 跨域样式表通过重新下载的副本读取,需要对方返回 CORS 头;这些规则是只读的。 + - 强制伪类(样式面板里的 `:hover`、`:focus` 等)通过伴随样式表模拟,层叠顺序保持不变;`@import` 引入的样式表和 `adoptedStyleSheets` 不在覆盖范围内。 +- 页面让某个元素全屏(Fullscreen API)期间,devtools 和它的 F12 按钮都无法使用:浏览器不让全屏元素以外的任何内容接收输入,外壳模式下也一样。退出全屏(Esc)后即可恢复。 +- 前端要部署在和页面不同的源上。如果同源,devtools 自己的设置会写进页面的 `localStorage`,并出现在 Application 面板里。 +- 被调试页面一侧只用标准 Web API,可以运行在任何现代浏览器中。devtools 界面由 Google 针对 Chrome 开发和测试,Firefox 和 Safari 尚未验证;新版前端可能需要额外的兼容补丁。 + +## 发布形式 + +发布一个总包,以及 npm 组织 [`@devtools-anywhere`](https://www.npmjs.com/org/devtools-anywhere) 下的几个子包: + +| 包 | 内容 | +|---|---| +| `devtools-anywhere` | 总包:`probe.iife.js`(探针加自动挂载)、`devtools-anywhere.iife.js`(`<devtools-anywhere>` 组件,由探针加载)、`devtools-anywhere-service-worker.js`、`snapdom.js`(投屏和截图的绘制器,按需加载),以及组件、事件、探针脚本和切换器属性的 TypeScript 类型。没有可以 import 的模块,运行时只有这些脚本 | +| `@devtools-anywhere/devtools-frontend` | 构建好的前端静态资源,界面带英文和中文,可以自托管,也可以从 CDN 引用 | +| `@devtools-anywhere/devtools-frontend-locales` | 可选:其余 80 种界面语言,文件路径与前端包一致。两个包放在同一目录下提供即可(装了它的话 Vite 插件会自动这样做);缺少的语言会退回英文 | +| `@devtools-anywhere/vite-plugin` | Vite 插件 | +| `@devtools-anywhere/remote-server` | 远程调试服务端(Node.js)及其命令行工具 `devtools-anywhere-remote` | + +## 设计 + +工作原理和设计取舍(探针、外壳模式、注入方式、选型、拆包理由)见 [docs/design.zh-CN.md](docs/design.zh-CN.md)。 + ## 开发 环境配置、目录结构与实现约束、实时重新构建、测试、构建前端和发布,见[开发指南](DEV.zh-CN.md)。 diff --git a/docs/design.md b/docs/design.md new file mode 100644 index 0000000..fe0c2c8 --- /dev/null +++ b/docs/design.md @@ -0,0 +1,76 @@ +# Design + +English | [简体中文](design.zh-CN.md) + +How devtools-anywhere works and why it is built this way. For using it, see the [README](../README.md); for working on it, the [development guide](../DEV.md). + +## Technology choices + +| Part | Choice | Rationale | +|---|---|---| +| CDP implementation | Written in this project, using [chobitsu](https://github.com/liriliri/chobitsu) as a reference | In-page debugging can only collect data through JS APIs, so it has to be written in JS/TS. Owning it lets the probe and the CDP domains share the same hooks (no replay tricks), fixes chobitsu's known issues, and tracks the latest protocol through [`devtools-protocol`](https://github.com/ChromeDevTools/devtools-protocol) types | +| Frontend | Official devtools-frontend, built from source with a small patch set | The build pins an official revision and keeps patches as `.patch` files that are replayed on upgrade, instead of maintaining a long-lived fork branch. The patches only register an own entrypoint (`anywhere_app`); everything else is done without touching upstream code (see [Working on the frontend](../DEV.md#working-on-the-frontend)) | +| Isolation | iframe | Keeps DevTools' own DOM, logs and requests out of the inspected page, and prevents the two global environments from interfering with each other | + +This project depends on neither Chii nor chobitsu. Their source is read as a reference for edge cases, but no code is copied; real Chrome's CDP output and the protocol definition are the source of truth. + +## Architecture + +``` +┌────────── Top-level window (shell) ──────────┐ +│ ┌────────────────────────────────────────┐ │ +│ │ Page iframe │ │ +│ │ Probe (first sync script in <head>) │ │ +│ │ └─ buffer → CDP runtime (lazy) │ │ +│ └───────────────────┬────────────────────┘ │ +│ postMessage (origin-checked) │ +│ ┌───────────────────┴────────────────────┐ │ +│ │ DevTools iframe │ │ +│ └────────────────────────────────────────┘ │ +└──────────────────────────────────────────────┘ +``` + +### 1. Probe + +A synchronous script of about 6.2 KB (min+gzip, including shell mode, auto-mounting and remote debugging) that must be the first script executed in `<head>`. + +- Immediately hooks `console`, `window.onerror`, `unhandledrejection`, `fetch`, `XHR` and `WebSocket`, and writes events with timestamps into a buffer. WebSocket frames are kept as given; binary payloads are only converted to base64 by the CDP agent. Until a CDP session mounts, each list (console messages, requests, WebSockets, frames per WebSocket) keeps its last 10,000 entries, a bound for pages that never load one; the session then sets its own: it keeps the last 1000 console messages like V8, and every request and WebSocket like Chrome, dropping only the oldest response bodies past the buffer sizes given to `Network.enable` (Chrome's defaults when none are). +- Requests that cannot be hooked (the document, style sheets, scripts, images, fonts, …) are recorded from Resource Timing; a buffered `PerformanceObserver` also delivers those from before the probe ran. They have URL, type, timing, size and (where the browser exposes it) status and MIME type, but no headers; their bodies are loaded again from the HTTP cache on demand. In shell mode with the Service Worker, it adds the request and response headers of same-origin ones. Hooks stay first because they work from the very start, keep call stacks, and need no deployment; a Service Worker only controls pages after it is installed, conflicts with a site's own, and would have to proxy requests to see responses. +- The CDP runtime and the frontend are loaded lazily. The runtime's Runtime and Network domains consume the probe's buffer directly and subscribe to new events, so every API is hooked only once. + +Nothing outside a page can observe its output; only a script that runs before the page's own code can capture the earliest output. The full CDP runtime is too large to load synchronously, so only the minimal capture logic goes first. + +This approach has been validated: see [experiments/early-capture/RESULTS.md](../experiments/early-capture/RESULTS.md). With a ~1.4 KB probe and chobitsu loaded lazily, every early console call, exception and request reached DevTools, with expandable objects and original timestamps and stacks. + +### 2. Shell mode (in-place rewrite) + +No standalone `devtools.html` is hosted, and there is no navigation to a blob URL, since blob URLs break on reload and cannot be shared. Instead: + +1. With the zero-config probe (`data-devtools-frontend-url`, or the Vite plugin), the top-level page enters shell mode by default on its first load: the probe stores the frontend and bundle URLs in `sessionStorage` itself, before the page's scripts run, so the page is not reloaded. DevTools starts hidden unless `data-open-immediately` is set. Otherwise, the lock button in DevTools' toolbar, "Keep DevTools open across reloads", stores them and reloads the page. +2. The probe, being the first script, sees the config in the top-level window. It writes an unclosed `<plaintext>` so the rest of the page becomes inert text (none of its scripts run), then replaces the document with the shell via `document.open()` / `document.write()`. The shell contains the page iframe (pointing to `location.href`) and `<devtools-anywhere>`, which knows it is the shell's element by the document it is in. (Chrome ignores `document.open()` after `window.stop()`, hence `<plaintext>`.) +3. Inside the iframe, the probe captures as usual and loads the bundle, which connects the page's CDP session to the shell. The shell relays messages between the page and the frontend. +4. When the page reloads or navigates, the new page says hello; the shell tells the frontend the page navigated and re-sends every `enable` command the frontend has sent, so the new page replays its own earliest output. The address bar and title follow the page, through loads, history traversals and same-document navigations (`pushState` where the Navigation API exists), so that a reload or a copied link opens the page shown. +5. DevTools fills the shell and lays out the page and itself, as in Chrome: the page iframe goes where the frontend's page area is, on top of it. Dock side and sizes are DevTools' own (its resizer; on touch screens, a wider handle on the edge between the page and DevTools). Undocked, DevTools is a window over the page. The toolbar's device button is device mode, as in Chrome, in place of the screencast: the page iframe is sized as the device and scaled to fit, and the toolbar's menu takes screenshots. Undocked, device mode's toolbar is drawn in a page of the frontend's (`device_mode_emulation_frame.html`, as Chrome does) shown under the window. +6. The toolbar's × hides DevTools and stays in shell mode; the page fills the shell at its own size until DevTools is shown again. The lock button again clears the config and reloads the page on its own; the choice is remembered in `localStorage`, and wins over the page's `data-default-shell-mode` from then on, unless the page sets `data-shell-mode-locked`. + +The stored URLs are only trusted when they have the same origin as the probe script; otherwise the config is cleared and the page loads normally. Shell mode therefore needs the probe, the bundle and the frontend served from the page's origin. + +Inside an iframe, `viewport`, `100vh`, `position: fixed`, `window.top` and others may behave differently from a top-level page; the page's HTML is requested twice (by the top-level window, then by the iframe), and a page returned for a form POST is requested again with GET. Turn shell mode off with `data-default-shell-mode="off"` (the Vite plugin's `defaultShellMode: 'off'`), and DevTools is embedded directly in the page. A page that refuses to be framed (`X-Frame-Options`, CSP `frame-ancestors`), or that keeps navigating the shell away, is shown on its own for the rest of the session. + +### 3. Injection + +| Method | When to use | +|---|---| +| Add a `<script>` manually (must be the first one in `<head>`) | Any page | +| `@devtools-anywhere/vite-plugin`: injects in development only (`vite serve`) | Projects using Vite (a webpack plugin may follow) | +| `devtools-anywhere-service-worker.js`: in shell mode, injects the probe into same-origin pages loaded in the page iframe that do not include it | Secure context (HTTPS or localhost), a scope covering the pages (`Service-Worker-Allowed: /`), and no Service Worker of the site's own | + +All methods share the same probe. + +## Packaging + +Why the packages listed in the [README](../README.md#packaging) are split this way: + +- The in-page CDP agent and its probe are not a separate package: the probe and the domains share the same hooks and buffers, and only `devtools-anywhere` uses them. They live in `devtools-anywhere` (`src/cdp-agent/`), which rules out version mismatches; the probe is still built as a separate tiny file, `probe.iife.js`. +- The frontend is separate because it is large (about 16 MB) and follows Chrome's release cadence. Its other languages (about 42 MB) are another package, so that only those who need them install them. +- All packages share one version number and are released together, so matching versions always work together. diff --git a/docs/design.zh-CN.md b/docs/design.zh-CN.md new file mode 100644 index 0000000..d7bc576 --- /dev/null +++ b/docs/design.zh-CN.md @@ -0,0 +1,76 @@ +# 设计 + +[English](design.md) | 简体中文 + +devtools-anywhere 的工作原理和设计取舍。使用方法见 [README](../README.zh-CN.md),参与开发见[开发指南](../DEV.zh-CN.md)。 + +## 选型 + +| 部分 | 选择 | 理由 | +|---|---|---| +| CDP 实现 | 在本项目中自行实现,参考 [chobitsu](https://github.com/liriliri/chobitsu) | 在页面内调试只能通过 JS API 采集数据,所以只能用 JS/TS 实现。自行实现可以让探针和各个 CDP 域共用同一套 hook(不再需要回放技巧),修掉 chobitsu 的已知问题,并通过 [`devtools-protocol`](https://github.com/ChromeDevTools/devtools-protocol) 的类型跟进最新协议 | +| 前端 | 官方 devtools-frontend,从源码构建,加一个很小的补丁集 | 构建时钉住官方的某个版本,补丁以 `.patch` 文件维护,升级时逐个重放,不维护长期 fork 分支。补丁只负责注册我们自己的入口(`anywhere_app`),其余定制都不改上游代码(见[修改前端](../DEV.zh-CN.md#修改前端)) | +| 隔离 | iframe | 隔离 devtools 自身的 DOM、日志和请求,避免它们出现在被调试页面中,也避免与页面的全局环境互相干扰 | + +本项目不依赖 Chii 和 chobitsu。遇到边界情况时会阅读它们的源码作为参考,但不复制代码;以真实 Chrome 的 CDP 输出和协议定义为准。 + +## 架构 + +``` +┌──────────── 顶层窗口(外壳)─────────────┐ +│ ┌──────────────────────────────────┐ │ +│ │ 用户页面 iframe │ │ +│ │ 探针(<head> 首个同步脚本) │ │ +│ │ └─ 缓冲 → CDP 运行时(延迟加载) │ │ +│ └──────────────┬───────────────────┘ │ +│ postMessage(校验 origin) │ +│ ┌──────────────┴───────────────────┐ │ +│ │ devtools iframe │ │ +│ └──────────────────────────────────┘ │ +└────────────────────────────────────────┘ +``` + +### 1. 探针 + +压缩后约 6.2 KB(min+gzip,包含外壳模式、自动挂载和远程调试)的同步脚本,必须是 `<head>` 中第一个执行的脚本。 + +- 立即 hook `console`、`window.onerror`、`unhandledrejection`、`fetch`、`XHR` 和 `WebSocket`,把事件连同时间戳写入缓冲。WebSocket 帧按原样保存,二进制负载到 CDP agent 才转成 base64。CDP 会话挂载之前,每类记录(控制台消息、请求、WebSocket、每个 WebSocket 的帧)各保留最近 1 万条,作为从不加载 CDP agent 的页面的兜底;CDP 会话挂载后改由它设定上限,控制台消息与 V8 一样保留最近 1000 条。 +- 无法 hook 的请求(文档、样式表、脚本、图片、字体等)通过 Resource Timing 记录;带 `buffered` 的 `PerformanceObserver` 还能拿到探针运行之前的请求。这些记录有 URL、类型、时序、大小,以及浏览器公开时的状态码和 MIME 类型,但没有请求头和响应头;响应体在需要时从 HTTP 缓存重新读取。外壳模式下启用了 Service Worker 时,它会为同源请求补上请求头和响应头。之所以仍以 hook 为主:hook 从最开始就生效、带调用栈、无需部署;Service Worker 要安装后才能接管页面,会和网站自己的 Service Worker 冲突,而且要代理请求才能看到响应。 +- CDP 运行时和前端延迟加载。运行时的 Runtime、Network 域直接读取探针的缓冲并订阅新事件,因此每个 API 只被 hook 一次。 + +页面外没有任何途径能观察页面内的输出,只有比业务代码更早执行的脚本才能捕获最早的输出。完整的 CDP 运行时体积太大,不适合同步加载,所以只把最小的捕获逻辑放在最前面。 + +这个方案已经验证可行,见 [experiments/early-capture/RESULTS.md](../experiments/early-capture/RESULTS.md):用约 1.4 KB 的探针加延迟加载的 chobitsu,所有早期的 console 调用、异常和请求都进入了 devtools,对象可以展开,时间戳和调用栈都是原始的。 + +### 2. 外壳模式(原地改写) + +不托管独立的 `devtools.html`,也不跳转到 blob URL,因为 blob 会导致刷新失效、地址无法分享。做法是: + +1. 使用零配置探针(`data-devtools-frontend-url`,或 Vite 插件)时,顶层页面首次加载就默认进入外壳模式:探针在页面脚本运行前自己把前端地址和 bundle 地址写入 `sessionStorage`,不需要再刷新一次。除非设置了 `data-open-immediately`,devtools 一开始是隐藏的。其他情况下,点击 DevTools 工具栏上的锁形按钮“Keep DevTools open across reloads”,写入这些地址后刷新页面。 +2. 探针作为第一个脚本,在顶层窗口检测到配置后,先写入一个不闭合的 `<plaintext>`,让页面剩余的内容变成纯文本(页面脚本都不会执行),再用 `document.open()` / `document.write()` 把文档替换成外壳。外壳中有指向 `location.href` 的用户页面 iframe,以及 `<devtools-anywhere>`,它凭所在的文档认出自己是外壳的元素。(Chrome 在 `window.stop()` 之后会忽略 `document.open()`,所以改用 `<plaintext>`。) +3. 在 iframe 中,探针照常捕获,并加载 bundle,把页面的 CDP 会话连接到外壳。外壳在页面和前端之间转发消息。 +4. 页面刷新或跳转后,新页面会发出 hello;外壳通知前端页面已导航,并把前端发过的所有 `enable` 命令重新发给新页面,新页面就会回放它自己最早的输出。地址栏和标题跟随页面变化,包括加载、前进后退和同文档导航(有 Navigation API 时也包括 `pushState`),因此刷新或复制链接打开的都是当前显示的页面。 +5. DevTools 铺满外壳,由它来排布页面和自己,和 Chrome 一样:页面 iframe 放在前端的页面区域上,叠在上层。停靠位置和尺寸由 DevTools 自己管理(用它的分隔条;触屏上,页面和 DevTools 的分界处有一条更宽的拖动条)。取消停靠时,DevTools 是页面上方的浮动窗口。工具栏上的设备按钮是设备模式,和 Chrome 一样,取代了投屏:页面 iframe 按设备尺寸设置并缩放到适合窗口,工具栏的菜单可以截图。取消停靠时,设备模式的工具栏画在前端的一个页面(`device_mode_emulation_frame.html`,Chrome 也是这么做的)里,显示在浮动窗口下方。 +6. 点工具栏上的 × 只隐藏 devtools,仍留在外壳模式;在重新显示 devtools 之前,页面按自己的尺寸铺满外壳。再点一次锁形按钮会清除配置,并在普通模式下重新加载页面;这个选择会记在 `localStorage` 里,之后优先于页面的 `data-default-shell-mode`,除非页面设置了 `data-shell-mode-locked`。 + +只有与探针脚本同源的地址才会被采信,否则清除配置、页面照常加载。因此外壳模式要求探针、bundle 和前端都由页面所在的源提供。 + +页面放进 iframe 后,`viewport`、`100vh`、`position: fixed`、`window.top` 等的行为可能和直接打开时不同;页面的 HTML 会被请求两次(顶层窗口一次,iframe 一次),表单 POST 返回的页面会被用 GET 再请求一次。用 `data-default-shell-mode="off"`(Vite 插件的 `defaultShellMode: 'off'`)可以关闭外壳模式,改为把 devtools 直接嵌在页面中。拒绝被嵌入(`X-Frame-Options`、CSP `frame-ancestors`)或反复把外壳跳走的页面,在本会话剩下的时间里会单独显示。 + +### 3. 注入方式 + +| 方式 | 适用场景 | +|---|---| +| 页面手动引入 `<script>`(必须放在 `<head>` 第一个) | 任意页面 | +| `@devtools-anywhere/vite-plugin`:只在开发模式(`vite serve`)下注入 | 使用 Vite 的项目(之后可能补充 webpack 插件) | +| `devtools-anywhere-service-worker.js`:外壳模式下,给页面 iframe 中加载的、没有引入探针的同源页面注入探针 | 安全上下文(HTTPS 或 localhost)、作用域覆盖这些页面(`Service-Worker-Allowed: /`),并且站点没有自己的 Service Worker | + +所有方式共用同一份探针。 + +## 拆包 + +[README](../README.zh-CN.md#发布形式) 里列出的包为什么这样拆: + +- 页面内的 CDP agent 和探针不单独成包:探针和各个域共用同一套 hook 和缓冲,而且只有 `devtools-anywhere` 使用它们。它们放在 `devtools-anywhere` 中(`src/cdp-agent/`),不会出现版本不匹配;探针仍单独构建成一个很小的文件 `probe.iife.js`。 +- 前端单独成包,因为它体积大(约 16 MB),更新节奏跟随 Chrome 版本。其他语言(约 42 MB)再拆成一个包,需要的人才装。 +- 所有包使用同一个版本号、一起发布,版本号相同的包一定能配合使用。 From 16523bd57218e493f167474dbbaf7df555ba5868 Mon Sep 17 00:00:00 2001 From: jerrychan7 <czy-ken@163.com> Date: Sun, 1 Nov 2026 22:03:46 +0800 Subject: [PATCH 16/26] feat(shell): resize docked DevTools with a grip, shared by the panel and the shell A docked panel was resized by a bar across its whole edge, and in shell mode by the frontend's own resizer, a few pixels wide and invisible (with a wider handle on touch screens only, which covered the top of DevTools). Both now get the same grip: a small pill with three dots against the middle of DevTools' edge, on the page's side, like a tab of the panel. It takes input only where it is seen, so it covers none of DevTools and only as much of the page as it shows; it is larger on touch screens. - grip.ts is the reusable piece: a Grip placed against an edge of its parent, across it, inside or outside, with its size for a mouse and for a finger, drag callbacks, and colors from --grip-* custom properties. - The panel puts it outside its inner edge; the bar now only shows on the undocked window, which it moves. - The shell puts it inside the page's area, against its edge next to DevTools, as the frontend lays the page out; it takes DevTools' toolbar colors like the panel's. - The device mode tests find the grip by its title, check its size and place, and wait for the undocked page's width as well as its position, which raced. - In the shell the grip sits on the page's side of the split, which the frontend now reports (`split`, its rect) in place of the sidebar size: the page bounds are only the device's screen in device mode, which put the grip on the screen's edge, away from DevTools. --- CHANGELOG.md | 3 +- CHANGELOG.zh-CN.md | 3 +- README.md | 2 +- README.zh-CN.md | 2 +- docs/design.md | 2 +- docs/design.zh-CN.md | 2 +- packages/devtools-anywhere/src/element.ts | 113 ++++++++++------- packages/devtools-anywhere/src/embedder.ts | 7 +- packages/devtools-anywhere/src/grip.ts | 120 ++++++++++++++++++ .../devtools-anywhere/src/shell-layout.ts | 111 ++++++++-------- .../src/anywhere_app/anywhere_app.ts | 12 +- tests/e2e/device-mode.test.ts | 60 ++++++++- 12 files changed, 323 insertions(+), 114 deletions(-) create mode 100644 packages/devtools-anywhere/src/grip.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 259f17d..ac9d81f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,8 @@ All packages share one version and are released together (see DEV.md, "Releasing ## Unreleased -- Device mode in shell mode, as in Chrome: DevTools lays out the page and itself, the toolbar's device button sizes the page as a device (scaled to fit) and its menu takes screenshots, docked or undocked. It sets the page's size only, not its pixel ratio, user agent or touch input. On touch screens a handle on the edge between the page and DevTools resizes them. Outside shell mode the button is still the screencast's. +- The docked panel is resized with a grip on the middle of its inner edge, on the page's side, in place of the bar across its whole edge: it covers neither DevTools nor more of the page than it shows, and looks the same in and out of shell mode. The undocked window keeps its bar, which moves it. +- Device mode in shell mode, as in Chrome: DevTools lays out the page and itself, the toolbar's device button sizes the page as a device (scaled to fit) and its menu takes screenshots, docked or undocked. It sets the page's size only, not its pixel ratio, user agent or touch input. A grip against the middle of DevTools' edge, on the page's side, resizes them without covering DevTools, larger on touch screens. Outside shell mode the button is still the screencast's. - `Page.captureScreenshot` and `Page.getLayoutMetrics`: screenshots of the viewport, a region or the whole page, rendered by snapDOM and marked `approximate: true`, for tools and agents that drive the page over CDP. ## 0.1.0 diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md index a3a4f1b..145a053 100644 --- a/CHANGELOG.zh-CN.md +++ b/CHANGELOG.zh-CN.md @@ -4,7 +4,8 @@ ## Unreleased -- 外壳模式下新增设备模式,和 Chrome 一样:由 DevTools 排布页面和自己,工具栏上的设备按钮按设备尺寸设置页面(缩放到适合窗口),它的菜单可以截图,停靠和取消停靠时都能用。它只设置页面的尺寸,不改变像素比、UA 和触摸输入。触屏上,页面和 DevTools 的分界处有一条拖动条用来调整大小。非外壳模式下,这个按钮仍然是投屏。 +- 停靠的面板改用内侧边缘正中、靠页面一侧的抓手调整大小,取代原来贯穿整条边的横条:它不遮挡 DevTools,也只占页面上它看得见的那一小块,外壳模式内外样式一致。浮动窗口仍保留用来移动的横条。 +- 外壳模式下新增设备模式,和 Chrome 一样:由 DevTools 排布页面和自己,工具栏上的设备按钮按设备尺寸设置页面(缩放到适合窗口),它的菜单可以截图,停靠和取消停靠时都能用。它只设置页面的尺寸,不改变像素比、UA 和触摸输入。DevTools 面板边缘正中、靠页面一侧有一个抓手,用来调整大小,不遮挡 DevTools,触屏上更大。非外壳模式下,这个按钮仍然是投屏。 - 新增 `Page.captureScreenshot` 和 `Page.getLayoutMetrics`:可以截取视口、指定区域或整个页面,由 snapDOM 绘制,并标注 `approximate: true`,供通过 CDP 操作页面的工具和 agent 使用。 ## 0.1.0 diff --git a/README.md b/README.md index c5ba14d..69cdf64 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ Only `vite serve` is affected: the probe is injected as the first script of ever ### The element -A floating F12 button opens the panel; drag its bar to resize (in shell mode, DevTools' own resizer, see [shell mode](docs/design.md#2-shell-mode-in-place-rewrite)). Left of DevTools' settings button, the lock button switches shell mode on and off (a dialog explains it first, and an info bar shows while it is on) and the refresh button reloads the page; the toolbar's × closes the panel. DevTools' main menu (⋮) has "Dock side": bottom (the default), right, left, or undocked, a window over the page moved by its bar and resized from its corner. In shell mode the page shrinks to make room, as in Chrome; otherwise the panel covers the page. The side and sizes are remembered in localStorage, sizes as percentages of the viewport (`dvh`/`dvw`), so they fit any screen and orientation. Its one attribute, `devtools-frontend-url`, is optional: without it, the probe's `data-devtools-frontend-url` is used. The rest is the library's: the probe's options set the element up, and the user opens and closes it; it has no methods of its own. The page can restyle the F12 button with `devtools-anywhere::part(toggler)`, in shell mode too: there the button is in the page iframe, in a `<devtools-anywhere>` under the page's `<html>` that only holds it and opens and closes the shell's DevTools (the shell's own button shows only while the page in the iframe has no probe, such as another origin's page). The probe's element does not expose its iframe as a part. Keep the element in one place, since moving it reloads the iframe. +A floating F12 button opens the panel; drag the grip on its inner edge, on the page's side, to resize it (in shell mode too, next to DevTools' own resizer, see [shell mode](docs/design.md#2-shell-mode-in-place-rewrite)). Left of DevTools' settings button, the lock button switches shell mode on and off (a dialog explains it first, and an info bar shows while it is on) and the refresh button reloads the page; the toolbar's × closes the panel. DevTools' main menu (⋮) has "Dock side": bottom (the default), right, left, or undocked, a window over the page moved by its bar and resized from its corner. In shell mode the page shrinks to make room, as in Chrome; otherwise the panel covers the page. The side and sizes are remembered in localStorage, sizes as percentages of the viewport (`dvh`/`dvw`), so they fit any screen and orientation. Its one attribute, `devtools-frontend-url`, is optional: without it, the probe's `data-devtools-frontend-url` is used. The rest is the library's: the probe's options set the element up, and the user opens and closes it; it has no methods of its own. The page can restyle the F12 button with `devtools-anywhere::part(toggler)`, in shell mode too: there the button is in the page iframe, in a `<devtools-anywhere>` under the page's `<html>` that only holds it and opens and closes the shell's DevTools (the shell's own button shows only while the page in the iframe has no probe, such as another origin's page). The probe's element does not expose its iframe as a part. Keep the element in one place, since moving it reloads the iframe. A page can also place `<devtools-anywhere>` itself, as a box where DevTools shows: a block in the page's flow (`display: block`), sized by the page's CSS, with DevTools loaded as soon as it is connected. The page shows and hides it with CSS or `hidden`; hidden, DevTools stays loaded. It has no F12 button, dock side or resizing, and never enters shell mode (DevTools offers no way to); inside the shell's page it works as on its own. `devtools-frontend-url` is its only attribute; others are ignored. DevTools' iframe in it can be styled with `devtools-anywhere::part(iframe)`. diff --git a/README.zh-CN.md b/README.zh-CN.md index 6238f89..1fb285f 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -82,7 +82,7 @@ npx -p @devtools-anywhere/remote-server devtools-anywhere-remote --port 8080 ### 组件 -点击右下角的 F12 悬浮按钮打开面板;拖动面板的横条调整大小(外壳模式下用 DevTools 自己的分隔条,见[外壳模式](docs/design.zh-CN.md#2-外壳模式原地改写))。DevTools 设置按钮左边,锁形按钮开关外壳模式(开启前有对话框说明,开启后顶部有提示条),刷新按钮重新加载页面;工具栏上的 × 关闭面板。DevTools 主菜单(⋮)里有“停靠位置”:底部(默认)、右侧、左侧,或取消停靠,变成页面上方的浮动窗口,拖动横条移动、拖动右下角调整大小。外壳模式下页面会让出空间,和 Chrome 一样;普通模式下面板盖在页面上。停靠位置和尺寸会记在 localStorage 里;尺寸记的是占视口的百分比(`dvh`/`dvw`),换屏幕或横竖屏切换后依然合适。唯一的属性 `devtools-frontend-url` 是可选的,不写时沿用探针的 `data-devtools-frontend-url`。其余状态都由库维护:探针的参数决定组件的配置,打开和关闭由用户操作;组件没有自己的方法。页面可以用 `devtools-anywhere::part(toggler)` 修改 F12 按钮的样式,外壳模式下同样有效:此时按钮在页面 iframe 里,由页面 `<html>` 下一个只承载按钮的 `<devtools-anywhere>` 显示,点击开关外壳的 DevTools(只有 iframe 里的页面没有探针时,例如其他源的页面,外壳才显示自己的按钮)。探针挂载的组件不把它的 iframe 暴露为部件。组件要固定挂在一个位置,在 DOM 中移动它会导致 iframe 重新加载。 +点击右下角的 F12 悬浮按钮打开面板;拖动面板内侧边缘正中、靠页面一侧的抓手调整大小(外壳模式下也有,另外还能用 DevTools 自己的分隔条,见[外壳模式](docs/design.zh-CN.md#2-外壳模式原地改写))。DevTools 设置按钮左边,锁形按钮开关外壳模式(开启前有对话框说明,开启后顶部有提示条),刷新按钮重新加载页面;工具栏上的 × 关闭面板。DevTools 主菜单(⋮)里有“停靠位置”:底部(默认)、右侧、左侧,或取消停靠,变成页面上方的浮动窗口,拖动横条移动、拖动右下角调整大小。外壳模式下页面会让出空间,和 Chrome 一样;普通模式下面板盖在页面上。停靠位置和尺寸会记在 localStorage 里;尺寸记的是占视口的百分比(`dvh`/`dvw`),换屏幕或横竖屏切换后依然合适。唯一的属性 `devtools-frontend-url` 是可选的,不写时沿用探针的 `data-devtools-frontend-url`。其余状态都由库维护:探针的参数决定组件的配置,打开和关闭由用户操作;组件没有自己的方法。页面可以用 `devtools-anywhere::part(toggler)` 修改 F12 按钮的样式,外壳模式下同样有效:此时按钮在页面 iframe 里,由页面 `<html>` 下一个只承载按钮的 `<devtools-anywhere>` 显示,点击开关外壳的 DevTools(只有 iframe 里的页面没有探针时,例如其他源的页面,外壳才显示自己的按钮)。探针挂载的组件不把它的 iframe 暴露为部件。组件要固定挂在一个位置,在 DOM 中移动它会导致 iframe 重新加载。 页面也可以自己放置 `<devtools-anywhere>`,作为显示 DevTools 的一个区域:它是页面文档流里的块级盒子(`display: block`),尺寸由页面的 CSS 决定,一连接就加载 DevTools。页面用 CSS 或 `hidden` 控制它的显示和隐藏;隐藏时 DevTools 不会卸载。它没有 F12 按钮,不能停靠或拖拽调整大小,也不会进入外壳模式(DevTools 里没有入口);在外壳里的页面中照常工作。它只有 `devtools-frontend-url` 一个属性,其他属性一律忽略。里面的 DevTools iframe 可以用 `devtools-anywhere::part(iframe)` 设置样式。 diff --git a/docs/design.md b/docs/design.md index fe0c2c8..7ba4a99 100644 --- a/docs/design.md +++ b/docs/design.md @@ -50,7 +50,7 @@ No standalone `devtools.html` is hosted, and there is no navigation to a blob UR 2. The probe, being the first script, sees the config in the top-level window. It writes an unclosed `<plaintext>` so the rest of the page becomes inert text (none of its scripts run), then replaces the document with the shell via `document.open()` / `document.write()`. The shell contains the page iframe (pointing to `location.href`) and `<devtools-anywhere>`, which knows it is the shell's element by the document it is in. (Chrome ignores `document.open()` after `window.stop()`, hence `<plaintext>`.) 3. Inside the iframe, the probe captures as usual and loads the bundle, which connects the page's CDP session to the shell. The shell relays messages between the page and the frontend. 4. When the page reloads or navigates, the new page says hello; the shell tells the frontend the page navigated and re-sends every `enable` command the frontend has sent, so the new page replays its own earliest output. The address bar and title follow the page, through loads, history traversals and same-document navigations (`pushState` where the Navigation API exists), so that a reload or a copied link opens the page shown. -5. DevTools fills the shell and lays out the page and itself, as in Chrome: the page iframe goes where the frontend's page area is, on top of it. Dock side and sizes are DevTools' own (its resizer; on touch screens, a wider handle on the edge between the page and DevTools). Undocked, DevTools is a window over the page. The toolbar's device button is device mode, as in Chrome, in place of the screencast: the page iframe is sized as the device and scaled to fit, and the toolbar's menu takes screenshots. Undocked, device mode's toolbar is drawn in a page of the frontend's (`device_mode_emulation_frame.html`, as Chrome does) shown under the window. +5. DevTools fills the shell and lays out the page and itself, as in Chrome: the page iframe goes where the frontend's page area is, on top of it. Dock side and sizes are DevTools' own (its resizer, and a grip against the middle of DevTools' edge on the page's side, like a tab of the panel, larger on touch screens). Undocked, DevTools is a window over the page. The toolbar's device button is device mode, as in Chrome, in place of the screencast: the page iframe is sized as the device and scaled to fit, and the toolbar's menu takes screenshots. Undocked, device mode's toolbar is drawn in a page of the frontend's (`device_mode_emulation_frame.html`, as Chrome does) shown under the window. 6. The toolbar's × hides DevTools and stays in shell mode; the page fills the shell at its own size until DevTools is shown again. The lock button again clears the config and reloads the page on its own; the choice is remembered in `localStorage`, and wins over the page's `data-default-shell-mode` from then on, unless the page sets `data-shell-mode-locked`. The stored URLs are only trusted when they have the same origin as the probe script; otherwise the config is cleared and the page loads normally. Shell mode therefore needs the probe, the bundle and the frontend served from the page's origin. diff --git a/docs/design.zh-CN.md b/docs/design.zh-CN.md index d7bc576..d2acb66 100644 --- a/docs/design.zh-CN.md +++ b/docs/design.zh-CN.md @@ -50,7 +50,7 @@ devtools-anywhere 的工作原理和设计取舍。使用方法见 [README](../R 2. 探针作为第一个脚本,在顶层窗口检测到配置后,先写入一个不闭合的 `<plaintext>`,让页面剩余的内容变成纯文本(页面脚本都不会执行),再用 `document.open()` / `document.write()` 把文档替换成外壳。外壳中有指向 `location.href` 的用户页面 iframe,以及 `<devtools-anywhere>`,它凭所在的文档认出自己是外壳的元素。(Chrome 在 `window.stop()` 之后会忽略 `document.open()`,所以改用 `<plaintext>`。) 3. 在 iframe 中,探针照常捕获,并加载 bundle,把页面的 CDP 会话连接到外壳。外壳在页面和前端之间转发消息。 4. 页面刷新或跳转后,新页面会发出 hello;外壳通知前端页面已导航,并把前端发过的所有 `enable` 命令重新发给新页面,新页面就会回放它自己最早的输出。地址栏和标题跟随页面变化,包括加载、前进后退和同文档导航(有 Navigation API 时也包括 `pushState`),因此刷新或复制链接打开的都是当前显示的页面。 -5. DevTools 铺满外壳,由它来排布页面和自己,和 Chrome 一样:页面 iframe 放在前端的页面区域上,叠在上层。停靠位置和尺寸由 DevTools 自己管理(用它的分隔条;触屏上,页面和 DevTools 的分界处有一条更宽的拖动条)。取消停靠时,DevTools 是页面上方的浮动窗口。工具栏上的设备按钮是设备模式,和 Chrome 一样,取代了投屏:页面 iframe 按设备尺寸设置并缩放到适合窗口,工具栏的菜单可以截图。取消停靠时,设备模式的工具栏画在前端的一个页面(`device_mode_emulation_frame.html`,Chrome 也是这么做的)里,显示在浮动窗口下方。 +5. DevTools 铺满外壳,由它来排布页面和自己,和 Chrome 一样:页面 iframe 放在前端的页面区域上,叠在上层。停靠位置和尺寸由 DevTools 自己管理(用它的分隔条;DevTools 面板边缘正中、靠页面一侧还有一个抓手,像面板的一个小标签,触屏上更大)。取消停靠时,DevTools 是页面上方的浮动窗口。工具栏上的设备按钮是设备模式,和 Chrome 一样,取代了投屏:页面 iframe 按设备尺寸设置并缩放到适合窗口,工具栏的菜单可以截图。取消停靠时,设备模式的工具栏画在前端的一个页面(`device_mode_emulation_frame.html`,Chrome 也是这么做的)里,显示在浮动窗口下方。 6. 点工具栏上的 × 只隐藏 devtools,仍留在外壳模式;在重新显示 devtools 之前,页面按自己的尺寸铺满外壳。再点一次锁形按钮会清除配置,并在普通模式下重新加载页面;这个选择会记在 `localStorage` 里,之后优先于页面的 `data-default-shell-mode`,除非页面设置了 `data-shell-mode-locked`。 只有与探针脚本同源的地址才会被采信,否则清除配置、页面照常加载。因此外壳模式要求探针、bundle 和前端都由页面所在的源提供。 diff --git a/packages/devtools-anywhere/src/element.ts b/packages/devtools-anywhere/src/element.ts index 865d2b3..7c42547 100644 --- a/packages/devtools-anywhere/src/element.ts +++ b/packages/devtools-anywhere/src/element.ts @@ -17,6 +17,7 @@ import { } from './cdp-agent'; import { connectFrontend, relayShell } from './connection'; import { SHELL_HANDSHAKE } from './shell-messages'; +import { Grip } from './grip'; import { ShellLayout } from './shell-layout'; import type { GlobalElementConfig } from './api'; import type { DevtoolsAnywhereAttributes, TogglerAttributes } from './public-types'; @@ -63,6 +64,8 @@ const VW = typeof CSS !== 'undefined' && CSS.supports('width', '1dvw') ? 'dvw' : const percent = (part: number, whole: number) => Math.round((part / whole) * 1000) / 10; const DOCK_KEY = 'devtools-anywhere:dock'; const MIN_SIZE = 80; +/** The panel's inner edge, next to the page, for each docked side. */ +const GRIP_EDGE = { bottom: 'top', right: 'left', left: 'right' } as const; /** * localStorage, which throws when it is unavailable: Android WebViews without DOM storage, @@ -95,7 +98,7 @@ const REQUEST_CHECKS: { [T in EmbedderRequest['type']]: (request: object) => boo 'keep-open': request => 'keep' in request && typeof request.keep === 'boolean', theme: request => ['toolbar', 'divider', 'icon'].every(key => typeof Reflect.get(request, key) === 'string'), 'page-bounds': request => ['x', 'y', 'width', 'height'].every(key => finite(Reflect.get(request, key)) !== undefined), - split: request => 'size' in request && finite(request.size) !== undefined, + split: request => ['x', 'y', 'width', 'height'].every(key => finite(Reflect.get(request, key)) !== undefined), toolbox: request => 'url' in request && typeof request.url === 'string', }; const isRequestType = (type: unknown): type is EmbedderRequest['type'] => @@ -150,11 +153,13 @@ const STYLE = ` padding: 0 env(safe-area-inset-right, 0px) env(safe-area-inset-bottom, 0px) env(safe-area-inset-left, 0px); } .panel[data-open] { display: flex; } -/* The bar takes the DevTools toolbar's colors, which the frontend reports (theme request). */ +/* The undocked window's bar, which moves it; it takes the DevTools toolbar's colors, which the + frontend reports (theme request), as does the grip that resizes the docked panel (grip.ts). */ .bar { flex: none; position: relative; height: 16px; background: var(--bar-background, #f1f3f4); - cursor: row-resize; touch-action: none; + cursor: move; touch-action: none; display: none; } +[data-dock=undocked] .bar { display: block; } .bar::before { content: ''; position: absolute; left: 50%; top: 6px; width: 32px; height: 4px; margin-left: -16px; border-radius: 2px; background: var(--bar-handle, #bdc1c6); @@ -164,8 +169,7 @@ const STYLE = ` background: none; color: var(--bar-icon, #5f6368); font: 14px/16px system-ui, sans-serif; cursor: pointer; } /* DevTools' own toolbar has a close button, except when undocked: then the bar has one. */ -.close { right: 4px; display: none; } -[data-dock=undocked] .close { display: block; } +.close { right: 4px; } /* The undocked window's resize corner, for touch screens where CSS resize does not work. */ .corner { position: absolute; right: 0; bottom: 0; width: 16px; height: 16px; display: none; @@ -186,27 +190,18 @@ iframe { flex: 1; min-width: 0; min-height: 0; border: 0; } .panel[data-dock=left] { right: auto; flex-direction: row-reverse; border-right: 1px solid var(--bar-border, #cacdd1); padding-right: 0; } -[data-dock=right] .bar, [data-dock=left] .bar { width: 16px; height: auto; cursor: col-resize; } -[data-dock=right] .bar::before, [data-dock=left] .bar::before { - left: 6px; top: 50%; width: 4px; height: 32px; margin: -16px 0 0; -} -[data-dock=right] .bar button, [data-dock=left] .bar button { left: 0; right: auto; width: 16px; } /* Undocked: a window over the page, moved by its bar and resized from its corner. */ .panel[data-dock=undocked] { right: auto; bottom: auto; border: 1px solid var(--bar-border, #cacdd1); border-radius: 6px; overflow: hidden; box-shadow: 0 4px 16px rgba(0, 0, 0, .3); resize: both; padding: 0; } -[data-dock=undocked] .bar { cursor: move; } /* Touch screens: targets large enough for a finger. */ @media (pointer: coarse) { .bar { height: 28px; } .bar::before { top: 12px; } .bar button { width: 44px; height: 28px; line-height: 28px; font-size: 18px; } - [data-dock=right] .bar, [data-dock=left] .bar { width: 28px; } - [data-dock=right] .bar::before, [data-dock=left] .bar::before { left: 12px; } - [data-dock=right] .bar button, [data-dock=left] .bar button { width: 28px; height: 44px; } .corner { width: 32px; height: 32px; } } @@ -282,6 +277,7 @@ export class DevtoolsAnywhereElement extends HTMLElement { private shellLink: { attach(): void; detach(): void } | null = null; /** In the shell: places the page where the frontend lays it out (shell-layout.ts). */ private shellLayout: ShellLayout | null = null; + private readonly grip: Grip; constructor() { super(); @@ -311,7 +307,9 @@ export class DevtoolsAnywhereElement extends HTMLElement { }); part('.close').addEventListener('click', () => this.setOpen(false)); this.addEventListener(CONTROL_EVENT, this.onControl); - this.setupResizer(part<HTMLElement>('.bar')); + this.setupMover(part<HTMLElement>('.bar')); + this.grip = this.createGrip(); + this.panel.append(this.grip.element); this.setupCorner(part<HTMLElement>('.corner')); // The undocked panel is resized by the browser (CSS resize): remember its size once it // settles, not on every frame of the drag. @@ -354,20 +352,24 @@ export class DevtoolsAnywhereElement extends HTMLElement { else void this.exitShell(); } else if (request.type === 'theme') { const colors: [string, string[]][] = [ - [request.toolbar, ['--bar-background']], - [request.divider, ['--bar-handle', '--bar-border']], - [request.icon, ['--bar-icon']], + [request.toolbar, ['--bar-background', '--grip-background']], + [request.divider, ['--bar-handle', '--bar-border', '--grip-border']], + [request.icon, ['--bar-icon', '--grip-dot']], ]; + const set: Record<string, string> = {}; for (const [value, properties] of colors) { if (!CSS.supports('color', value)) continue; - for (const property of properties) this.panel.style.setProperty(property, value); + for (const property of properties) set[property] = value; } + for (const [property, value] of Object.entries(set)) this.panel.style.setProperty(property, value); + // The shell's grip is outside this element. + this.shellLayout?.setGripColors(set); } else if (request.type === 'close') { this.setOpen(false); } else if (request.type === 'page-bounds') { this.shellLayout?.pageBounds(request); } else if (request.type === 'split') { - this.shellLayout?.splitSize(request.size); + this.shellLayout?.splitArea(request); } else if (request.type === 'toolbox') { // Only a page of the frontend's own origin. let url: URL; @@ -740,6 +742,9 @@ export class DevtoolsAnywhereElement extends HTMLElement { const stored = Number(readStored(WIDTH_KEY)); style.width = stored ? `${stored}${VW}` : `50${VW}`; } + // Docked, the grip on the panel's inner edge resizes it; in the shell, the shell's grip does. + if (dock !== 'undocked') this.grip.place(GRIP_EDGE[dock], 'outside'); + this.grip.show(dock !== 'undocked' && !isShellElement(this) && !this.panel.hasAttribute('data-custom')); // The shell document is ours: the page iframe comes first, then this element. if (isShellElement(this)) { document.body.style.flexDirection = dock === 'right' ? 'row' : dock === 'left' ? 'row-reverse' : 'column'; @@ -810,44 +815,58 @@ export class DevtoolsAnywhereElement extends HTMLElement { this.frame.src = `${url.href.split('#')[0]}#?${hash}`; } - /** Dragging the bar resizes the docked panel, or moves the undocked one. */ - private setupResizer(resizer: HTMLElement) { - resizer.addEventListener('pointerdown', down => { + /** Dragging the bar moves the undocked panel. */ + private setupMover(bar: HTMLElement) { + bar.addEventListener('pointerdown', down => { if ((down.target as HTMLElement).closest('button')) return; down.preventDefault(); - resizer.setPointerCapture(down.pointerId); + bar.setPointerCapture(down.pointerId); this.panel.classList.add('dragging'); - const dock = this.dock; const start = this.panel.getBoundingClientRect(); - const clampSize = (size: number, max: number) => Math.min(Math.max(size, MIN_SIZE), max - 16); const move = (e: PointerEvent) => { - const style = this.panel.style; - if (dock === 'bottom') style.height = `${clampSize(innerHeight - e.clientY, innerHeight)}px`; - else if (dock === 'right') style.width = `${clampSize(innerWidth - e.clientX, innerWidth)}px`; - else if (dock === 'left') style.width = `${clampSize(e.clientX, innerWidth)}px`; - else { - const left = start.left + e.clientX - down.clientX; - const top = start.top + e.clientY - down.clientY; - // The whole window stays in the viewport, so its resize corner stays reachable; a - // window larger than the viewport keeps its top left corner in view. - style.left = `${Math.max(Math.min(left, innerWidth - start.width), 0)}px`; - style.top = `${Math.max(Math.min(top, innerHeight - start.height), 0)}px`; - } + const left = start.left + e.clientX - down.clientX; + const top = start.top + e.clientY - down.clientY; + // The whole window stays in the viewport, so its resize corner stays reachable; a + // window larger than the viewport keeps its top left corner in view. + this.panel.style.left = `${Math.max(Math.min(left, innerWidth - start.width), 0)}px`; + this.panel.style.top = `${Math.max(Math.min(top, innerHeight - start.height), 0)}px`; }; const up = () => { this.panel.classList.remove('dragging'); - if (dock === 'bottom') store(HEIGHT_KEY, String(percent(this.panel.offsetHeight, innerHeight))); - else if (dock === 'undocked') this.saveFloat(); - else store(WIDTH_KEY, String(percent(this.panel.offsetWidth, innerWidth))); + this.saveFloat(); // From the pixels of the drag to the remembered percentages. this.layout(); - resizer.removeEventListener('pointermove', move); - resizer.removeEventListener('pointerup', up); - resizer.removeEventListener('pointercancel', up); + bar.removeEventListener('pointermove', move); + bar.removeEventListener('pointerup', up); + bar.removeEventListener('pointercancel', up); }; - resizer.addEventListener('pointermove', move); - resizer.addEventListener('pointerup', up); - resizer.addEventListener('pointercancel', up); + bar.addEventListener('pointermove', move); + bar.addEventListener('pointerup', up); + bar.addEventListener('pointercancel', up); + }); + } + + /** The grip on the docked panel's inner edge, on the page's side: dragging it resizes the panel. */ + private createGrip() { + const clampSize = (size: number, max: number) => Math.min(Math.max(size, MIN_SIZE), max - 16); + return new Grip({ + edge: 'top', + side: 'outside', + title: 'Drag to resize', + onStart: () => this.panel.classList.add('dragging'), + onMove: e => { + const style = this.panel.style; + if (this.dock === 'bottom') style.height = `${clampSize(innerHeight - e.clientY, innerHeight)}px`; + else if (this.dock === 'right') style.width = `${clampSize(innerWidth - e.clientX, innerWidth)}px`; + else if (this.dock === 'left') style.width = `${clampSize(e.clientX, innerWidth)}px`; + }, + onEnd: () => { + this.panel.classList.remove('dragging'); + if (this.dock === 'bottom') store(HEIGHT_KEY, String(percent(this.panel.offsetHeight, innerHeight))); + else store(WIDTH_KEY, String(percent(this.panel.offsetWidth, innerWidth))); + // From the pixels of the drag to the remembered percentages. + this.layout(); + }, }); } diff --git a/packages/devtools-anywhere/src/embedder.ts b/packages/devtools-anywhere/src/embedder.ts index 75eb319..bb58938 100644 --- a/packages/devtools-anywhere/src/embedder.ts +++ b/packages/devtools-anywhere/src/embedder.ts @@ -15,8 +15,9 @@ export type DockSide = (typeof DOCK_SIDES)[number]; * Requests from the frontend to the embedder: its dock side menu, its close button, keeping * DevTools open across reloads (shell mode), the theme's colors for the panel's bar, and in shell * mode where the page goes (`page-bounds`, the frontend's InspectedPagePlaceholder, in CSS pixels - * of the frontend's viewport, like Chrome's setInspectedPageBounds) and how much room DevTools - * takes next to it (`split`, in CSS pixels, for the shell's touch handle on the edge between them). + * of the frontend's viewport, like Chrome's setInspectedPageBounds) and the page's side of the split + * next to DevTools (`split`, in the same pixels: the whole of it, where the page bounds are only the + * device's screen in device mode), for the shell's grip on DevTools' edge. * Undocked, device mode draws into a page of the frontend's (`toolbox`, its URL), which Chrome * opens in the tab under DevTools' window and the shell shows in an iframe under the panel, where * the frontend finds it among its parent's frames; the page bounds are then in CSS pixels of that @@ -28,7 +29,7 @@ export type EmbedderRequest = | { type: 'keep-open'; keep: boolean } | { type: 'theme'; toolbar: string; divider: string; icon: string } | ({ type: 'page-bounds' } & PageBounds) - | { type: 'split'; size: number } + | ({ type: 'split' } & PageBounds) | { type: 'toolbox'; url: string }; /** A rectangle in CSS pixels. */ diff --git a/packages/devtools-anywhere/src/grip.ts b/packages/devtools-anywhere/src/grip.ts new file mode 100644 index 0000000..55c8be9 --- /dev/null +++ b/packages/devtools-anywhere/src/grip.ts @@ -0,0 +1,120 @@ +// A grip for dragging a divider: a small pill with three dots that takes input only where it is +// seen, so that it hides none of what is around it. It is placed against an edge of its parent +// (which must be a containing block, positioned or fixed), on the middle of that edge: across +// the edge, inside the parent or outside it. Its colors are the custom properties +// --grip-background, --grip-border and --grip-dot, inherited from where it is placed. + +/** An edge of the grip's parent. */ +export type Edge = 'top' | 'right' | 'bottom' | 'left'; + +/** Where the grip sits against the edge: across it, inside the parent, or outside it. */ +export type GripSide = 'center' | 'inside' | 'outside'; + +/** A size in CSS pixels: across the edge, then along it. */ +export type GripSize = readonly [across: number, along: number]; + +export interface GripOptions { + edge: Edge; + side?: GripSide; + /** For a mouse, and larger for a finger on touch screens. */ + size?: { mouse: GripSize; touch: GripSize }; + /** Its tooltip. */ + title?: string; + onStart?(down: PointerEvent): void; + /** Each move of the pointer while the grip is dragged; `down` is where the drag started. */ + onMove(move: PointerEvent, down: PointerEvent): void; + onEnd?(): void; +} + +const DEFAULT_SIZE = { mouse: [12, 36], touch: [20, 52] } as const; +const OPPOSITE: Record<Edge, Edge> = { top: 'bottom', right: 'left', bottom: 'top', left: 'right' }; +/** The corners on each side, in border-radius order (top left, top right, bottom right, bottom left). */ +const CORNERS: Record<Edge, readonly number[]> = { top: [0, 1], right: [1, 2], bottom: [2, 3], left: [3, 0] }; +const COARSE = '(pointer: coarse)'; + +export class Grip { + readonly element: HTMLDivElement; + private edge: Edge; + private side: GripSide; + private readonly size: { mouse: GripSize; touch: GripSize }; + private readonly coarse = matchMedia(COARSE); + private readonly onPointerChange = () => this.render(); + + constructor(private readonly options: GripOptions) { + this.edge = options.edge; + this.side = options.side ?? 'center'; + this.size = options.size ?? DEFAULT_SIZE; + const el = (this.element = document.createElement('div')); + if (options.title) el.title = options.title; + el.style.cssText = + 'position:absolute;z-index:1;display:none;box-sizing:border-box;touch-action:none;' + + 'align-items:center;justify-content:center;gap:3px;' + + 'background:var(--grip-background,#f1f3f4);border:1px solid var(--grip-border,#cacdd1)'; + for (let i = 0; i < 3; i++) { + const dot = document.createElement('span'); + dot.style.cssText = + 'width:3px;height:3px;border-radius:50%;background:var(--grip-dot,#5f6368);pointer-events:none'; + el.append(dot); + } + this.coarse.addEventListener('change', this.onPointerChange); + this.setupDrag(); + this.render(); + } + + /** Moves the grip to another edge of its parent, or another side of it. */ + place(edge: Edge, side: GripSide = this.side) { + this.edge = edge; + this.side = side; + this.render(); + } + + show(shown: boolean) { + this.element.style.display = shown ? 'flex' : 'none'; + } + + dispose() { + this.coarse.removeEventListener('change', this.onPointerChange); + this.element.remove(); + } + + private render() { + const { edge, side } = this; + const style = this.element.style; + const [across, along] = this.size[this.coarse.matches ? 'touch' : 'mouse']; + const horizontal = edge === 'top' || edge === 'bottom'; + style.cursor = horizontal ? 'row-resize' : 'col-resize'; + style.flexDirection = horizontal ? 'row' : 'column'; + style.width = `${horizontal ? along : across}px`; + style.height = `${horizontal ? across : along}px`; + // On the middle of the edge, and across it, inside or outside by its offset from the edge. + style.top = style.right = style.bottom = style.left = ''; + style[horizontal ? 'left' : 'top'] = `calc(50% - ${along / 2}px)`; + style[edge] = `${{ center: -across / 2, inside: 0, outside: -across }[side]}px`; + // Against the edge, a side of the grip is flat and has no border; the others are rounded. + const flat: Edge | null = side === 'inside' ? edge : side === 'outside' ? OPPOSITE[edge] : null; + const radius = [6, 6, 6, 6]; + if (flat) for (const corner of CORNERS[flat]) radius[corner] = 0; + style.borderRadius = radius.map(r => `${r}px`).join(' '); + style.borderWidth = '1px'; + if (flat) style.setProperty(`border-${flat}-width`, '0'); + } + + private setupDrag() { + const el = this.element; + el.addEventListener('pointerdown', down => { + down.preventDefault(); + el.setPointerCapture(down.pointerId); + this.options.onStart?.(down); + const move = (e: PointerEvent) => this.options.onMove(e, down); + const up = () => { + el.removeEventListener('pointermove', move); + el.removeEventListener('pointerup', up); + el.removeEventListener('pointercancel', up); + this.options.onEnd?.(); + }; + el.addEventListener('pointermove', move); + el.addEventListener('pointerup', up); + el.addEventListener('pointercancel', up); + }); + } +} diff --git a/packages/devtools-anywhere/src/shell-layout.ts b/packages/devtools-anywhere/src/shell-layout.ts index 7ba4961..4630c1c 100644 --- a/packages/devtools-anywhere/src/shell-layout.ts +++ b/packages/devtools-anywhere/src/shell-layout.ts @@ -5,15 +5,14 @@ // answers by sizing and scaling the page iframe. Undocked, device mode draws into a page of the // frontend's (`toolbox`), shown here in an iframe under the floating panel; the bounds are then in // that iframe. Until the frontend says where the page goes, or while DevTools is hidden, the page -// fills the shell. On touch screens a handle on the edge between the page and DevTools resizes -// them, since the frontend's own resizer is a few pixels wide. +// fills the shell. Docked, a grip on the page's side of the middle of the edge between the page +// and DevTools resizes them, since the frontend's own resizer is a few pixels wide and shows +// nothing; it covers none of DevTools, and only as much of the page as it shows. import type { DeviceMetrics } from './connection'; +import { Grip } from './grip'; import { type DockSide, type FrontendNotice, type PageBounds, REQUEST_KEY, SIDEBAR_SIZE_NOTICE } from './embedder'; -/** Width of the touch handle, as the panel's bar on touch screens. */ -const HANDLE = 28; - /** Sets the given inline styles, and clears the others this layout sets. */ function place( el: HTMLElement, @@ -35,12 +34,15 @@ function place( export class ShellLayout { private bounds: PageBounds | null = null; - private split: number | null = null; + /** The page's side of the split next to DevTools (`split`), where the grip goes. */ + private split: PageBounds | null = null; private metrics: DeviceMetrics | null = null; private toolbox: HTMLIFrameElement | null = null; private open = false; private dock: DockSide = 'bottom'; - private readonly handle: HTMLDivElement; + /** The page's side of the split next to DevTools, for the grip on DevTools' edge (grip.ts). */ + private readonly pageArea: HTMLDivElement; + private readonly grip: Grip; /** * @param host the shell's `<devtools-anywhere>`, which fills the shell while the frontend lays out @@ -54,10 +56,18 @@ export class ShellLayout { private readonly page: HTMLIFrameElement, private readonly frontendOrigin: () => string ) { - this.handle = document.createElement('div'); - this.handle.style.cssText = 'position:fixed;z-index:2;display:none;touch-action:none'; - document.body.append(this.handle); - this.setupHandle(); + this.pageArea = document.createElement('div'); + this.pageArea.style.cssText = 'position:fixed;z-index:2;display:none;pointer-events:none'; + this.grip = new Grip({ + edge: 'bottom', + side: 'inside', + title: 'Drag to resize', + onMove: e => this.resizeTo(e), + }); + this.grip.element.style.pointerEvents = 'auto'; + this.grip.show(true); + this.pageArea.append(this.grip.element); + document.body.append(this.pageArea); addEventListener('resize', this.onResize); } @@ -66,7 +76,8 @@ export class ShellLayout { /** The element left the shell: stops listening and removes what this layout added. */ dispose() { removeEventListener('resize', this.onResize); - this.handle.remove(); + this.grip.dispose(); + this.pageArea.remove(); this.removeToolbox(); } @@ -75,6 +86,7 @@ export class ShellLayout { if (dock !== this.dock) { // The bounds were in the other side's layout (or in the toolbox): wait for new ones. this.bounds = null; + this.split = null; if (dock !== 'undocked') this.removeToolbox(); } this.open = open; @@ -87,8 +99,8 @@ export class ShellLayout { this.render(); } - splitSize(size: number) { - this.split = size; + splitArea(area: PageBounds) { + this.split = area; this.render(); } @@ -130,7 +142,7 @@ export class ShellLayout { // (device mode does not recover from starting small); undocked, it stays a floating panel. this.host.toggleAttribute('data-laid-out', this.open && this.dock !== 'undocked'); if (this.toolbox) this.toolbox.style.display = this.open ? '' : 'none'; - this.renderHandle(); + this.renderGrip(); // Until then the page fills the shell, on top. if (!laidOut || !this.bounds || !origin) return place(this.page, this.open ? { left: '0', top: '0', width: '100%', height: '100%' } : {}); @@ -154,44 +166,41 @@ export class ShellLayout { }); } - /** The touch handle straddles the edge between the page and DevTools, docked only. */ - private renderHandle() { - const style = this.handle.style; - const shown = this.open && this.dock !== 'undocked' && this.split !== null && !!this.bounds; - style.display = shown && matchMedia('(pointer: coarse)').matches ? 'block' : 'none'; - if (!shown || this.split === null) return; + /** + * The grip sits against the middle of the page's edge next to DevTools, on the page's side, as + * a tab of DevTools' panel; docked only. + */ + private renderGrip() { + // Not the page bounds: in device mode they are only the device's screen, in the middle. + const area = this.split; + const shown = this.open && this.dock !== 'undocked' && !!area && !!this.bounds; + this.pageArea.style.display = shown ? 'block' : 'none'; + if (!shown || !area || this.dock === 'undocked') return; const frame = this.frontend.getBoundingClientRect(); - const vertical = this.dock === 'bottom'; - style.cursor = vertical ? 'row-resize' : 'col-resize'; - style.left = `${vertical ? frame.left : this.dock === 'right' ? frame.right - this.split - HANDLE / 2 : frame.left + this.split - HANDLE / 2}px`; - style.top = `${vertical ? frame.bottom - this.split - HANDLE / 2 : frame.top}px`; - style.width = `${vertical ? frame.width : HANDLE}px`; - style.height = `${vertical ? HANDLE : frame.height}px`; + Object.assign(this.pageArea.style, { + left: `${frame.left + area.x}px`, + top: `${frame.top + area.y}px`, + width: `${area.width}px`, + height: `${area.height}px`, + }); + this.grip.place(this.dock); } - private setupHandle() { - this.handle.addEventListener('pointerdown', down => { - down.preventDefault(); - this.handle.setPointerCapture(down.pointerId); - const move = (e: PointerEvent) => { - const frame = this.frontend.getBoundingClientRect(); - const size = - this.dock === 'bottom' - ? frame.bottom - e.clientY - : this.dock === 'right' - ? frame.right - e.clientX - : e.clientX - frame.left; - const notice: FrontendNotice = { type: SIDEBAR_SIZE_NOTICE, size: Math.round(size) }; - this.frontend.contentWindow?.postMessage({ [REQUEST_KEY]: notice }, this.frontendOrigin()); - }; - const up = () => { - this.handle.removeEventListener('pointermove', move); - this.handle.removeEventListener('pointerup', up); - this.handle.removeEventListener('pointercancel', up); - }; - this.handle.addEventListener('pointermove', move); - this.handle.addEventListener('pointerup', up); - this.handle.addEventListener('pointercancel', up); - }); + /** The grip's colors (its custom properties, grip.ts), DevTools' toolbar's. */ + setGripColors(colors: Record<string, string>) { + for (const [property, value] of Object.entries(colors)) this.pageArea.style.setProperty(property, value); + } + + /** Dragging the grip: DevTools takes the room from the pointer to its far edge. */ + private resizeTo(e: PointerEvent) { + const frame = this.frontend.getBoundingClientRect(); + const size = + this.dock === 'bottom' + ? frame.bottom - e.clientY + : this.dock === 'right' + ? frame.right - e.clientX + : e.clientX - frame.left; + const notice: FrontendNotice = { type: SIDEBAR_SIZE_NOTICE, size: Math.round(size) }; + this.frontend.contentWindow?.postMessage({ [REQUEST_KEY]: notice }, this.frontendOrigin()); } } diff --git a/packages/devtools-frontend/src/anywhere_app/anywhere_app.ts b/packages/devtools-frontend/src/anywhere_app/anywhere_app.ts index 08ed821..558a963 100644 --- a/packages/devtools-frontend/src/anywhere_app/anywhere_app.ts +++ b/packages/devtools-frontend/src/anywhere_app/anywhere_app.ts @@ -220,17 +220,25 @@ window.open = (url?: string|URL, target?: string, features?: string) => { return open(url, target, features); }; let splitWatched = false; +/** The page's side of the split, next to DevTools, for the shell's grip on DevTools' edge. */ +function notifySplit(split: UI.SplitWidget.SplitWidget): void { + const main = split.mainWidget()?.element.getBoundingClientRect(); + if (main) { + notifyEmbedder({type: 'split', x: main.x, y: main.y, width: main.width, height: main.height}); + } +} host.setInspectedPageBounds = bounds => { notifyEmbedder({type: 'page-bounds', ...bounds}); const split = UI.InspectorView.InspectorView.maybeGetInspectorViewInstance()?.ownerSplit(); if (!split) { return; } - notifyEmbedder({type: 'split', size: split.sidebarSize()}); + notifySplit(split); if (!splitWatched) { splitWatched = true; + // Measured once laid out at the new size. split.addEventListener( - UI.SplitWidget.Events.SIDEBAR_SIZE_CHANGED, event => notifyEmbedder({type: 'split', size: event.data})); + UI.SplitWidget.Events.SIDEBAR_SIZE_CHANGED, () => requestAnimationFrame(() => notifySplit(split))); } }; diff --git a/tests/e2e/device-mode.test.ts b/tests/e2e/device-mode.test.ts index d197803..69c0c61 100644 --- a/tests/e2e/device-mode.test.ts +++ b/tests/e2e/device-mode.test.ts @@ -229,7 +229,12 @@ test('undocked, device mode draws into its own page under the floating panel', { await waitFor(() => pickDevice(toolbox, 'iPhone 16').then(() => true), "the toolbox's toolbar"); const where = await waitFor(async () => { const [state, spot] = [await pageState(), await placeholder(toolbox).catch(() => null)]; - return spot && state.viewport.width === 393 && Math.abs(state.rect.left - spot.left) <= 2 ? { state, spot } : null; + return spot && + state.viewport.width === 393 && + Math.abs(state.rect.left - spot.left) <= 2 && + Math.abs(state.rect.width - spot.width) <= 2 + ? { state, spot } + : null; }, 'the page in the toolbox'); near(where.state.rect.width, where.spot.width, 'width'); await dock('bottom'); @@ -253,6 +258,27 @@ test('docked to the right, the page goes where the frontend says', { skip }, asy await dock('bottom'); }); +test("in device mode, the grip stays on DevTools' edge, not on the device's screen", { skip }, async () => { + const fe = await frontend(); + await waitFor(async () => { + // DevTools' top edge, docked at the bottom, in the shell's coordinates. + const edge = await fe.evaluate(async () => { + const script = document.querySelector<HTMLScriptElement>('script[type=module]')?.src ?? ''; + // The built frontend has no type declarations: InspectorView's element. + const UI: { InspectorView: { InspectorView: { instance(): { element: Element } } } } = await ( + await import(script) + ).modules['ui/legacy/legacy.js'](); + return UI.InspectorView.InspectorView.instance().element.getBoundingClientRect().top; + }); + const offset = await (await fe.frameElement())?.evaluate(f => f.getBoundingClientRect().top); + const grip = await page.evaluate(() => { + const el = document.querySelector<HTMLElement>('body > div > div[title="Drag to resize"]'); + return el?.getClientRects().length ? el.getBoundingClientRect().bottom : null; + }); + return grip !== null && offset !== undefined && Math.abs(grip - (edge + offset)) <= 2; + }, "the grip on DevTools' edge"); +}); + test('leaving device mode gives the page the room the frontend lays out for it', { skip }, async () => { await runAction('emulation.toggle-device-mode'); const fe = await frontend(); @@ -264,6 +290,32 @@ test('leaving device mode gives the page the room the frontend lays out for it', assert.equal(where.state.viewport.width, Math.round(where.state.rect.width)); }); +test( + "docked, a grip on the edge between the page and DevTools, on the page's side, resizes them", + { skip }, + async () => { + const fe = await frontend(); + const found = await waitFor(async () => { + const [spot, handle] = [ + await placeholder(fe), + await page.evaluate(() => { + const el = document.querySelector<HTMLElement>('body > div > div[title="Drag to resize"]'); + if (!el || !el.getClientRects().length) return null; + const { top, width, height } = el.getBoundingClientRect(); + return { top, width, height, dots: el.children.length }; + }), + ]; + // Docked at the bottom, the edge is the bottom of the page's area: the grip sits on it, above. + return handle && Math.abs(handle.top + handle.height - (spot.top + spot.height)) <= 2 ? handle : null; + }, 'the handle on the edge'); + // For a mouse, a grip of 36 × 12 pixels with three dots: it takes input only where it is seen. + assert.deepEqual( + { width: found.width, height: found.height, dots: found.dots }, + { width: 36, height: 12, dots: 3 } + ); + } +); + test('on a touch screen, a handle on the edge between the page and DevTools resizes them', { skip }, async () => { // A touch screen, as the shell sees it; a resize lays the shell out again. await page.evaluate(() => { @@ -275,10 +327,8 @@ test('on a touch screen, a handle on the edge between the page and DevTools resi /** The handle's center, once shown. */ const handle = () => page.evaluate(() => { - const el = [...document.body.children].find( - (e): e is HTMLElement => e instanceof HTMLDivElement && e.style.cursor === 'row-resize' - ); - if (!el || el.style.display === 'none') return null; + const el = document.querySelector<HTMLElement>('body > div > div[title="Drag to resize"]'); + if (!el || !el.getClientRects().length) return null; const { left, top, width, height } = el.getBoundingClientRect(); return { x: left + width / 2, y: top + height / 2 }; }); From 5885cab6ee05541b2b1cbc2df7b35a48b15eae83 Mon Sep 17 00:00:00 2001 From: jerrychan7 <czy-ken@163.com> Date: Mon, 2 Nov 2026 00:13:10 +0800 Subject: [PATCH 17/26] docs: a short README to start with, and a usage guide to look things up in The README spent its length on long sentences that readers had to dig through for the one option they needed. It now says what devtools-anywhere is, how it compares, how to start (Vite, a CDN script tag, a phone from a computer), the common options, what works, and the packages, with two screenshots of WebMC (https://github.com/jerrychan7/WebMC): device mode in landscape on a computer, and a phone, the Elements panel dark next to the light page. The images are lossless WebP. docs/usage.md and docs/usage.zh-CN.md are the reference, in tables and short lines: which setup for which scenario, the script tag and its attributes with their Vite options and defaults, the Vite plugin, an element placed by the page, the panel's controls and togglers, shell mode (what it changes, when it is off, its caveats, the Service Worker), remote debugging (CLI options, behavior, Node.js API and endpoints), the limitations, the screenshots' parameters, and the packages. Everything the README said before is kept, and corrected where it was wrong: shell mode needs the bundle and frontend on the probe's origin (not the page's), which is why a CDN setup turns it off; the Vite plugin defaults to the installed frontend; its serviceWorker option is on / off only. The design documents link to the usage guide, the Chinese one gains the half sentence it was missing on the network buffers and says DevTools throughout, and two long English sentences are split. DEV.md, the packages' READMEs and a comment in screenshot.ts point to the usage guide. --- DEV.md | 4 +- DEV.zh-CN.md | 4 +- README.md | 169 ++++------- README.zh-CN.md | 171 ++++-------- docs/design.md | 17 +- docs/design.zh-CN.md | 35 ++- docs/images/desktop.webp | Bin 0 -> 176132 bytes docs/images/mobile.webp | Bin 0 -> 262514 bytes docs/usage.md | 263 ++++++++++++++++++ docs/usage.zh-CN.md | 263 ++++++++++++++++++ .../src/cdp-agent/domains/page/screenshot.ts | 2 +- packages/remote-server/README.md | 2 +- packages/vite-plugin/README.md | 2 +- 13 files changed, 689 insertions(+), 243 deletions(-) create mode 100644 docs/images/desktop.webp create mode 100644 docs/images/mobile.webp create mode 100644 docs/usage.md create mode 100644 docs/usage.zh-CN.md diff --git a/DEV.md b/DEV.md index ab51f4a..2882cb2 100644 --- a/DEV.md +++ b/DEV.md @@ -88,7 +88,7 @@ tests/ tests that span packages e2e/ probe → CDP → frontend support/ launches Chrome with the static server playground/ pages and server for development -docs/ design (design.md, design.zh-CN.md) +docs/ usage guide, design and the README's screenshots experiments/ validation records ``` @@ -199,7 +199,7 @@ The pre-commit hook (husky + lint-staged) runs on the staged files only: it form The build rolls each package's type declarations up into one file per entry point (`dist/index.d.ts`) with API Extractor, and writes the package's API to `packages/<package>/etc/`: `*.public.api.md` for the public API (what is promised), `*.api.md` for everything exported, internal included (`scripts/api-extract.ts`, configured by `api-extractor.base.json`; it copies `declare global` blocks into the rolled-up file, which API Extractor leaves out). `devtools-anywhere` has no runtime entry: its `dist/index.d.ts` is types only, and its scripts are built as IIFEs. Commit a changed report with the change that caused it: it is how a change to the public API shows in review. With `CI=true`, which GitHub Actions sets, a report that no longer matches fails the build, so CI catches a report left uncommitted. Every export of an entry point needs a release tag: `@public` only for what the README documents (remote-server's `startRemoteServer` / `createRemoteHandler`, the Vite plugin), `@internal` for everything else, which is used only between this repository's packages, built and released together, and may change at any time; when in doubt, `@internal`, and export nothing an entry point does not need. Doc comments are TSDoc: put package names such as `@devtools-anywhere/...` in backticks and code examples in fenced blocks. -Also update both `README.md` and `README.zh-CN.md` (and this guide and `docs/design.md` in both languages) when behavior, usage or limitations change, and add a line for users under `Unreleased` in both `CHANGELOG.md` and `CHANGELOG.zh-CN.md` (moving the frontend to a new Chrome milestone also changes its `Based on Chrome` line). +Also update both `README.md` and `README.zh-CN.md` (and this guide, `docs/usage.md` and `docs/design.md` in both languages) when behavior, usage or limitations change, and add a line for users under `Unreleased` in both `CHANGELOG.md` and `CHANGELOG.zh-CN.md` (moving the frontend to a new Chrome milestone also changes its `Based on Chrome` line). When a dependency that ends up in a build output is added or upgraded, or code is ported from another project (including moving the DevTools frontend to a new `REVISION`), run `pnpm licenses:update` to regenerate `THIRD_PARTY_LICENSES.md` at the root and in the packages (for a new library, add it to the list in `scripts/third-party-licenses.ts` first). It needs the frontend built: the build writes the `front_end/third_party` libraries that end up in `dist/` to `packages/devtools-frontend/dist-third-party.ignore.json` (next to `dist/`, not published), which the script checks its list against; `build:frontend --copy-only` regenerates it from the last build. diff --git a/DEV.zh-CN.md b/DEV.zh-CN.md index a900334..bb4aa1b 100644 --- a/DEV.zh-CN.md +++ b/DEV.zh-CN.md @@ -88,7 +88,7 @@ tests/ 跨包的测试 e2e/ 探针 → CDP → 前端的完整流程 support/ 启动 Chrome 和静态服务器 playground/ 开发调试用的页面和服务器 -docs/ 设计文档(design.md、design.zh-CN.md) +docs/ 使用指南、设计文档和 README 的截图 experiments/ 验证记录 ``` @@ -199,7 +199,7 @@ pre-commit 钩子(husky + lint-staged)只处理暂存的文件:先用 oxfm 构建时用 API Extractor 把各包的类型声明按入口合并成单个文件(`dist/index.d.ts`),并把包的 API 写进 `packages/<包>/etc/`:`*.public.api.md` 是公开 API(对外的承诺),`*.api.md` 是全部导出(含内部)(`scripts/api-extract.ts`,配置见 `api-extractor.base.json`;API Extractor 会丢掉 `declare global` 块,脚本会把它们补进合并后的文件)。`devtools-anywhere` 没有运行时入口:它的 `dist/index.d.ts` 只有类型,脚本都构建成 IIFE。报告有变化时与引起它的改动一起提交:公开 API 的变化靠它在评审中看到。设置 `CI=true` 时(GitHub Actions 会自动设置),报告与 API 不一致会让构建失败,所以忘了提交的报告会被 CI 拦下。入口的每个导出都要有发布标签:`@public` 只给 README 写明的用法(remote-server 的 `startRemoteServer` / `createRemoteHandler`、Vite 插件),其余一律 `@internal`,只供本仓库各包之间使用(一起构建、一起发布),随时可改;拿不准就标 `@internal`,入口用不到的就不导出。文档注释按 TSDoc 写:`@devtools-anywhere/...` 这类包名放进反引号,代码示例放进围栏代码块。 -行为、用法或限制有变化时,同时更新 `README.md` 和 `README.zh-CN.md`(以及本指南和 `docs/design.md` 的两种语言版本),并在 `CHANGELOG.md` 和 `CHANGELOG.zh-CN.md` 的 `Unreleased` 下各给用户写一行说明(前端升级到新的 Chrome 里程碑时,还要改它的 `Based on Chrome` 那一行)。 +行为、用法或限制有变化时,同时更新 `README.md` 和 `README.zh-CN.md`(以及本指南、`docs/usage.md` 和 `docs/design.md` 的两种语言版本),并在 `CHANGELOG.md` 和 `CHANGELOG.zh-CN.md` 的 `Unreleased` 下各给用户写一行说明(前端升级到新的 Chrome 里程碑时,还要改它的 `Based on Chrome` 那一行)。 新增或升级会进入产物的依赖、移植其它项目的代码(包括把 DevTools 前端换到新的 `REVISION`)时,运行 `pnpm licenses:update` 重新生成根目录和各包的 `THIRD_PARTY_LICENSES.md`(新增的库先加进 `scripts/third-party-licenses.ts` 的清单)。 diff --git a/README.md b/README.md index 69cdf64..377e1a5 100644 --- a/README.md +++ b/README.md @@ -2,150 +2,101 @@ English | [简体中文](README.zh-CN.md) -Real Chrome DevTools inside any web page, especially on mobile, where browsers have no developer tools. +Real Chrome DevTools inside any web page, phones included. -## Why - -Existing in-page debuggers fall short: - -| Tool | Limitation | -|---|---| -| eruda / vConsole | Only capture output after they initialize. They are large and usually loaded asynchronously, so the earliest logs and errors of a page are lost | -| mobile-devtools | A custom UI rather than Chrome DevTools; initialized through framework adapters, so it starts after the app code (based on its README, not verified) | -| Chii | Can capture early output, but setup and configuration are cumbersome, and its frontend is stuck on an old Chrome DevTools (around version 122) | +<figure> +<p align="center"> + <img src="docs/images/desktop.webp" alt="Desktop: the page in device mode as an iPhone SE in landscape, with the Elements panel docked to the right" width="62%"> + <img src="docs/images/mobile.webp" alt="Phone: the Elements panel under the page" width="22%"> +</p> +<figcaption><p align="center"><sub>Screenshots: <a href="https://github.com/jerrychan7/WebMC">WebMC</a>, a Minecraft in WebGL, with devtools-anywhere. Left: device mode on a computer. Right: on a phone.</sub></p></figcaption> +</figure> -Goals: - -1. **Never miss early output**: console messages, errors and network requests are visible from the very first script on the page. -2. **Zero-config setup**: a single `<script>` tag or a build plugin is all it takes. -3. **DevTools survive reloads**: DevTools stay open across page reloads and navigations, with logs preserved. -4. **Keep up with the latest Chrome DevTools**: maintain a patch set on top of the official devtools-frontend and upgrade regularly instead of staying on an old version. +## Why -## Usage (preview) +| | devtools-anywhere | eruda / vConsole | Chii | +|---|---|---|---| +| UI | The latest Chrome DevTools | A panel of their own | An old Chrome DevTools (about 122) | +| The page's earliest logs and errors | ✅ From the first script | ❌ Lost until they load | ✅ | +| Setup | One `<script>` or a Vite plugin | One `<script>` | Cumbersome | -The packages are on npm (`npm i devtools-anywhere @devtools-anywhere/devtools-frontend`); the paths below refer to their `dist/` directories, which a build of this repository makes too. `data-devtools-frontend-url` points at `anywhere_app.html` of a `@devtools-anywhere/devtools-frontend` build. Its `bundle/` files are named after their content and can be cached for good; serve `anywhere_app.html` itself with revalidation (`Cache-Control: no-cache`), so that an update reaches the browser. +## Quick start -### One script tag +**Vite**, during `vite serve` only: -```html -<head> - <!-- First synchronous script: captures output from the very start. --> - <script src="/path/to/devtools-anywhere/dist/probe.iife.js" data-devtools-frontend-url="/path/to/devtools-frontend/dist/anywhere_app.html"></script> - ... -</head> -``` - -From a CDN: jsDelivr and unpkg serve every npm package, so nothing needs hosting. Pin an exact version, the same for both packages. Only English and Chinese are available this way, since the other languages are in a separate package that has to be served from the frontend's directory. Shell mode is not available either, as it needs the probe, the bundle and the frontend from the page's origin (see [shell mode](docs/design.md#2-shell-mode-in-place-rewrite)): `data-default-shell-mode="off"` embeds DevTools directly in the page. - -```html -<!-- jsDelivr --> -<script src="https://cdn.jsdelivr.net/npm/devtools-anywhere@0.1.0/dist/probe.iife.js" data-devtools-frontend-url="https://cdn.jsdelivr.net/npm/@devtools-anywhere/devtools-frontend@0.1.0/dist/anywhere_app.html" data-default-shell-mode="off"></script> -<!-- unpkg --> -<script src="https://unpkg.com/devtools-anywhere@0.1.0/dist/probe.iife.js" data-devtools-frontend-url="https://unpkg.com/@devtools-anywhere/devtools-frontend@0.1.0/dist/anywhere_app.html" data-default-shell-mode="off"></script> +```bash +npm i -D @devtools-anywhere/vite-plugin ``` -After the page loads, the probe loads `devtools-anywhere.iife.js` from next to itself (or `data-bundle`) and mounts its own `<devtools-anywhere>` under `<html>`, next to `<body>`, whatever `<devtools-anywhere>` elements the page has; there is one such global element per page, and another one only logs a warning. Optional attributes: `data-open-immediately` opens DevTools right away; `data-default-shell-mode="off"` stays out of shell mode, which the top-level page enters by default (`on`); `data-shell-mode-locked` keeps shell mode as `data-default-shell-mode` says, ignoring the user's choice (which stays stored) and hiding DevTools' switch, though a page that refuses to be framed is still shown on its own; `data-no-toggler` hides the F12 button, also in shell mode; `data-service-worker` enables the Service Worker in shell mode, which injects the probe into same-origin pages in the page iframe that do not include it and adds the request and response headers of same-origin requests (documents, stylesheets, scripts, images and so on); it is off by default, see its [requirements](docs/design.md#3-injection). - -### Vite - ```js // vite.config.js import devtoolsAnywhere from '@devtools-anywhere/vite-plugin' -export default { - plugins: [devtoolsAnywhere()], -} +export default { plugins: [devtoolsAnywhere()] } ``` -Only `vite serve` is affected: the probe is injected as the first script of every page, and the bundle, the Service Worker and the frontend are served under `/@devtools-anywhere/`. The frontend comes from `@devtools-anywhere/devtools-frontend`. Options: `devtoolsFrontendPath` (the entry page of another frontend build on disk, such as `…/anywhere_app.html`; its directory is served), `devtoolsFrontendUrl` (a frontend hosted elsewhere, served in place of it), `openImmediately`, `defaultShellMode` (`'on'` or `'off'`), `shellModeLocked`, `noToggler` (hides the F12 button), `serviceWorker` (see its [requirements](docs/design.md#3-injection); the plugin sends `Service-Worker-Allowed` itself), which match the probe's attributes, `remote` (the URL of a [remote debugging](#remote-debugging) server to connect pages to, alongside the element), with `idInQuery` (`data-id-in-query`, for a server with `idInQuery`). - -### The element - -A floating F12 button opens the panel; drag the grip on its inner edge, on the page's side, to resize it (in shell mode too, next to DevTools' own resizer, see [shell mode](docs/design.md#2-shell-mode-in-place-rewrite)). Left of DevTools' settings button, the lock button switches shell mode on and off (a dialog explains it first, and an info bar shows while it is on) and the refresh button reloads the page; the toolbar's × closes the panel. DevTools' main menu (⋮) has "Dock side": bottom (the default), right, left, or undocked, a window over the page moved by its bar and resized from its corner. In shell mode the page shrinks to make room, as in Chrome; otherwise the panel covers the page. The side and sizes are remembered in localStorage, sizes as percentages of the viewport (`dvh`/`dvw`), so they fit any screen and orientation. Its one attribute, `devtools-frontend-url`, is optional: without it, the probe's `data-devtools-frontend-url` is used. The rest is the library's: the probe's options set the element up, and the user opens and closes it; it has no methods of its own. The page can restyle the F12 button with `devtools-anywhere::part(toggler)`, in shell mode too: there the button is in the page iframe, in a `<devtools-anywhere>` under the page's `<html>` that only holds it and opens and closes the shell's DevTools (the shell's own button shows only while the page in the iframe has no probe, such as another origin's page). The probe's element does not expose its iframe as a part. Keep the element in one place, since moving it reloads the iframe. - -A page can also place `<devtools-anywhere>` itself, as a box where DevTools shows: a block in the page's flow (`display: block`), sized by the page's CSS, with DevTools loaded as soon as it is connected. The page shows and hides it with CSS or `hidden`; hidden, DevTools stays loaded. It has no F12 button, dock side or resizing, and never enters shell mode (DevTools offers no way to); inside the shell's page it works as on its own. `devtools-frontend-url` is its only attribute; others are ignored. DevTools' iframe in it can be styled with `devtools-anywhere::part(iframe)`. - -Any element can open and close the panel: give it `data-devtools-anywhere-toggle`, empty for the probe's element. Set to the id of an element the page placed, a click only fires `devtools-toggle-request` on that element, for the page to act on; such togglers get no `aria-expanded`. Clicks are caught on the document, so togglers added later work too; the default action is not prevented, and the togglers' `aria-expanded` follows the panel. A toggler whose element is missing logs a warning, once per value. Whether the panel is open shows only there, in the togglers' `aria-expanded`: the element fires no event of its own. +**Any page**, from a CDN, first in `<head>` (shell mode is not supported from a CDN, so it is turned off): ```html -<button data-devtools-anywhere-toggle>DevTools</button> +<script src="https://cdn.jsdelivr.net/npm/devtools-anywhere@0.1.0/dist/probe.iife.js" + data-devtools-frontend-url="https://cdn.jsdelivr.net/npm/@devtools-anywhere/devtools-frontend@0.1.0/dist/anywhere_app.html" + data-default-shell-mode="off"></script> ``` -Togglers work once the probe has loaded `devtools-anywhere.iife.js`, which registers the element, and not inside a shadow root. In shell mode, the togglers of the page inside the shell control the shell's panel, whatever their value. They do nothing in a page of another origin than the shell, such as a cross-origin iframe. - -### Remote debugging +Open the page and tap the **F12** button. -To inspect a page on a phone from Chrome on a computer, start the remote debugging server on the computer: +**A page on a phone, from a computer**: ```bash -npx -p @devtools-anywhere/remote-server devtools-anywhere-remote --port 8080 -``` - -It prints the target list's URL, with a token, and the script tag for pages, naming the computer by its LAN address (`--public-url` names it otherwise, for example behind a proxy). Load the probe from the server, with `data-remote`, as the first script in the page's `<head>`: - -```html -<script src="http://192.168.1.2:8080/probe.iife.js" data-remote></script> +npx -p @devtools-anywhere/remote-server devtools-anywhere-remote ``` -The page connects after it loads (early output is captured as usual), and shows up in the target list, which can be filtered and shows how many DevTools windows are attached; "inspect" opens DevTools on it in a tab of its own. DevTools stays connected when the page reloads or navigates within the tab, and the page reconnects by itself when the connection drops (it stays on the list meanwhile). Several DevTools windows can inspect one page at once, each in a session of its own, as in Chrome: the page runs a CDP session per DevTools session. `data-remote` can also name the server (`data-remote="http://host:8080/"`) when the probe comes from elsewhere. It is resolved against the probe's URL like a link, so a server under a path needs the trailing slash: `/devtools/` names the server at `/devtools/`, while `/devtools` names the one at `/`. With `data-devtools-frontend-url` as well, the page also gets its own DevTools panel; both inspect it at once, each in a session of its own. Shell mode works as without `data-remote` (it needs the probe, bundle and frontend on one origin, so not with the probe from the server and a frontend elsewhere): the page in the shell connects, the shell itself does not. With `data-service-worker` too, so do the same-origin pages without the probe that the page iframe navigates to: the Service Worker gives them the probe, and they connect to the same server. Whether a page in the shell connects, with a probe of its own or one the Service Worker gave it, is up to the shell's probe: without a server there, none connects, and the page's own `data-remote` has no effect. The shell keeps that server in memory, not in `sessionStorage` with the rest of its config, and tells each page in the shell once it has loaded, so a script of the page cannot point the next pages elsewhere. +Add the printed `<script>` to the page, open the printed target list on the computer, and click "inspect". -- The server serves the probe, its bundle and the DevTools frontend; the frontend runs on the computer, so the same-origin caveat does not apply. -- Inspecting a page means running any script in it: the target list and DevTools connections need the token (`--token <value>`, by default a random one; `--no-token` turns it off). Pages connect without it. -- An HTTPS page can only connect to an HTTPS server: `--cert` and `--key`. Other options: `--host`, `--base-path`, `--id-in-query` (see `idInQuery` below), `--devtools-frontend-path`, and `--devtools-frontend-url` to open a frontend hosted elsewhere (a CDN; an HTTPS one needs the server over HTTPS too; its links carry `ws=`/`wss=` and the token in the URL fragment, so they are not sent to the CDN). The same is available from Node.js as `startRemoteServer(options)`, and `createRemoteHandler(options)` gives its request and upgrade handlers, to mount under a base path in a server of one's own (Express, Koa, …): each returns false for what is not its own. Pages connect to `<base>target/<id>` and DevTools to `<base>client/<id>`; with `idInQuery: true`, for a host that routes WebSocket upgrades by exact path only, both connect to the base path itself (`<base>?target=<id>`, `<base>?client=<id>`): the target list and the printed script tag use that form, whose script tag carries `data-id-in-query`, and register the base path, with its trailing slash, for upgrades. Both forms are always accepted. A frontend opened by itself connects to any target as `anywhere_app.html?ws=<host:port/path>` (`wss=` over HTTPS; also read from the fragment, `#?ws=`), the WebSocket URL without its scheme, as in Chrome's `devtools://devtools/bundled/devtools_app.html?ws=`. -- `/json/list` (with the token) lists the targets in the shape of Chrome's. +Self-hosting, every option and the element: see the [usage guide](docs/usage.md). -## Limitations +## Common options -- An in-page CDP implementation can only use JS APIs, so the following are **not available**: breakpoint debugging, Profiler, Memory, and real network timing. These panels are not included in the frontend rather than shown and found broken. A `debugger;` statement in page scripts cannot pause either; in shell mode, each one hit in a same-origin script logs a warning with its stack in the Console instead. Real breakpoints need the browser's own debugger (`chrome://inspect` for Android, Safari's Web Inspector for iOS); [remote debugging](#remote-debugging) does not change this, since the page still runs the same in-page CDP agent. The Sources panel's Page tab lists the page's documents, external scripts and style sheets, images and fonts (inline scripts are not listed, since their position in the document is not known); snippets work. -- Where page scripts lack an API, the gap is filled exactly or not at all; approximations are not shown as facts: - - User-agent styles are not shown: browsers do not expose their default style sheet, and a bundled copy could differ from the browser's. - - Event listeners are recorded by the probe from the moment it runs (it must be the first script); their source locations are not available. - - WebSocket connections and their frames are shown, but not the handshake headers, which the page cannot see. - - The screencast (the toolbar's device button, off by default) shows the viewport as [snapDOM](https://github.com/zumerlab/snapdom) redraws it from the DOM, not the pixels on screen: canvas, video and cross-origin images may be missing, and it updates about twice a second at most. snapDOM (about 90 KB gzip) is loaded only when the screencast starts or a screenshot is taken. - - Screenshots (`Page.captureScreenshot`, for tools and agents that drive the page over CDP) are rendered by snapDOM the same way, so they miss what the screencast misses. The response says so with a field Chrome does not send, `approximate: true`. Otherwise the parameters behave as in Chrome: `clip` is in page coordinates, the image has device pixels (`devicePixelRatio` times `clip.scale`), and `captureBeyondViewport` without a clip takes the whole page. An image past the browser's canvas limits (32767 pixels a side, 16384 on Safari, 16384 × 16384 in area) is scaled down to fit, and snapDOM warns about it in the page's console. Browsers that cannot encode WebP (Safari) return PNG when WebP is asked for. - - Device mode (shell mode only, since it needs the page in an iframe it can resize) sets the page's size, as the viewport the page sees: media queries, `vw`/`vh` and `innerWidth` follow. It does not change `devicePixelRatio`, the user agent or touch input, which a page cannot change: the toolbar offers no device pixel ratio or device type, and a device preset sets only the size. Screenshots from its menu are snapDOM's, as above, at the page's own pixel ratio. - - Input from the screencast is replayed as DOM events, which are not trusted (`isTrusted` is false): focus, clicks, scrolling and text editing are carried out, but pages that check `isTrusted` ignore them. - - Cross-origin style sheets are read through a fetched copy, which needs CORS headers; their rules are read-only. - - Forced pseudo-classes (`:hover`, `:focus`, … from the Styles pane) are emulated with companion style sheets that keep the cascade order; sheets from `@import` and `adoptedStyleSheets` are not covered. -- While the page has an element fullscreen (the Fullscreen API), DevTools and its F12 button cannot be used: the browser lets nothing outside the fullscreen element take input, in shell mode too. Leave fullscreen (Esc) to use them again. -- Host the frontend on a different origin from the page. On the same origin, DevTools' own settings share the page's `localStorage` and show up in the Application panel. -- The page side uses only standard web APIs and works in any modern browser. The DevTools UI is developed and tested by Google for Chrome; Firefox and Safari are not verified yet, and newer frontend versions may need extra compatibility patches. +| `<script>` attribute | Vite option | Effect | +|---|---|---| +| `data-open-immediately` | `openImmediately` | Opens DevTools as the page loads | +| `data-default-shell-mode="off"` | `defaultShellMode: 'off'` | No shell mode: the panel covers the page | +| `data-no-toggler` | `noToggler` | Hides the F12 button | +| `data-remote` | `remote` | Connects to a remote debugging server | -## Packaging +Shell mode (on by default when self-hosted) puts the page in an iframe, so DevTools stay open across reloads and device mode works: see [shell mode](docs/usage.md#shell-mode). -An umbrella package plus scoped packages under the [`@devtools-anywhere`](https://www.npmjs.com/org/devtools-anywhere) npm organization: +## What works -| Package | Contents | +| Panel | Support | |---|---| -| `devtools-anywhere` | Umbrella package: `probe.iife.js` (probe plus auto-mounting), `devtools-anywhere.iife.js` (the `<devtools-anywhere>` element, which the probe loads), `devtools-anywhere-service-worker.js`, `snapdom.js` (the renderer of the screencast and of screenshots, loaded on demand), and TypeScript types for the element, its events and the script and toggler attributes. There is no module to import: the scripts are the runtime | -| `@devtools-anywhere/devtools-frontend` | Prebuilt frontend static assets with English and Chinese UI, self-hostable or served from a CDN | -| `@devtools-anywhere/devtools-frontend-locales` | Optional: the other 80 UI languages, at the same paths as in the frontend package. Serve both from one directory (the Vite plugin does this when it is installed); a missing language falls back to English | -| `@devtools-anywhere/vite-plugin` | Vite plugin | -| `@devtools-anywhere/remote-server` | Remote debugging server (Node.js) and its CLI, `devtools-anywhere-remote` | +| Elements, Styles | ✅ Without the browser's default styles | +| Console | ✅ Including the page's earliest output | +| Network | ✅ No real timing; no WebSocket handshake headers | +| Sources | ⚠️ Files and snippets, no breakpoints | +| Application | ✅ | +| Device mode | ⚠️ Shell mode only; size only, not pixel ratio, user agent or touch | +| Screencast, screenshots | ⚠️ Redrawn from the DOM, not the screen's pixels | +| Performance, Memory, Profiler | ❌ Page scripts cannot get this data | -## Design +The page side uses only standard web APIs and works in any modern browser; the DevTools UI is verified in Chrome only. All the limits: see the [usage guide](docs/usage.md#limitations). -How it works, and why it is built this way: the probe, shell mode, injection, the technology choices and the packaging are described in [docs/design.md](docs/design.md). +## Packages -## Development - -See the [development guide](DEV.md) for setting up the environment, the repository layout and implementation rules, live rebuilds, tests, building the frontend, and releasing. +| Package | For | +|---|---| +| `devtools-anywhere` | The probe and the in-page element | +| `@devtools-anywhere/devtools-frontend` | The DevTools frontend (English and Chinese UI) | +| `@devtools-anywhere/devtools-frontend-locales` | Optional: the other 80 UI languages | +| `@devtools-anywhere/vite-plugin` | Vite plugin | +| `@devtools-anywhere/remote-server` | Remote debugging server and CLI | -## References +## Documentation -- [chobitsu](https://github.com/liriliri/chobitsu): reference for the CDP implementation -- [Chii](https://github.com/liriliri/chii): embedded mode in `src/target/DevtoolsFrame.ts`, and its remote debugging server -- [ChromeDevTools/devtools-frontend](https://github.com/ChromeDevTools/devtools-frontend) -- [ChromeDevTools/devtools-protocol](https://github.com/ChromeDevTools/devtools-protocol) -- [Puppeteer](https://github.com/puppeteer/puppeteer): used as the source of truth in conformance tests -- [solid-playground preview](https://github.com/solidjs/solid-playground/blob/master/packages/solid-repl/src/components/preview.tsx) +- [Usage guide](docs/usage.md): every way to set it up, the options, the limits +- [Design](docs/design.md): how it works and why +- [Development guide](DEV.md): setup, tests, releases ## License -devtools-anywhere is released under the [MIT](LICENSE) license. Each published package also ships its own `LICENSE` and, where its build contains third-party code, a `THIRD_PARTY_LICENSES.md`. - -Third-party code in the published packages, with copyright notices and full license texts, is listed in [THIRD_PARTY_LICENSES.md](THIRD_PARTY_LICENSES.md): - -- `@devtools-anywhere/devtools-frontend` and `@devtools-anywhere/devtools-frontend-locales` are built from the Chrome DevTools frontend (BSD-3-Clause, The Chromium Authors), including the libraries it bundles. -- `devtools-anywhere` bundles [snapDOM](https://github.com/zumerlab/snapdom) (MIT). -- The module lexer in `packages/devtools-anywhere/src/cdp-agent/core/lexer.ts` is ported from [es-module-lexer](https://github.com/guybedford/es-module-lexer) (MIT, Guy Bedford). +[MIT](LICENSE). Third-party code in the published packages (the Chrome DevTools frontend, snapDOM, es-module-lexer) and its licenses: [THIRD_PARTY_LICENSES.md](THIRD_PARTY_LICENSES.md). diff --git a/README.zh-CN.md b/README.zh-CN.md index 1fb285f..866cfe7 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -2,150 +2,101 @@ [English](README.md) | 简体中文 -在任意网页里使用真正的 Chrome DevTools,尤其适合没有开发者工具的移动端浏览器。 +在任意网页里用上真正的 Chrome DevTools,手机浏览器也可以。 -## 为什么 - -现有的页面内调试工具各有短板: - -| 工具 | 短板 | -|---|---| -| eruda / vConsole | 只能捕获初始化之后的输出。它们体积较大,通常异步加载,页面最开始的日志和报错会丢失 | -| mobile-devtools | 自己实现的 UI,不是 Chrome DevTools;通过框架适配器初始化,晚于应用代码启动(根据其 README 判断,未验证) | -| Chii | 能做到早期捕获,但接入和配置比较繁琐,前端停留在旧版 Chrome DevTools(约 122) | +<figure> +<p align="center"> + <img src="docs/images/desktop.webp" alt="桌面:设备模式下以横屏 iPhone SE 尺寸显示的页面,元素面板停靠在右侧" width="62%"> + <img src="docs/images/mobile.webp" alt="手机:页面下方的元素面板" width="22%"> +</p> +<figcaption><p align="center"><sub>截图:在 <a href="https://github.com/jerrychan7/WebMC">WebMC</a>(基于 WebGL 的网页版 Minecraft)中使用 devtools-anywhere。左:电脑上的设备模式;右:手机上。</sub></p></figcaption> +</figure> -本项目的目标: - -1. **不丢最早的输出**:从页面第一行脚本开始,console、报错、网络请求都能看到。 -2. **接入零配置**:一行 `<script>` 或一个构建插件就能用。 -3. **刷新不丢 devtools**:页面刷新或跳转时,devtools 保持打开,日志可以持续保留。 -4. **持续跟进新版 Chrome DevTools**:基于官方 devtools-frontend 维护一组补丁,定期升级,不长期停留在旧版本。 - -## 用法(预览) - -各个包已发布到 npm(`npm i devtools-anywhere @devtools-anywhere/devtools-frontend`),下面的路径指它们的 `dist/` 目录,本仓库构建出的文件也一样。`data-devtools-frontend-url` 指向 `@devtools-anywhere/devtools-frontend` 构建产物里的 `anywhere_app.html`。产物里 `bundle/` 下的文件以内容命名,可以永久缓存;`anywhere_app.html` 本身要带重新验证提供(`Cache-Control: no-cache`),更新才能到达浏览器。 +## 为什么 -### 一行 script +| | devtools-anywhere | eruda / vConsole | Chii | +|---|---|---|---| +| 界面 | 最新的 Chrome DevTools | 自己实现的面板 | 旧版 Chrome DevTools(约 122) | +| 页面最早的日志和报错 | ✅ 从第一行脚本开始 | ❌ 初始化前的会丢 | ✅ | +| 接入 | 一行 `<script>` 或 Vite 插件 | 一行 `<script>` | 比较繁琐 | -```html -<head> - <!-- 第一个同步脚本:从最开始捕获输出。 --> - <script src="/path/to/devtools-anywhere/dist/probe.iife.js" data-devtools-frontend-url="/path/to/devtools-frontend/dist/anywhere_app.html"></script> - ... -</head> -``` +## 快速上手 -也可以直接用 CDN:jsDelivr 和 unpkg 会提供所有 npm 包,不用自己部署。请固定到确切的版本号,两个包用同一个版本。这种方式只有英文和中文界面,因为其他语言在单独的包里,必须和前端放在同一目录下提供。外壳模式也用不了,因为它要求探针、bundle 和前端都与页面同源(见[外壳模式](docs/design.zh-CN.md#2-外壳模式原地改写)):用 `data-default-shell-mode="off"` 把 devtools 直接嵌在页面里。 +**Vite 项目**,只在 `vite serve` 时生效: -```html -<!-- jsDelivr --> -<script src="https://cdn.jsdelivr.net/npm/devtools-anywhere@0.1.0/dist/probe.iife.js" data-devtools-frontend-url="https://cdn.jsdelivr.net/npm/@devtools-anywhere/devtools-frontend@0.1.0/dist/anywhere_app.html" data-default-shell-mode="off"></script> -<!-- unpkg --> -<script src="https://unpkg.com/devtools-anywhere@0.1.0/dist/probe.iife.js" data-devtools-frontend-url="https://unpkg.com/@devtools-anywhere/devtools-frontend@0.1.0/dist/anywhere_app.html" data-default-shell-mode="off"></script> +```bash +npm i -D @devtools-anywhere/vite-plugin ``` -页面加载完成后,探针会从同目录(或 `data-bundle` 指定的地址)加载 `devtools-anywhere.iife.js`,并在 `<html>` 下、与 `<body>` 同级挂载它自己的 `<devtools-anywhere>`,不管页面里有没有 `<devtools-anywhere>`;每个页面只有一个这样的全局元素,再出现的只在控制台警告。可选属性:`data-open-immediately` 立即打开 devtools;`data-default-shell-mode="off"` 不进入外壳模式(顶层页面默认 `on`,即进入);`data-shell-mode-locked` 锁定外壳模式,始终按 `data-default-shell-mode` 执行,忽略用户的选择(已存的选择保留)并隐藏 DevTools 里的开关,但页面禁止被嵌入 iframe 时仍会退回普通模式;`data-no-toggler` 隐藏 F12 按钮,外壳模式下同样生效;`data-service-worker` 在外壳模式下启用 Service Worker,它给页面 iframe 中没有引入探针的同源页面注入探针,并为同源的文档、样式表、脚本、图片等请求补上请求头和响应头;默认关闭,使用条件见[注入方式](docs/design.zh-CN.md#3-注入方式)。 - -### Vite - ```js // vite.config.js import devtoolsAnywhere from '@devtools-anywhere/vite-plugin' -export default { - plugins: [devtoolsAnywhere()], -} +export default { plugins: [devtoolsAnywhere()] } ``` -只影响 `vite serve`:探针作为第一个脚本注入每个页面,bundle、Service Worker 和前端都在 `/@devtools-anywhere/` 下提供。前端默认来自 `@devtools-anywhere/devtools-frontend`。选项:`devtoolsFrontendPath`(本地另一份前端构建的入口文件路径,如 `…/anywhere_app.html`,托管它所在的目录)、`devtoolsFrontendUrl`(托管在别处的前端,设置后不再托管本地文件)、`openImmediately`、`defaultShellMode`(`'on'` 或 `'off'`)、`shellModeLocked`、`noToggler`(隐藏 F12 按钮)、`serviceWorker`(使用条件见[注入方式](docs/design.zh-CN.md#3-注入方式),`Service-Worker-Allowed` 头由插件返回),与探针的属性一一对应;`remote`([远程调试](#远程调试)服务端的地址,页面同时连接到它,组件照常挂载),以及配合它的 `idInQuery`(`data-id-in-query`,对应服务端的 `idInQuery`)。 - -### 远程调试 - -要在电脑上的 Chrome 里调试手机上的页面,先在电脑上启动远程调试服务端: - -```bash -npx -p @devtools-anywhere/remote-server devtools-anywhere-remote --port 8080 -``` - -它会打印目标列表的地址(带 token)和给页面用的 script 标签,标签里用的是电脑的局域网地址(`--public-url` 可以改成别的地址,例如经过代理时)。从服务端加载探针,带上 `data-remote`,放在页面 `<head>` 的第一个脚本位置: +**任意页面**,用 CDN,放在 `<head>` 里第一个(CDN 上不支持外壳模式,所以关掉): ```html -<script src="http://192.168.1.2:8080/probe.iife.js" data-remote></script> +<script src="https://cdn.jsdelivr.net/npm/devtools-anywhere@0.1.0/dist/probe.iife.js" + data-devtools-frontend-url="https://cdn.jsdelivr.net/npm/@devtools-anywhere/devtools-frontend@0.1.0/dist/anywhere_app.html" + data-default-shell-mode="off"></script> ``` -页面加载完成后连接到服务端(早期输出照常捕获),并出现在目标列表中;列表可以筛选,也会显示连着几个 DevTools。点“inspect”会在新标签页里打开它的 DevTools。页面在同一个标签页里刷新或跳转时,DevTools 保持连接;连接断开后页面会自动重连,期间它仍留在列表中。一个页面可以同时被多个 DevTools 调试,和 Chrome 一样各有独立的会话:页面为每个会话运行一个 CDP 会话。探针从别处加载时,`data-remote` 也可以写服务端地址(`data-remote="http://host:8080/"`)。它像链接一样相对探针的地址解析,所以挂在路径下的服务端要带结尾斜杠:`/devtools/` 指 `/devtools/` 下的服务端,`/devtools` 指的却是 `/` 下的。再加上 `data-devtools-frontend-url` 时,页面同时还有自己的 DevTools 面板,两边可以同时调试,各自有独立的会话。外壳模式与不带 `data-remote` 时一样(它要求探针、bundle 和前端同源,所以探针从服务端加载、前端在别处时不会进入):连接服务端的是外壳里的页面,外壳本身不连。再加上 `data-service-worker` 时,页面 iframe 跳转到的没有引入探针的同源页面也会连接:Service Worker 给它们注入探针,它们连接同一个服务端。外壳里的页面(自带探针或由 Service Worker 注入)连不连远程由外壳的探针决定:外壳的探针没有配服务端时都不连,页面自己的 `data-remote` 不起作用。这个服务端地址由外壳保存在内存里,不和其余外壳配置一起写进 `sessionStorage`,外壳里的页面加载完成后再由外壳告诉它,所以页面脚本无法让之后的页面连到别处。 +打开页面,点 **F12** 按钮。 -- 服务端同时提供探针、bundle 和 DevTools 前端;前端运行在电脑上,不受“前端要和页面不同源”的限制。 -- 调试一个页面就意味着能在其中执行任意脚本:目标列表和 DevTools 连接都需要 token(`--token <值>`,默认随机生成;`--no-token` 关闭)。页面连接不需要 token。 -- HTTPS 页面只能连接 HTTPS 服务端:使用 `--cert` 和 `--key`。其他选项:`--host`、`--base-path`、`--id-in-query`(即下文的 `idInQuery`)、`--devtools-frontend-path`,以及 `--devtools-frontend-url`(打开托管在别处的前端,如 CDN;HTTPS 的前端要求服务端也用 HTTPS;它的链接把 `ws=`/`wss=` 和 token 放在 URL 片段里,所以不会发给 CDN)。在 Node.js 中也可以调用 `startRemoteServer(options)`;`createRemoteHandler(options)` 提供请求和 upgrade 两个处理函数,可以挂在自己的服务(Express、Koa 等)的某个 base path 下,不属于它的请求返回 false。页面连接 `<base>target/<id>`,DevTools 连接 `<base>client/<id>`;宿主只能按精确路径分发 WebSocket upgrade 时,用 `idInQuery: true`,两者都连到 base path 本身(`<base>?target=<id>`、`<base>?client=<id>`):目标列表和打印的 script 标签改用这种形式,标签上带 `data-id-in-query`,宿主只需为 upgrade 注册 base path(带结尾斜杠)。两种形式始终都接受。单独打开的前端可以用 `anywhere_app.html?ws=<host:port/path>`(HTTPS 用 `wss=`;也可以从片段 `#?ws=` 读取)连接任一目标,参数是去掉协议的 WebSocket 地址,和 Chrome 的 `devtools_app.html?ws=` 一样。 -- `/json/list`(带 token)按 Chrome 的格式列出目标。 +**在电脑上调试手机里的页面**: -### 组件 +```bash +npx -p @devtools-anywhere/remote-server devtools-anywhere-remote +``` -点击右下角的 F12 悬浮按钮打开面板;拖动面板内侧边缘正中、靠页面一侧的抓手调整大小(外壳模式下也有,另外还能用 DevTools 自己的分隔条,见[外壳模式](docs/design.zh-CN.md#2-外壳模式原地改写))。DevTools 设置按钮左边,锁形按钮开关外壳模式(开启前有对话框说明,开启后顶部有提示条),刷新按钮重新加载页面;工具栏上的 × 关闭面板。DevTools 主菜单(⋮)里有“停靠位置”:底部(默认)、右侧、左侧,或取消停靠,变成页面上方的浮动窗口,拖动横条移动、拖动右下角调整大小。外壳模式下页面会让出空间,和 Chrome 一样;普通模式下面板盖在页面上。停靠位置和尺寸会记在 localStorage 里;尺寸记的是占视口的百分比(`dvh`/`dvw`),换屏幕或横竖屏切换后依然合适。唯一的属性 `devtools-frontend-url` 是可选的,不写时沿用探针的 `data-devtools-frontend-url`。其余状态都由库维护:探针的参数决定组件的配置,打开和关闭由用户操作;组件没有自己的方法。页面可以用 `devtools-anywhere::part(toggler)` 修改 F12 按钮的样式,外壳模式下同样有效:此时按钮在页面 iframe 里,由页面 `<html>` 下一个只承载按钮的 `<devtools-anywhere>` 显示,点击开关外壳的 DevTools(只有 iframe 里的页面没有探针时,例如其他源的页面,外壳才显示自己的按钮)。探针挂载的组件不把它的 iframe 暴露为部件。组件要固定挂在一个位置,在 DOM 中移动它会导致 iframe 重新加载。 +把打印出的 `<script>` 加进页面,在电脑上打开打印出的目标列表,点「inspect」。 -页面也可以自己放置 `<devtools-anywhere>`,作为显示 DevTools 的一个区域:它是页面文档流里的块级盒子(`display: block`),尺寸由页面的 CSS 决定,一连接就加载 DevTools。页面用 CSS 或 `hidden` 控制它的显示和隐藏;隐藏时 DevTools 不会卸载。它没有 F12 按钮,不能停靠或拖拽调整大小,也不会进入外壳模式(DevTools 里没有入口);在外壳里的页面中照常工作。它只有 `devtools-frontend-url` 一个属性,其他属性一律忽略。里面的 DevTools iframe 可以用 `devtools-anywhere::part(iframe)` 设置样式。 +自托管、全部选项和组件的用法见[使用指南](docs/usage.zh-CN.md)。 -页面上任意元素都能开关面板:加上 `data-devtools-anywhere-toggle`,留空则指向探针挂载的组件。值为页面自己放置的某个组件的 id 时,点击只会在该组件上派发 `devtools-toggle-request` 事件,由页面自行处理;这类切换器不设置 `aria-expanded`。点击在 document 上统一捕获,后加入的切换器同样生效;不阻止默认行为,切换器的 `aria-expanded` 跟随面板状态。找不到目标时只打一条警告(同一个值只警告一次)。面板是否打开只能从切换器的 `aria-expanded` 得知,组件本身不派发事件。 +## 常用选项 -```html -<button data-devtools-anywhere-toggle>DevTools</button> -``` +| `<script>` 属性 | Vite 选项 | 作用 | +|---|---|---| +| `data-open-immediately` | `openImmediately` | 页面加载后直接打开 DevTools | +| `data-default-shell-mode="off"` | `defaultShellMode: 'off'` | 不用外壳模式,面板盖在页面上 | +| `data-no-toggler` | `noToggler` | 隐藏 F12 按钮 | +| `data-remote` | `remote` | 连接到远程调试服务端 | -切换器在探针加载 `devtools-anywhere.iife.js`(由它注册组件)后才生效,放在 shadow root 里不起作用。外壳模式下,外壳里那个页面的切换器一律控制外壳的面板,忽略属性值。与外壳不同源的页面(如跨源 iframe)里的切换器不起作用。 +外壳模式(自己部署时默认开启)把页面放进 iframe,刷新时 DevTools 不会关闭,也能用设备模式,见[外壳模式](docs/usage.zh-CN.md#外壳模式)。 -## 能力边界 +## 能用什么 -- 页面内的 CDP 实现只能基于 JS API,因此以下能力**无法提供**:断点调试、Profiler、Memory、真实的网络时序。这些面板不打包进前端,而不是展示给用户后再让他发现不能用。页面脚本里的 `debugger;` 语句同样无法暂停;在外壳模式下,同源脚本中执行到的每个 `debugger;` 会改为在控制台输出一条带堆栈的警告。真正的断点要靠浏览器自带的调试器(Android 用 `chrome://inspect`,iOS 用 Safari 的 Web Inspector);[远程调试](#远程调试)改变不了这一点,因为页面里运行的仍是同一个页面内 CDP agent。源代码面板的“网页”标签页能列出页面的文档、外链脚本和样式表、图片和字体(内联脚本不列出,因为拿不到它们在文档中的位置);代码段可以用。 -- 页面脚本缺少相应 API 的地方,要么精确补上,要么不做;不会把近似结果当作事实展示: - - 不显示浏览器默认样式(user-agent styles):浏览器不开放它的默认样式表,内置一份副本又可能和浏览器实际使用的不一致。 - - 事件监听器由探针从它执行的那一刻起记录(探针必须是第一个脚本);拿不到处理函数的源码位置。 - - 能显示 WebSocket 连接和帧,但没有握手阶段的请求头和响应头,页面拿不到它们。 - - 投屏(工具栏上的设备按钮,默认关闭)显示的是 [snapDOM](https://github.com/zumerlab/snapdom) 根据 DOM 重新绘制的视口,不是屏幕上的真实像素:canvas、视频和跨域图片可能缺失,每秒最多更新约两次。snapDOM(gzip 后约 90 KB)只在开始投屏或截图时加载。 - - 截图(`Page.captureScreenshot`,供通过 CDP 操作页面的工具和 agent 使用)同样由 snapDOM 绘制,投屏缺失的内容截图里也会缺失。返回结果里会多一个 Chrome 没有的字段 `approximate: true` 来说明这一点。其余参数的行为和 Chrome 一致:`clip` 按页面坐标计算,图片按设备像素输出(`devicePixelRatio` 乘以 `clip.scale`),不带 `clip` 时设置 `captureBeyondViewport` 会截取整个页面。超出浏览器画布上限(单边 32767 像素,Safari 上 16384;面积 16384 × 16384)的图片会等比缩小,snapDOM 会在页面的控制台里给出警告。不支持编码 WebP 的浏览器(Safari)在请求 WebP 时返回 PNG。 - - 设备模式(只在外壳模式下提供,因为它要求页面在一个可以调整尺寸的 iframe 里)设置的是页面的尺寸,也就是页面看到的视口:媒体查询、`vw`/`vh` 和 `innerWidth` 都会随之变化。它不改变 `devicePixelRatio`、UA 和触摸输入,这些是页面改不了的:工具栏里没有设备像素比和设备类型,选择设备预设时只套用尺寸。它菜单里的截图同样由 snapDOM 绘制,按页面自己的像素比输出。 - - 投屏上的输入以 DOM 事件重放,这些事件不是可信事件(`isTrusted` 为 false):聚焦、点击、滚动和文本编辑都会执行,但检查 `isTrusted` 的页面会忽略它们。 - - 跨域样式表通过重新下载的副本读取,需要对方返回 CORS 头;这些规则是只读的。 - - 强制伪类(样式面板里的 `:hover`、`:focus` 等)通过伴随样式表模拟,层叠顺序保持不变;`@import` 引入的样式表和 `adoptedStyleSheets` 不在覆盖范围内。 -- 页面让某个元素全屏(Fullscreen API)期间,devtools 和它的 F12 按钮都无法使用:浏览器不让全屏元素以外的任何内容接收输入,外壳模式下也一样。退出全屏(Esc)后即可恢复。 -- 前端要部署在和页面不同的源上。如果同源,devtools 自己的设置会写进页面的 `localStorage`,并出现在 Application 面板里。 -- 被调试页面一侧只用标准 Web API,可以运行在任何现代浏览器中。devtools 界面由 Google 针对 Chrome 开发和测试,Firefox 和 Safari 尚未验证;新版前端可能需要额外的兼容补丁。 +| 面板 | 支持情况 | +|---|---| +| 元素、样式 | ✅ 不显示浏览器默认样式 | +| 控制台 | ✅ 包括页面最早的输出 | +| 网络 | ✅ 没有真实的时序;WebSocket 握手头看不到 | +| 源代码 | ⚠️ 能看文件、用代码段,不能下断点 | +| 应用 | ✅ | +| 设备模式 | ⚠️ 仅外壳模式,只改尺寸,不改像素比、UA 和触摸 | +| 投屏、截图 | ⚠️ 从 DOM 重绘,不是屏幕像素 | +| 性能、内存、Profiler | ❌ 页面脚本拿不到这些数据 | -## 发布形式 +页面这边只用标准 Web API,现代浏览器都能用;DevTools 界面只在 Chrome 上验证过。完整的限制见[使用指南](docs/usage.zh-CN.md#能力边界)。 -发布一个总包,以及 npm 组织 [`@devtools-anywhere`](https://www.npmjs.com/org/devtools-anywhere) 下的几个子包: +## 包 -| 包 | 内容 | +| 包 | 用途 | |---|---| -| `devtools-anywhere` | 总包:`probe.iife.js`(探针加自动挂载)、`devtools-anywhere.iife.js`(`<devtools-anywhere>` 组件,由探针加载)、`devtools-anywhere-service-worker.js`、`snapdom.js`(投屏和截图的绘制器,按需加载),以及组件、事件、探针脚本和切换器属性的 TypeScript 类型。没有可以 import 的模块,运行时只有这些脚本 | -| `@devtools-anywhere/devtools-frontend` | 构建好的前端静态资源,界面带英文和中文,可以自托管,也可以从 CDN 引用 | -| `@devtools-anywhere/devtools-frontend-locales` | 可选:其余 80 种界面语言,文件路径与前端包一致。两个包放在同一目录下提供即可(装了它的话 Vite 插件会自动这样做);缺少的语言会退回英文 | +| `devtools-anywhere` | 探针和页面里的组件 | +| `@devtools-anywhere/devtools-frontend` | DevTools 前端(英文、中文界面) | +| `@devtools-anywhere/devtools-frontend-locales` | 可选:另外 80 种界面语言 | | `@devtools-anywhere/vite-plugin` | Vite 插件 | -| `@devtools-anywhere/remote-server` | 远程调试服务端(Node.js)及其命令行工具 `devtools-anywhere-remote` | - -## 设计 - -工作原理和设计取舍(探针、外壳模式、注入方式、选型、拆包理由)见 [docs/design.zh-CN.md](docs/design.zh-CN.md)。 - -## 开发 +| `@devtools-anywhere/remote-server` | 远程调试服务端和命令行 | -环境配置、目录结构与实现约束、实时重新构建、测试、构建前端和发布,见[开发指南](DEV.zh-CN.md)。 +## 文档 -## 参考 - -- [chobitsu](https://github.com/liriliri/chobitsu):CDP 实现的参考 -- [Chii](https://github.com/liriliri/chii):嵌入模式 `src/target/DevtoolsFrame.ts`,以及它的远程调试服务端 -- [ChromeDevTools/devtools-frontend](https://github.com/ChromeDevTools/devtools-frontend) -- [ChromeDevTools/devtools-protocol](https://github.com/ChromeDevTools/devtools-protocol) -- [Puppeteer](https://github.com/puppeteer/puppeteer):一致性测试中作为标准答案 -- [solid-playground preview](https://github.com/solidjs/solid-playground/blob/master/packages/solid-repl/src/components/preview.tsx) +- [使用指南](docs/usage.zh-CN.md):所有接入方式、选项、限制 +- [设计](docs/design.zh-CN.md):工作原理和取舍 +- [开发指南](DEV.zh-CN.md):搭建环境、测试、发布 ## 许可证 -devtools-anywhere 以 [MIT](LICENSE) 许可发布。每个发布包都带有自己的 `LICENSE`;产物中含第三方代码的包另附 `THIRD_PARTY_LICENSES.md`。 - -发布包中的第三方代码及其版权声明、完整许可文本见 [THIRD_PARTY_LICENSES.md](THIRD_PARTY_LICENSES.md): - -- `@devtools-anywhere/devtools-frontend` 与 `@devtools-anywhere/devtools-frontend-locales` 由 Chrome DevTools frontend 构建(BSD-3-Clause,The Chromium Authors),包含它打包的各个库。 -- `devtools-anywhere` 打包了 [snapDOM](https://github.com/zumerlab/snapdom)(MIT)。 -- `packages/devtools-anywhere/src/cdp-agent/core/lexer.ts` 中的模块词法分析器移植自 [es-module-lexer](https://github.com/guybedford/es-module-lexer)(MIT,Guy Bedford)。 +[MIT](LICENSE)。发布包中的第三方代码(Chrome DevTools 前端、snapDOM、es-module-lexer)及其许可证见 [THIRD_PARTY_LICENSES.md](THIRD_PARTY_LICENSES.md)。 diff --git a/docs/design.md b/docs/design.md index 7ba4a99..d884221 100644 --- a/docs/design.md +++ b/docs/design.md @@ -2,7 +2,7 @@ English | [简体中文](design.zh-CN.md) -How devtools-anywhere works and why it is built this way. For using it, see the [README](../README.md); for working on it, the [development guide](../DEV.md). +How devtools-anywhere works and why it is built this way. For using it, see the [usage guide](usage.md); for working on it, the [development guide](../DEV.md). ## Technology choices @@ -34,7 +34,7 @@ This project depends on neither Chii nor chobitsu. Their source is read as a ref A synchronous script of about 6.2 KB (min+gzip, including shell mode, auto-mounting and remote debugging) that must be the first script executed in `<head>`. -- Immediately hooks `console`, `window.onerror`, `unhandledrejection`, `fetch`, `XHR` and `WebSocket`, and writes events with timestamps into a buffer. WebSocket frames are kept as given; binary payloads are only converted to base64 by the CDP agent. Until a CDP session mounts, each list (console messages, requests, WebSockets, frames per WebSocket) keeps its last 10,000 entries, a bound for pages that never load one; the session then sets its own: it keeps the last 1000 console messages like V8, and every request and WebSocket like Chrome, dropping only the oldest response bodies past the buffer sizes given to `Network.enable` (Chrome's defaults when none are). +- Immediately hooks `console`, `window.onerror`, `unhandledrejection`, `fetch`, `XHR` and `WebSocket`, and writes events with timestamps into a buffer. WebSocket frames are kept as given; binary payloads are only converted to base64 by the CDP agent. Until a CDP session mounts, each list (console messages, requests, WebSockets, frames per WebSocket) keeps its last 10,000 entries, a bound for pages that never load one. The session then sets its own limits: the last 1000 console messages, like V8; every request and WebSocket, like Chrome, dropping only the oldest response bodies past the buffer sizes given to `Network.enable` (Chrome's defaults when none are). - Requests that cannot be hooked (the document, style sheets, scripts, images, fonts, …) are recorded from Resource Timing; a buffered `PerformanceObserver` also delivers those from before the probe ran. They have URL, type, timing, size and (where the browser exposes it) status and MIME type, but no headers; their bodies are loaded again from the HTTP cache on demand. In shell mode with the Service Worker, it adds the request and response headers of same-origin ones. Hooks stay first because they work from the very start, keep call stacks, and need no deployment; a Service Worker only controls pages after it is installed, conflicts with a site's own, and would have to proxy requests to see responses. - The CDP runtime and the frontend are loaded lazily. The runtime's Runtime and Network domains consume the probe's buffer directly and subscribe to new events, so every API is hooked only once. @@ -50,7 +50,7 @@ No standalone `devtools.html` is hosted, and there is no navigation to a blob UR 2. The probe, being the first script, sees the config in the top-level window. It writes an unclosed `<plaintext>` so the rest of the page becomes inert text (none of its scripts run), then replaces the document with the shell via `document.open()` / `document.write()`. The shell contains the page iframe (pointing to `location.href`) and `<devtools-anywhere>`, which knows it is the shell's element by the document it is in. (Chrome ignores `document.open()` after `window.stop()`, hence `<plaintext>`.) 3. Inside the iframe, the probe captures as usual and loads the bundle, which connects the page's CDP session to the shell. The shell relays messages between the page and the frontend. 4. When the page reloads or navigates, the new page says hello; the shell tells the frontend the page navigated and re-sends every `enable` command the frontend has sent, so the new page replays its own earliest output. The address bar and title follow the page, through loads, history traversals and same-document navigations (`pushState` where the Navigation API exists), so that a reload or a copied link opens the page shown. -5. DevTools fills the shell and lays out the page and itself, as in Chrome: the page iframe goes where the frontend's page area is, on top of it. Dock side and sizes are DevTools' own (its resizer, and a grip against the middle of DevTools' edge on the page's side, like a tab of the panel, larger on touch screens). Undocked, DevTools is a window over the page. The toolbar's device button is device mode, as in Chrome, in place of the screencast: the page iframe is sized as the device and scaled to fit, and the toolbar's menu takes screenshots. Undocked, device mode's toolbar is drawn in a page of the frontend's (`device_mode_emulation_frame.html`, as Chrome does) shown under the window. +5. DevTools fills the shell and lays out the page and itself, as in Chrome: the page iframe goes where the frontend's page area is, on top of it. Dock side and sizes are DevTools' own, set with its resizer. A grip also sits against the middle of DevTools' edge, on the page's side, like a tab of the panel; it is larger on touch screens. Undocked, DevTools is a window over the page. The toolbar's device button is device mode, as in Chrome, in place of the screencast: the page iframe is sized as the device and scaled to fit, and the toolbar's menu takes screenshots. Undocked, device mode's toolbar is drawn in a page of the frontend's (`device_mode_emulation_frame.html`, as Chrome does) shown under the window. 6. The toolbar's × hides DevTools and stays in shell mode; the page fills the shell at its own size until DevTools is shown again. The lock button again clears the config and reloads the page on its own; the choice is remembered in `localStorage`, and wins over the page's `data-default-shell-mode` from then on, unless the page sets `data-shell-mode-locked`. The stored URLs are only trusted when they have the same origin as the probe script; otherwise the config is cleared and the page loads normally. Shell mode therefore needs the probe, the bundle and the frontend served from the page's origin. @@ -69,8 +69,17 @@ All methods share the same probe. ## Packaging -Why the packages listed in the [README](../README.md#packaging) are split this way: +Why the packages listed in the [usage guide](usage.md#packages) are split this way: - The in-page CDP agent and its probe are not a separate package: the probe and the domains share the same hooks and buffers, and only `devtools-anywhere` uses them. They live in `devtools-anywhere` (`src/cdp-agent/`), which rules out version mismatches; the probe is still built as a separate tiny file, `probe.iife.js`. - The frontend is separate because it is large (about 16 MB) and follows Chrome's release cadence. Its other languages (about 42 MB) are another package, so that only those who need them install them. - All packages share one version number and are released together, so matching versions always work together. + +## References + +- [chobitsu](https://github.com/liriliri/chobitsu): reference for the CDP implementation +- [Chii](https://github.com/liriliri/chii): embedded mode in `src/target/DevtoolsFrame.ts`, and its remote debugging server +- [ChromeDevTools/devtools-frontend](https://github.com/ChromeDevTools/devtools-frontend) +- [ChromeDevTools/devtools-protocol](https://github.com/ChromeDevTools/devtools-protocol) +- [Puppeteer](https://github.com/puppeteer/puppeteer): used as the source of truth in conformance tests +- [solid-playground preview](https://github.com/solidjs/solid-playground/blob/master/packages/solid-repl/src/components/preview.tsx) diff --git a/docs/design.zh-CN.md b/docs/design.zh-CN.md index d2acb66..67a6733 100644 --- a/docs/design.zh-CN.md +++ b/docs/design.zh-CN.md @@ -2,7 +2,7 @@ [English](design.md) | 简体中文 -devtools-anywhere 的工作原理和设计取舍。使用方法见 [README](../README.zh-CN.md),参与开发见[开发指南](../DEV.zh-CN.md)。 +devtools-anywhere 的工作原理和设计取舍。使用方法见[使用指南](usage.zh-CN.md),参与开发见[开发指南](../DEV.zh-CN.md)。 ## 选型 @@ -10,7 +10,7 @@ devtools-anywhere 的工作原理和设计取舍。使用方法见 [README](../R |---|---|---| | CDP 实现 | 在本项目中自行实现,参考 [chobitsu](https://github.com/liriliri/chobitsu) | 在页面内调试只能通过 JS API 采集数据,所以只能用 JS/TS 实现。自行实现可以让探针和各个 CDP 域共用同一套 hook(不再需要回放技巧),修掉 chobitsu 的已知问题,并通过 [`devtools-protocol`](https://github.com/ChromeDevTools/devtools-protocol) 的类型跟进最新协议 | | 前端 | 官方 devtools-frontend,从源码构建,加一个很小的补丁集 | 构建时钉住官方的某个版本,补丁以 `.patch` 文件维护,升级时逐个重放,不维护长期 fork 分支。补丁只负责注册我们自己的入口(`anywhere_app`),其余定制都不改上游代码(见[修改前端](../DEV.zh-CN.md#修改前端)) | -| 隔离 | iframe | 隔离 devtools 自身的 DOM、日志和请求,避免它们出现在被调试页面中,也避免与页面的全局环境互相干扰 | +| 隔离 | iframe | 隔离 DevTools 自身的 DOM、日志和请求,避免它们出现在被调试页面中,也避免与页面的全局环境互相干扰 | 本项目不依赖 Chii 和 chobitsu。遇到边界情况时会阅读它们的源码作为参考,但不复制代码;以真实 Chrome 的 CDP 输出和协议定义为准。 @@ -19,13 +19,13 @@ devtools-anywhere 的工作原理和设计取舍。使用方法见 [README](../R ``` ┌──────────── 顶层窗口(外壳)─────────────┐ │ ┌──────────────────────────────────┐ │ -│ │ 用户页面 iframe │ │ +│ │ 页面 iframe │ │ │ │ 探针(<head> 首个同步脚本) │ │ │ │ └─ 缓冲 → CDP 运行时(延迟加载) │ │ │ └──────────────┬───────────────────┘ │ │ postMessage(校验 origin) │ │ ┌──────────────┴───────────────────┐ │ -│ │ devtools iframe │ │ +│ │ DevTools iframe │ │ │ └──────────────────────────────────┘ │ └────────────────────────────────────────┘ ``` @@ -34,28 +34,28 @@ devtools-anywhere 的工作原理和设计取舍。使用方法见 [README](../R 压缩后约 6.2 KB(min+gzip,包含外壳模式、自动挂载和远程调试)的同步脚本,必须是 `<head>` 中第一个执行的脚本。 -- 立即 hook `console`、`window.onerror`、`unhandledrejection`、`fetch`、`XHR` 和 `WebSocket`,把事件连同时间戳写入缓冲。WebSocket 帧按原样保存,二进制负载到 CDP agent 才转成 base64。CDP 会话挂载之前,每类记录(控制台消息、请求、WebSocket、每个 WebSocket 的帧)各保留最近 1 万条,作为从不加载 CDP agent 的页面的兜底;CDP 会话挂载后改由它设定上限,控制台消息与 V8 一样保留最近 1000 条。 +- 立即 hook `console`、`window.onerror`、`unhandledrejection`、`fetch`、`XHR` 和 `WebSocket`,把事件连同时间戳写入缓冲。WebSocket 帧按原样保存,二进制负载到 CDP agent 才转成 base64。CDP 会话挂载之前,每类记录(控制台消息、请求、WebSocket、每个 WebSocket 的帧)各保留最近 1 万条,作为从不加载 CDP agent 的页面的兜底;CDP 会话挂载后改由它设定上限:控制台消息与 V8 一样保留最近 1000 条;请求和 WebSocket 与 Chrome 一样全部保留,只在超出 `Network.enable` 给定的缓冲大小(未给时用 Chrome 的默认值)后丢弃最早的响应体。 - 无法 hook 的请求(文档、样式表、脚本、图片、字体等)通过 Resource Timing 记录;带 `buffered` 的 `PerformanceObserver` 还能拿到探针运行之前的请求。这些记录有 URL、类型、时序、大小,以及浏览器公开时的状态码和 MIME 类型,但没有请求头和响应头;响应体在需要时从 HTTP 缓存重新读取。外壳模式下启用了 Service Worker 时,它会为同源请求补上请求头和响应头。之所以仍以 hook 为主:hook 从最开始就生效、带调用栈、无需部署;Service Worker 要安装后才能接管页面,会和网站自己的 Service Worker 冲突,而且要代理请求才能看到响应。 - CDP 运行时和前端延迟加载。运行时的 Runtime、Network 域直接读取探针的缓冲并订阅新事件,因此每个 API 只被 hook 一次。 页面外没有任何途径能观察页面内的输出,只有比业务代码更早执行的脚本才能捕获最早的输出。完整的 CDP 运行时体积太大,不适合同步加载,所以只把最小的捕获逻辑放在最前面。 -这个方案已经验证可行,见 [experiments/early-capture/RESULTS.md](../experiments/early-capture/RESULTS.md):用约 1.4 KB 的探针加延迟加载的 chobitsu,所有早期的 console 调用、异常和请求都进入了 devtools,对象可以展开,时间戳和调用栈都是原始的。 +这个方案已经验证可行,见 [experiments/early-capture/RESULTS.md](../experiments/early-capture/RESULTS.md):用约 1.4 KB 的探针加延迟加载的 chobitsu,所有早期的 console 调用、异常和请求都进入了 DevTools,对象可以展开,时间戳和调用栈都是原始的。 ### 2. 外壳模式(原地改写) -不托管独立的 `devtools.html`,也不跳转到 blob URL,因为 blob 会导致刷新失效、地址无法分享。做法是: +不托管独立的 `DevTools.html`,也不跳转到 blob URL,因为 blob 会导致刷新失效、地址无法分享。做法是: -1. 使用零配置探针(`data-devtools-frontend-url`,或 Vite 插件)时,顶层页面首次加载就默认进入外壳模式:探针在页面脚本运行前自己把前端地址和 bundle 地址写入 `sessionStorage`,不需要再刷新一次。除非设置了 `data-open-immediately`,devtools 一开始是隐藏的。其他情况下,点击 DevTools 工具栏上的锁形按钮“Keep DevTools open across reloads”,写入这些地址后刷新页面。 -2. 探针作为第一个脚本,在顶层窗口检测到配置后,先写入一个不闭合的 `<plaintext>`,让页面剩余的内容变成纯文本(页面脚本都不会执行),再用 `document.open()` / `document.write()` 把文档替换成外壳。外壳中有指向 `location.href` 的用户页面 iframe,以及 `<devtools-anywhere>`,它凭所在的文档认出自己是外壳的元素。(Chrome 在 `window.stop()` 之后会忽略 `document.open()`,所以改用 `<plaintext>`。) +1. 使用零配置探针(`data-devtools-frontend-url`,或 Vite 插件)时,顶层页面首次加载就默认进入外壳模式:探针在页面脚本运行前自己把前端地址和 bundle 地址写入 `sessionStorage`,不需要再刷新一次。除非设置了 `data-open-immediately`,DevTools 一开始是隐藏的。其他情况下,点击 DevTools 工具栏上的锁形按钮「Keep DevTools open across reloads」,写入这些地址后刷新页面。 +2. 探针作为第一个脚本,在顶层窗口检测到配置后,先写入一个不闭合的 `<plaintext>`,让页面剩余的内容变成纯文本(页面脚本都不会执行),再用 `document.open()` / `document.write()` 把文档替换成外壳。外壳中有指向 `location.href` 的页面 iframe,以及 `<devtools-anywhere>`,它凭所在的文档认出自己是外壳的组件。(Chrome 在 `window.stop()` 之后会忽略 `document.open()`,所以改用 `<plaintext>`。) 3. 在 iframe 中,探针照常捕获,并加载 bundle,把页面的 CDP 会话连接到外壳。外壳在页面和前端之间转发消息。 4. 页面刷新或跳转后,新页面会发出 hello;外壳通知前端页面已导航,并把前端发过的所有 `enable` 命令重新发给新页面,新页面就会回放它自己最早的输出。地址栏和标题跟随页面变化,包括加载、前进后退和同文档导航(有 Navigation API 时也包括 `pushState`),因此刷新或复制链接打开的都是当前显示的页面。 -5. DevTools 铺满外壳,由它来排布页面和自己,和 Chrome 一样:页面 iframe 放在前端的页面区域上,叠在上层。停靠位置和尺寸由 DevTools 自己管理(用它的分隔条;DevTools 面板边缘正中、靠页面一侧还有一个抓手,像面板的一个小标签,触屏上更大)。取消停靠时,DevTools 是页面上方的浮动窗口。工具栏上的设备按钮是设备模式,和 Chrome 一样,取代了投屏:页面 iframe 按设备尺寸设置并缩放到适合窗口,工具栏的菜单可以截图。取消停靠时,设备模式的工具栏画在前端的一个页面(`device_mode_emulation_frame.html`,Chrome 也是这么做的)里,显示在浮动窗口下方。 -6. 点工具栏上的 × 只隐藏 devtools,仍留在外壳模式;在重新显示 devtools 之前,页面按自己的尺寸铺满外壳。再点一次锁形按钮会清除配置,并在普通模式下重新加载页面;这个选择会记在 `localStorage` 里,之后优先于页面的 `data-default-shell-mode`,除非页面设置了 `data-shell-mode-locked`。 +5. DevTools 铺满外壳,由它来排布页面和自己,和 Chrome 一样:页面 iframe 叠放在前端预留的页面区域上方。停靠位置和尺寸由 DevTools 自己管理(用它的分隔条;DevTools 面板边缘正中、靠页面一侧还有一个抓手,像面板的一个小标签,触屏上更大)。取消停靠时,DevTools 是页面上方的浮动窗口。工具栏上的设备按钮是设备模式,和 Chrome 一样,取代了投屏:页面 iframe 按设备尺寸设置并缩放到适合窗口,工具栏的菜单可以截图。取消停靠时,设备模式的工具栏画在前端的一个页面(`device_mode_emulation_frame.html`,Chrome 也是这么做的)里,显示在浮动窗口下方。 +6. 点工具栏上的 × 只隐藏 DevTools,仍留在外壳模式;在重新显示 DevTools 之前,页面按自己的尺寸铺满外壳。再点一次锁形按钮会清除配置,并在普通模式下重新加载页面;这个选择会记在 `localStorage` 里,之后优先于页面的 `data-default-shell-mode`,除非页面设置了 `data-shell-mode-locked`。 只有与探针脚本同源的地址才会被采信,否则清除配置、页面照常加载。因此外壳模式要求探针、bundle 和前端都由页面所在的源提供。 -页面放进 iframe 后,`viewport`、`100vh`、`position: fixed`、`window.top` 等的行为可能和直接打开时不同;页面的 HTML 会被请求两次(顶层窗口一次,iframe 一次),表单 POST 返回的页面会被用 GET 再请求一次。用 `data-default-shell-mode="off"`(Vite 插件的 `defaultShellMode: 'off'`)可以关闭外壳模式,改为把 devtools 直接嵌在页面中。拒绝被嵌入(`X-Frame-Options`、CSP `frame-ancestors`)或反复把外壳跳走的页面,在本会话剩下的时间里会单独显示。 +页面放进 iframe 后,`viewport`、`100vh`、`position: fixed`、`window.top` 等的行为可能和直接打开时不同;页面的 HTML 会被请求两次(顶层窗口一次,iframe 一次),表单 POST 返回的页面会被用 GET 再请求一次。用 `data-default-shell-mode="off"`(Vite 插件的 `defaultShellMode: 'off'`)可以关闭外壳模式,改为把 DevTools 直接嵌在页面中。拒绝被嵌入(`X-Frame-Options`、CSP `frame-ancestors`)或反复把外壳跳走的页面,在本会话剩下的时间里会单独显示。 ### 3. 注入方式 @@ -69,8 +69,17 @@ devtools-anywhere 的工作原理和设计取舍。使用方法见 [README](../R ## 拆包 -[README](../README.zh-CN.md#发布形式) 里列出的包为什么这样拆: +[使用指南](usage.zh-CN.md#包)里列出的包为什么这样拆: - 页面内的 CDP agent 和探针不单独成包:探针和各个域共用同一套 hook 和缓冲,而且只有 `devtools-anywhere` 使用它们。它们放在 `devtools-anywhere` 中(`src/cdp-agent/`),不会出现版本不匹配;探针仍单独构建成一个很小的文件 `probe.iife.js`。 - 前端单独成包,因为它体积大(约 16 MB),更新节奏跟随 Chrome 版本。其他语言(约 42 MB)再拆成一个包,需要的人才装。 - 所有包使用同一个版本号、一起发布,版本号相同的包一定能配合使用。 + +## 参考 + +- [chobitsu](https://github.com/liriliri/chobitsu):CDP 实现的参考 +- [Chii](https://github.com/liriliri/chii):嵌入模式 `src/target/DevtoolsFrame.ts`,以及它的远程调试服务端 +- [ChromeDevTools/devtools-frontend](https://github.com/ChromeDevTools/devtools-frontend) +- [ChromeDevTools/devtools-protocol](https://github.com/ChromeDevTools/devtools-protocol) +- [Puppeteer](https://github.com/puppeteer/puppeteer):一致性测试中作为标准答案 +- [solid-playground preview](https://github.com/solidjs/solid-playground/blob/master/packages/solid-repl/src/components/preview.tsx) diff --git a/docs/images/desktop.webp b/docs/images/desktop.webp new file mode 100644 index 0000000000000000000000000000000000000000..61753d55be6a997c56dab70ad21d1e335a1eca44 GIT binary patch literal 176132 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 literal 0 HcmV?d00001 diff --git a/docs/images/mobile.webp b/docs/images/mobile.webp new file mode 100644 index 0000000000000000000000000000000000000000..1254db887d121f4d6185d5b69f27ada2e9450c01 GIT binary patch literal 262514 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%0AUtq_A+=Hq|Ba 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 B0$l(A literal 0 HcmV?d00001 diff --git a/docs/usage.md b/docs/usage.md new file mode 100644 index 0000000..248afbd --- /dev/null +++ b/docs/usage.md @@ -0,0 +1,263 @@ +# Usage guide + +English | [简体中文](usage.zh-CN.md) + +Reference for setting devtools-anywhere up. Quick start: [README](../README.md). How it works: [design](design.md). + +## Pick a setup + +| Scenario | Setup | +|---|---| +| Any page, self-hosted, with shell mode | [Script tag](#script-tag) | +| Any page, nothing to host | [Script tag](#script-tag) from a CDN | +| Vite project, in development | [Vite plugin](#vite) | +| A page on a phone, inspected from a computer | [Remote debugging](#remote-debugging) | +| DevTools in a box of the page's own layout | [Element placed by the page](#element-placed-by-the-page) | + +## Script tag + +```bash +npm i devtools-anywhere @devtools-anywhere/devtools-frontend +``` + +Serve the two packages' `dist/`, and put the probe first in `<head>`, synchronous: + +```html +<script src="/devtools-anywhere/dist/probe.iife.js" + data-devtools-frontend-url="/devtools-frontend/dist/anywhere_app.html"></script> +``` + +From a CDN, pin one exact version for both packages, and turn [shell mode](#shell-mode) off (it is not supported from a CDN): + +```html +<script src="https://cdn.jsdelivr.net/npm/devtools-anywhere@0.1.0/dist/probe.iife.js" + data-devtools-frontend-url="https://cdn.jsdelivr.net/npm/@devtools-anywhere/devtools-frontend@0.1.0/dist/anywhere_app.html" + data-default-shell-mode="off"></script> +``` + +| | Self-hosted | CDN (jsDelivr, unpkg) | +|---|---|---| +| Shell mode | ✅ On by default | ❌ Off | +| UI languages | All, with `@devtools-anywhere/devtools-frontend-locales` in the frontend's directory | English and Chinese | + +- After the page loads, the probe mounts one `<devtools-anywhere>` under `<html>`, whatever the page has; a second probe only warns. +- Caching: files under the frontend's `bundle/` are named after their content (cache for good); serve `anywhere_app.html` with `Cache-Control: no-cache`. + +### Attributes + +| Attribute | Vite option | Default | Effect | +|---|---|---|---| +| `data-devtools-frontend-url` | `devtoolsFrontendUrl` | Script tag: none. Vite: the installed frontend | The frontend's `anywhere_app.html`. Without it, no panel is mounted | +| `data-bundle` | — | Next to the probe | URL of `devtools-anywhere.iife.js` | +| `data-open-immediately` | `openImmediately` | Off | Opens DevTools as the page loads | +| `data-default-shell-mode` | `defaultShellMode` | `on` | `off`: no shell mode, the panel covers the page | +| `data-shell-mode-locked` | `shellModeLocked` | Off | Follows `data-default-shell-mode`, ignoring the user's choice (kept for later) and hiding DevTools' switch | +| `data-no-toggler` | `noToggler` | Off | Hides the F12 button | +| `data-service-worker` | `serviceWorker` (on / off) | Off | Turns the [Service Worker](#service-worker) on. As an attribute, its URL (empty: next to the bundle) | +| `data-remote` | `remote` | Off | Connects to a [remote debugging](#remote-debugging) server: its URL (empty: where the probe is served from) | +| `data-id-in-query` | `idInQuery` | Off | For a server with `idInQuery` | + +## Vite + +```bash +npm i -D @devtools-anywhere/vite-plugin +``` + +```js +// vite.config.js +import devtoolsAnywhere from '@devtools-anywhere/vite-plugin' + +export default { plugins: [devtoolsAnywhere()] } +``` + +- `vite serve` only. Injects the probe as the first script of every page; serves the bundle, Service Worker and frontend under `/@devtools-anywhere/`. +- Options: those of the [attributes](#attributes), plus `devtoolsFrontendPath` (another frontend build on disk, `…/anywhere_app.html`; its directory is served). +- `serviceWorker: true`: the plugin also sends `Service-Worker-Allowed`. +- With `@devtools-anywhere/devtools-frontend-locales` installed, all UI languages are served. + +## Element placed by the page + +```html +<devtools-anywhere devtools-frontend-url="/devtools-frontend/dist/anywhere_app.html" style="height: 400px"></devtools-anywhere> +``` + +- A block in the page's flow, sized by the page's CSS. DevTools loads once it is connected, and stays loaded while `hidden`. +- No F12 button, dock side, resizing or shell mode; inside the shell's page it works as on its own. +- One attribute, `devtools-frontend-url` (default: the probe's); others are ignored. +- Style DevTools' iframe with `devtools-anywhere::part(iframe)`. + +## The panel + +| Control | Effect | +|---|---| +| F12 button | Opens the panel | +| Grip on the middle of the panel's inner edge | Resizes the docked panel (in shell mode too, next to DevTools' own resizer) | +| ⋮ → Dock side | Bottom (default), right, left, or undocked: a window moved by its bar, resized from its corner | +| Lock button (left of ⚙) | Shell mode on / off; a dialog explains it first, an info bar shows while it is on | +| Refresh button | Reloads the page | +| × | Closes the panel | + +- Dock side and sizes are kept in `localStorage`, sizes as percentages of the viewport (`dvh`/`dvw`), so they fit any screen and orientation. +- In shell mode the page shrinks to make room; otherwise the panel covers it. +- Restyle the F12 button with `devtools-anywhere::part(toggler)`, in shell mode too: there it is in the page iframe (the shell shows its own only for a page without the probe, such as another origin's). +- Moving the element in the DOM reloads DevTools. + +### Togglers + +Any element can open and close the panel: + +```html +<button data-devtools-anywhere-toggle>DevTools</button> +``` + +| `data-devtools-anywhere-toggle` | On click | +|---|---| +| Empty | Toggles the probe's panel; `aria-expanded` follows it | +| Id of an element the page placed | Fires `devtools-toggle-request` on that element; no `aria-expanded` | + +- Clicks are caught on the document, so togglers added later work. The default action is not prevented. +- They work once the probe has loaded `devtools-anywhere.iife.js`. +- In the shell's page, every toggler controls the shell's panel. +- Togglers inside a shadow root, or in a page of another origin than the shell, do nothing. +- A toggler naming a missing element warns, once per value. +- The element fires no open / close event: `aria-expanded` on the togglers is the state. + +## Shell mode + +The top-level page is replaced by a shell: the page runs in an iframe next to DevTools, as in Chrome. + +| | Shell mode | Without | +|---|---|---| +| DevTools across reloads and navigations | Stays open, logs kept | Reopens with the page | +| Device mode | ✅ | Screencast instead | +| Layout | The page shrinks next to DevTools | The panel covers the page | + +On by default for the top-level page, when the bundle and frontend (and Service Worker) are on the probe's origin. It is off when: + +| Condition | | +|---|---| +| `data-default-shell-mode="off"` | Unless the user turned it on | +| The user turned it off (lock button) | Unless `data-shell-mode-locked` | +| The page cannot be framed (`X-Frame-Options`, CSP `frame-ancestors`) | For the rest of the session | +| The page keeps navigating the shell away | For the rest of the session | + +Caveats: + +- In an iframe, `100vh`, `position: fixed`, `window.top` and the viewport may behave differently. +- The page's HTML is requested twice; a page returned for a form POST is requested again with GET. + +### Service Worker + +`data-service-worker` / `serviceWorker`, in shell mode: + +- Injects the probe into same-origin pages in the page iframe that lack it. +- Adds request and response headers to same-origin requests (documents, style sheets, scripts, images, …). +- Needs a secure context (HTTPS or localhost), a scope covering the pages (`Service-Worker-Allowed: /`), and no Service Worker of the site's own. + +## Remote debugging + +```bash +npx -p @devtools-anywhere/remote-server devtools-anywhere-remote +``` + +1. Add the printed `<script>` to the page: + + ```html + <script src="http://192.168.1.2:8080/probe.iife.js" data-remote></script> + ``` + +2. Open the printed target list (it has the token) on the computer, and click "inspect". + +The server serves the probe, bundle and frontend itself, and the frontend runs on the computer: the same-origin rules above do not apply. + +### CLI options + +| Option | Default | Effect | +|---|---|---| +| `--port`, `-p` | `8080` | Port | +| `--host` | All interfaces | Interface to listen on | +| `--public-url` | LAN address | URL printed for pages, e.g. behind a proxy | +| `--base-path` | `/` | Serve under a path | +| `--token <t>` / `--no-token` | Random | Token for the target list and DevTools; pages need none | +| `--cert`, `--key` | — | HTTPS; needed for HTTPS pages | +| `--id-in-query` | Off | Target in the query, for hosts that route WebSocket upgrades by exact path | +| `--devtools-frontend-path` | The bundled frontend | Another frontend build on disk | +| `--devtools-frontend-url` | — | A frontend hosted elsewhere (a CDN; over HTTPS, the server needs HTTPS too). The WebSocket and token go in the URL fragment, not sent to the CDN | + +### Behavior + +- Pages connect after load; early output is captured as usual. +- DevTools stays connected across reloads and navigations in the tab. A page whose connection drops reconnects by itself, and stays on the list meanwhile. +- Several DevTools windows can inspect one page, each in its own session. +- The target list can be filtered and shows how many DevTools each page has. +- With `data-devtools-frontend-url` too, the page also has its own panel. +- Breakpoints still need the browser's own debugger (`chrome://inspect`, Safari Web Inspector): the page runs the same in-page agent. + +`data-remote` with a value names the server, when the probe comes from elsewhere. It resolves like a link: + +| `data-remote` | Server | +|---|---| +| `http://host:8080/devtools/` | At `/devtools/` | +| `http://host:8080/devtools` | At `/` | + +In shell mode: + +- The page in the shell connects, not the shell. +- The shell's probe decides for every page in the shell; their own `data-remote` is ignored, so a page's scripts cannot send later pages elsewhere. +- With `data-service-worker`, pages it injects connect too. +- Shell mode needs the frontend on the probe's origin: not with the probe from the server and a frontend elsewhere. + +### Node.js and endpoints + +| API / URL | Description | +|---|---| +| `startRemoteServer(options)` | The CLI's server | +| `createRemoteHandler(options)` | Request and upgrade handlers for your own server (Express, Koa, …); `false` for requests not theirs | +| `<base>target/<id>` | The page's WebSocket | +| `<base>client/<id>` | DevTools' WebSocket | +| `<base>?target=<id>`, `<base>?client=<id>` | The same, with `idInQuery` (both forms always accepted) | +| `/json/list` | Targets, in Chrome's format (needs the token) | +| `anywhere_app.html?ws=<host:port/path>` | A frontend opened by itself connects to a target, as Chrome's `devtools_app.html?ws=` (`wss=` over HTTPS; also `#?ws=`) | + +## Limitations + +Gaps are filled exactly or not at all: nothing approximate is shown as fact. + +| Feature | Status | Details | +|---|---|---| +| Breakpoints, Profiler, Memory, Performance | ❌ Panels not included | No JS API. `debugger;` does not pause; in shell mode it logs a warning with its stack | +| Network timing | ❌ | No JS API | +| Sources | ⚠️ | Documents, external scripts and style sheets, images, fonts; no inline scripts; snippets work | +| Browser default styles | ❌ | Not exposed to pages | +| Event listeners | ⚠️ | Only those added after the probe ran; no source location | +| WebSocket | ⚠️ | Connections and frames; no handshake headers | +| Cross-origin style sheets | ⚠️ | Need CORS; read-only | +| Forced `:hover`, `:focus`, … | ⚠️ | Not for `@import` and `adoptedStyleSheets` sheets | +| Screencast (device button outside shell mode) | ⚠️ | Redrawn by [snapDOM](https://github.com/zumerlab/snapdom) from the DOM: canvas, video and cross-origin images may be missing; ≤ 2 fps. Input replayed with `isTrusted: false` | +| Screenshots | ⚠️ | See [below](#screenshots) | +| Device mode | ⚠️ | Shell mode only. Sets the size only: no pixel ratio, user agent or touch; its screenshots are snapDOM's, at the page's pixel ratio | +| Fullscreen pages | ⚠️ | While an element is fullscreen, DevTools cannot be used, in shell mode too. Esc leaves fullscreen | +| Frontend on the page's origin | ⚠️ | DevTools' settings share the page's `localStorage`: host it on another origin | +| Browsers | ⚠️ | Page side: any modern browser. DevTools UI: verified in Chrome only; newer frontends may need compatibility patches | + +snapDOM (about 90 KB gzip) loads only for the screencast and screenshots. + +### Screenshots + +`Page.captureScreenshot`, for tools and agents that drive the page over CDP: + +- Rendered by snapDOM like the screencast, and marked `approximate: true` (a field Chrome does not send). +- `clip` is in page coordinates; the image is in device pixels (`devicePixelRatio` × `clip.scale`). +- `captureBeyondViewport` without `clip` takes the whole page. +- Past the canvas limits (32767 px a side, 16384 on Safari, 16384 × 16384 in area) the image is scaled down; snapDOM warns in the console. +- WebP falls back to PNG where it cannot be encoded (Safari). + +## Packages + +| Package | Contents | +|---|---| +| `devtools-anywhere` | `probe.iife.js`, `devtools-anywhere.iife.js` (the element), `devtools-anywhere-service-worker.js`, `snapdom.js`; TypeScript types for the element, its events and the attributes. No module to import | +| `@devtools-anywhere/devtools-frontend` | The frontend, English and Chinese UI | +| `@devtools-anywhere/devtools-frontend-locales` | The other 80 UI languages; serve from the frontend's directory. Missing languages fall back to English | +| `@devtools-anywhere/vite-plugin` | Vite plugin | +| `@devtools-anywhere/remote-server` | Remote debugging server and CLI (`devtools-anywhere-remote`) | diff --git a/docs/usage.zh-CN.md b/docs/usage.zh-CN.md new file mode 100644 index 0000000..111a4c4 --- /dev/null +++ b/docs/usage.zh-CN.md @@ -0,0 +1,263 @@ +# 使用指南 + +[English](usage.md) | 简体中文 + +devtools-anywhere 的接入参考。快速上手见 [README](../README.zh-CN.md),工作原理见[设计文档](design.zh-CN.md)。 + +## 选择接入方式 + +| 场景 | 接入方式 | +|---|---| +| 任意页面,自己部署,要外壳模式 | [script 标签](#script-标签) | +| 任意页面,不想部署 | 从 CDN 引入的 [script 标签](#script-标签) | +| Vite 项目,开发时用 | [Vite 插件](#vite) | +| 在电脑上调试手机里的页面 | [远程调试](#远程调试) | +| DevTools 嵌在页面自己的布局里 | [页面自己放置的组件](#页面自己放置的组件) | + +## script 标签 + +```bash +npm i devtools-anywhere @devtools-anywhere/devtools-frontend +``` + +部署这两个包的 `dist/`,把探针放在 `<head>` 里第一个,同步加载: + +```html +<script src="/devtools-anywhere/dist/probe.iife.js" + data-devtools-frontend-url="/devtools-frontend/dist/anywhere_app.html"></script> +``` + +用 CDN 时,两个包固定到同一个确切版本,并关掉[外壳模式](#外壳模式)(CDN 上不支持): + +```html +<script src="https://cdn.jsdelivr.net/npm/devtools-anywhere@0.1.0/dist/probe.iife.js" + data-devtools-frontend-url="https://cdn.jsdelivr.net/npm/@devtools-anywhere/devtools-frontend@0.1.0/dist/anywhere_app.html" + data-default-shell-mode="off"></script> +``` + +| | 自己部署 | CDN(jsDelivr、unpkg) | +|---|---|---| +| 外壳模式 | ✅ 默认开启 | ❌ 关闭 | +| 界面语言 | 全部(`@devtools-anywhere/devtools-frontend-locales` 放在前端目录下) | 英文和中文 | + +- 页面加载完成后,探针在 `<html>` 下挂载一个 `<devtools-anywhere>`,不管页面里有没有;第二个探针只会警告。 +- 缓存:前端 `bundle/` 下的文件以内容命名,可以永久缓存;`anywhere_app.html` 用 `Cache-Control: no-cache` 提供。 + +### 属性 + +| 属性 | Vite 选项 | 默认值 | 作用 | +|---|---|---|---| +| `data-devtools-frontend-url` | `devtoolsFrontendUrl` | script 标签:无。Vite:已安装的前端 | 前端的 `anywhere_app.html`。不写就不挂载面板 | +| `data-bundle` | — | 探针同目录 | `devtools-anywhere.iife.js` 的地址 | +| `data-open-immediately` | `openImmediately` | 关 | 页面加载后直接打开 DevTools | +| `data-default-shell-mode` | `defaultShellMode` | `on` | `off`:不用外壳模式,面板盖在页面上 | +| `data-shell-mode-locked` | `shellModeLocked` | 关 | 始终按 `data-default-shell-mode` 执行,忽略用户的选择(选择仍会保留),并隐藏 DevTools 里的开关 | +| `data-no-toggler` | `noToggler` | 关 | 隐藏 F12 按钮 | +| `data-service-worker` | `serviceWorker`(开 / 关) | 关 | 启用 [Service Worker](#service-worker)。作为属性时可以写它的地址(留空:bundle 同目录) | +| `data-remote` | `remote` | 关 | 连接[远程调试](#远程调试)服务端:它的地址(留空:探针所在的服务端) | +| `data-id-in-query` | `idInQuery` | 关 | 配合开启了 `idInQuery` 的服务端 | + +## Vite + +```bash +npm i -D @devtools-anywhere/vite-plugin +``` + +```js +// vite.config.js +import devtoolsAnywhere from '@devtools-anywhere/vite-plugin' + +export default { plugins: [devtoolsAnywhere()] } +``` + +- 只在 `vite serve` 时生效。探针作为每个页面的第一个脚本注入;bundle、Service Worker 和前端都在 `/@devtools-anywhere/` 下提供。 +- 选项:和[属性](#属性)一一对应,另加 `devtoolsFrontendPath`(本地另一份前端构建,`…/anywhere_app.html`,托管它所在的目录)。 +- `serviceWorker: true`:插件还会返回 `Service-Worker-Allowed` 头。 +- 装了 `@devtools-anywhere/devtools-frontend-locales` 就提供全部界面语言。 + +## 页面自己放置的组件 + +```html +<devtools-anywhere devtools-frontend-url="/devtools-frontend/dist/anywhere_app.html" style="height: 400px"></devtools-anywhere> +``` + +- 页面布局中的一个块,尺寸由页面 CSS 决定。连接到文档后就加载 DevTools,`hidden` 时保持加载。 +- 没有 F12 按钮、停靠位置、调整大小和外壳模式;在外壳里的页面中照常工作。 +- 只有一个属性 `devtools-frontend-url`(默认用探针的),其他属性一律忽略。 +- DevTools 的 iframe 可以用 `devtools-anywhere::part(iframe)` 改样式。 + +## 面板 + +| 控件 | 作用 | +|---|---| +| F12 按钮 | 打开面板 | +| 面板内侧边缘正中的抓手 | 调整停靠面板的大小(外壳模式下也有,旁边还有 DevTools 自己的分隔条) | +| ⋮ → 停靠位置 | 底部(默认)、右侧、左侧,或取消停靠:拖横条移动、拖右下角调整大小的浮动窗口 | +| 锁形按钮(⚙ 左边) | 开关外壳模式;开启前有对话框说明,开启期间顶部有提示条 | +| 刷新按钮 | 重新加载页面 | +| × | 关闭面板 | + +- 停靠位置和尺寸记在 `localStorage` 里,尺寸按视口百分比记录(`dvh`/`dvw`),换屏幕或横竖屏后依然合适。 +- 外壳模式下页面会让出空间;否则面板盖在页面上。 +- 可以用 `devtools-anywhere::part(toggler)` 修改 F12 按钮的样式,外壳模式下同样有效:此时按钮在页面 iframe 里(只有页面没有探针时,例如其他源的页面,外壳才显示自己的按钮)。 +- 在 DOM 里移动组件会重新加载 DevTools。 + +### 切换按钮 + +任何元素都可以开关面板: + +```html +<button data-devtools-anywhere-toggle>DevTools</button> +``` + +| `data-devtools-anywhere-toggle` | 点击时 | +|---|---| +| 空 | 开关探针的面板;`aria-expanded` 跟随面板状态 | +| 页面放置的组件的 id | 在那个组件上触发 `devtools-toggle-request`;没有 `aria-expanded` | + +- 在 document 上监听点击,之后加入的按钮也有效。不阻止默认行为。 +- 探针加载完 `devtools-anywhere.iife.js` 后才生效。 +- 外壳里的页面上,所有切换按钮都控制外壳的面板。 +- 位于 shadow root 里、或与外壳不同源的页面里的切换按钮无效。 +- 指向不存在的组件时会警告,每个值只警告一次。 +- 组件不触发开关事件:切换按钮上的 `aria-expanded` 就是状态。 + +## 外壳模式 + +顶层页面换成一个外壳:页面运行在 DevTools 旁边的 iframe 里,和 Chrome 一样。 + +| | 外壳模式 | 非外壳模式 | +|---|---|---| +| 刷新、跳转时的 DevTools | 保持打开,日志保留 | 随页面重新打开 | +| 设备模式 | ✅ | 换成投屏 | +| 布局 | 页面在 DevTools 旁边缩小 | 面板盖在页面上 | + +bundle、前端(以及 Service Worker)与探针同源时,顶层页面默认开启外壳模式。以下情况关闭: + +| 情况 | | +|---|---| +| `data-default-shell-mode="off"` | 用户手动开启时除外 | +| 用户点锁形按钮关闭 | 设置了 `data-shell-mode-locked` 时除外 | +| 页面禁止被嵌入(`X-Frame-Options`、CSP `frame-ancestors`) | 本次会话内一直关闭 | +| 页面反复把外壳跳转走 | 本次会话内一直关闭 | + +注意: + +- 在 iframe 里,`100vh`、`position: fixed`、`window.top` 和视口的表现可能不同。 +- 页面的 HTML 会请求两次;表单 POST 返回的页面会用 GET 再请求一次。 + +### Service Worker + +`data-service-worker` / `serviceWorker`,外壳模式下: + +- 给页面 iframe 里没有探针的同源页面注入探针。 +- 为同源请求(文档、样式表、脚本、图片等)补上请求头和响应头。 +- 需要安全上下文(HTTPS 或 localhost)、覆盖这些页面的作用域(`Service-Worker-Allowed: /`),且站点没有自己的 Service Worker。 + +## 远程调试 + +```bash +npx -p @devtools-anywhere/remote-server devtools-anywhere-remote +``` + +1. 把它打印出的 `<script>` 加进页面: + + ```html + <script src="http://192.168.1.2:8080/probe.iife.js" data-remote></script> + ``` + +2. 在电脑上打开它打印出的目标列表(地址里带令牌),点「inspect」。 + +服务端自己提供探针、bundle 和前端,前端运行在电脑上:上面的同源要求不适用。 + +### 命令行选项 + +| 选项 | 默认值 | 作用 | +|---|---|---| +| `--port`、`-p` | `8080` | 端口 | +| `--host` | 所有网卡 | 监听的网卡 | +| `--public-url` | 局域网地址 | 打印给页面的地址,例如在代理后面时 | +| `--base-path` | `/` | 挂在某个路径下 | +| `--token <t>` / `--no-token` | 随机 | 目标列表和 DevTools 需要的令牌;页面不需要 | +| `--cert`、`--key` | — | HTTPS;HTTPS 页面需要 | +| `--id-in-query` | 关 | 目标放在查询参数里,用于按完整路径转发 WebSocket 升级的主机 | +| `--devtools-frontend-path` | 自带的前端 | 本地另一份前端构建 | +| `--devtools-frontend-url` | — | 托管在别处的前端(CDN;用 HTTPS 时服务端也要 HTTPS)。WebSocket 地址和令牌放在 URL 片段里,不会发给 CDN | + +### 行为 + +- 页面加载完成后连接;早期输出照常捕获。 +- 页面刷新或在同一标签页内跳转时,DevTools 保持连接。断线的页面会自动重连,期间仍留在列表里。 +- 多个 DevTools 窗口可以同时调试一个页面,各自一个会话。 +- 目标列表可以筛选,并显示每个页面连着几个 DevTools。 +- 同时写了 `data-devtools-frontend-url` 时,页面还有自己的面板。 +- 断点仍要靠浏览器自带的调试器(`chrome://inspect`、Safari Web Inspector):页面里运行的仍是同一个页面内 agent。 + +探针来自别处时,用带值的 `data-remote` 指定服务端,按链接的规则解析: + +| `data-remote` | 服务端 | +|---|---| +| `http://host:8080/devtools/` | 在 `/devtools/` 下 | +| `http://host:8080/devtools` | 在 `/` 下 | + +外壳模式下: + +- 连接的是外壳里的页面,不是外壳本身。 +- 外壳里的所有页面由外壳的探针决定,页面自己的 `data-remote` 无效,所以页面脚本没法把之后的页面指向别的服务端。 +- 配合 `data-service-worker`,被它注入的页面也会连接。 +- 外壳模式要求前端与探针同源:探针来自服务端、前端在别处时用不了。 + +### Node.js 与接口 + +| API / 地址 | 说明 | +|---|---| +| `startRemoteServer(options)` | 命令行用的服务端 | +| `createRemoteHandler(options)` | 请求和升级处理函数,挂到自己的服务里(Express、Koa 等);不属于它的请求返回 `false` | +| `<base>target/<id>` | 页面的 WebSocket | +| `<base>client/<id>` | DevTools 的 WebSocket | +| `<base>?target=<id>`、`<base>?client=<id>` | 同上,`idInQuery` 时的形式(两种都始终接受) | +| `/json/list` | 目标列表,格式同 Chrome(需要令牌) | +| `anywhere_app.html?ws=<host:port/path>` | 单独打开的前端连接某个目标,同 Chrome 的 `devtools_app.html?ws=`(HTTPS 用 `wss=`;也可写在 `#?ws=`) | + +## 能力边界 + +拿不到的数据要么精确补上,要么不显示:不把近似值当成事实。 + +| 功能 | 状态 | 说明 | +|---|---|---| +| 断点、Profiler、内存、性能 | ❌ 不打包这些面板 | 没有 JS API。`debugger;` 不会暂停;外壳模式下改为输出一条带堆栈的警告 | +| 网络时序 | ❌ | 没有 JS API | +| 源代码 | ⚠️ | 文档、外链脚本和样式表、图片、字体;不列内联脚本;代码段可用 | +| 浏览器默认样式 | ❌ | 浏览器不向页面公开 | +| 事件监听器 | ⚠️ | 只有探针运行之后添加的;没有源码位置 | +| WebSocket | ⚠️ | 连接和帧;没有握手头 | +| 跨源样式表 | ⚠️ | 需要 CORS;只读 | +| 强制 `:hover`、`:focus` 等 | ⚠️ | 不覆盖 `@import` 和 `adoptedStyleSheets` 的样式表 | +| 投屏(非外壳模式下的设备按钮) | ⚠️ | 由 [snapDOM](https://github.com/zumerlab/snapdom) 从 DOM 重绘:canvas、视频、跨源图片可能缺失;每秒最多约 2 帧。输入以 `isTrusted: false` 的事件回放 | +| 截图 | ⚠️ | 见[下文](#截图) | +| 设备模式 | ⚠️ | 仅外壳模式。只改尺寸,不改像素比、UA 和触摸;它的截图由 snapDOM 生成,按页面自身的像素比 | +| 全屏的页面 | ⚠️ | 有元素全屏时用不了 DevTools,外壳模式下也一样。按 Esc 退出全屏即可 | +| 前端与页面同源 | ⚠️ | DevTools 的设置会和页面共用 `localStorage`:前端放在别的源 | +| 浏览器 | ⚠️ | 页面这边:现代浏览器都行。DevTools 界面:只在 Chrome 上验证过;新版前端可能需要兼容补丁 | + +snapDOM(gzip 后约 90 KB)只在投屏和截图时加载。 + +### 截图 + +`Page.captureScreenshot`,供通过 CDP 操作页面的工具和 agent 使用: + +- 和投屏一样由 snapDOM 绘制,并标注 `approximate: true`(Chrome 不返回这个字段)。 +- `clip` 用页面坐标;图片按设备像素计算(`devicePixelRatio` × `clip.scale`)。 +- 没有 `clip` 时,`captureBeyondViewport` 截整个页面。 +- 超出画布上限(每边 32767 像素,Safari 为 16384,面积 16384 × 16384)时缩小,snapDOM 会在控制台警告。 +- 不支持编码 WebP 的浏览器(Safari)退回 PNG。 + +## 包 + +| 包 | 内容 | +|---|---| +| `devtools-anywhere` | `probe.iife.js`、`devtools-anywhere.iife.js`(组件)、`devtools-anywhere-service-worker.js`、`snapdom.js`;组件、事件和各属性的 TypeScript 类型。没有可以 import 的模块 | +| `@devtools-anywhere/devtools-frontend` | 前端,英文和中文界面 | +| `@devtools-anywhere/devtools-frontend-locales` | 其余 80 种界面语言;和前端放在同一目录。缺少的语言退回英文 | +| `@devtools-anywhere/vite-plugin` | Vite 插件 | +| `@devtools-anywhere/remote-server` | 远程调试服务端和命令行(`devtools-anywhere-remote`) | diff --git a/packages/devtools-anywhere/src/cdp-agent/domains/page/screenshot.ts b/packages/devtools-anywhere/src/cdp-agent/domains/page/screenshot.ts index 0b5af21..8399300 100644 --- a/packages/devtools-anywhere/src/cdp-agent/domains/page/screenshot.ts +++ b/packages/devtools-anywhere/src/cdp-agent/domains/page/screenshot.ts @@ -14,7 +14,7 @@ const SAFE_PIXELS = 4096 * 4096; /** * Page.captureScreenshot's response, with `approximate`: the image is the page's DOM redrawn - * by snapDOM, not the pixels Chrome painted (README, "Limitations"). + * by snapDOM, not the pixels Chrome painted (docs/usage.md, "Limitations"). */ export type CaptureScreenshotResponse = Protocol.Page.CaptureScreenshotResponse & { approximate: true }; diff --git a/packages/remote-server/README.md b/packages/remote-server/README.md index f00b66b..1dcabfc 100644 --- a/packages/remote-server/README.md +++ b/packages/remote-server/README.md @@ -6,4 +6,4 @@ A relay that lets Chrome DevTools on one machine inspect a page running on anoth npx @devtools-anywhere/remote-server --port 8080 ``` -Options, the page side and security: [Remote debugging](https://github.com/jerrychan7/devtools#remote-debugging) ([中文](https://github.com/jerrychan7/devtools/blob/main/README.zh-CN.md#远程调试)). Requires Node.js 22.18 or later. MIT licensed. +Options, the page side and security: [Remote debugging](https://github.com/jerrychan7/devtools/blob/main/docs/usage.md#remote-debugging) ([中文](https://github.com/jerrychan7/devtools/blob/main/docs/usage.zh-CN.md#远程调试)). Requires Node.js 22.18 or later. MIT licensed. diff --git a/packages/vite-plugin/README.md b/packages/vite-plugin/README.md index a565bfe..118c184 100644 --- a/packages/vite-plugin/README.md +++ b/packages/vite-plugin/README.md @@ -8,4 +8,4 @@ import devtoolsAnywhere from '@devtools-anywhere/vite-plugin'; export default { plugins: [devtoolsAnywhere()] }; ``` -Options: [Vite](https://github.com/jerrychan7/devtools#vite) ([中文](https://github.com/jerrychan7/devtools/blob/main/README.zh-CN.md#vite)). Requires Node.js 22.18 or later. MIT licensed. +Options: [Vite](https://github.com/jerrychan7/devtools/blob/main/docs/usage.md#vite) ([中文](https://github.com/jerrychan7/devtools/blob/main/docs/usage.zh-CN.md#vite)). Requires Node.js 22.18 or later. MIT licensed. From 66a2a30bc3bfbedfc976cb0735f6d318d74ec55c Mon Sep 17 00:00:00 2001 From: jerrychan7 <czy-ken@163.com> Date: Mon, 2 Nov 2026 00:34:50 +0800 Subject: [PATCH 18/26] fix(runtime): preview console.table's rows so the frontend can draw the table console.table of an array of objects showed a single "Value" column, since the rows' previews had no fields of their own. Like V8, the data is now previewed as a table: up to 1000 rows, each with a preview one level deep and no limit on its columns. Columns given as an array filter the rows, and only the data is sent, as Chrome does. The conformance tests compare the whole arguments with Chrome's, for rows of objects and arrays, an object of objects, columns, many columns and rows, and a primitive. --- .../src/cdp-agent/core/objects.ts | 32 +++++++++++++++++-- .../runtime/runtime.conformance.test.ts | 22 +++++++++++++ .../src/cdp-agent/domains/runtime/runtime.ts | 17 ++++++++-- 3 files changed, 66 insertions(+), 5 deletions(-) diff --git a/packages/devtools-anywhere/src/cdp-agent/core/objects.ts b/packages/devtools-anywhere/src/cdp-agent/core/objects.ts index be6ce9a..4542254 100644 --- a/packages/devtools-anywhere/src/cdp-agent/core/objects.ts +++ b/packages/devtools-anywhere/src/cdp-agent/core/objects.ts @@ -20,6 +20,8 @@ function toProtocolSubtype(subtype: NonNullable<Subtype>): ProtocolSubtype { const MAX_OBJECT_PREVIEW = 5; const MAX_ARRAY_PREVIEW = 100; const MAX_ENTRY_PREVIEW = 5; +/** console.table's rows, and the columns of each, as V8 previews them. */ +const MAX_TABLE_PREVIEW = 1000; /** An entry of a Map or Set, exposed under the `[[Entries]]` internal property. */ class InternalEntry { @@ -181,6 +183,11 @@ export interface WrapOptions { generatePreview?: boolean | undefined; returnByValue?: boolean | undefined; group?: string | undefined; + /** + * console.table's data: like V8, its preview lists up to 1000 rows, each with a preview of its + * own (one level deep), of only the given columns if any. + */ + table?: { columns?: readonly string[] | undefined } | undefined; } /** Turns page values into CDP RemoteObjects and keeps the objects alive by id. */ @@ -223,7 +230,8 @@ export class ObjectRegistry { if (type === 'function') result.className = 'Function'; else if (type === 'object') result.className = subtype === 'error' ? (value as Error).name || className(value) : className(value); - if (options.generatePreview && type === 'object') result.preview = this.preview(value as object, subtype); + if (options.table && type === 'object') result.preview = this.tablePreview(value as object, subtype, options.table); + else if (options.generatePreview && type === 'object') result.preview = this.preview(value as object, subtype); result.objectId = this.idOf(value, options.group); return result; } @@ -385,7 +393,25 @@ export class ObjectRegistry { return result; } - private preview(obj: object, subtype: Subtype): ObjectPreview { + private tablePreview(obj: object, subtype: Subtype, { columns }: { columns?: readonly string[] | undefined }) { + const preview = this.preview(obj, subtype, MAX_TABLE_PREVIEW); + for (const row of preview.properties) { + if (row.type !== 'object' || row.subtype === 'null') continue; + let value: unknown; + try { + value = Reflect.get(obj, row.name); + } catch { + continue; + } + const { subtype: rowSubtype } = classify(value); + const rowPreview = this.preview(value as object, rowSubtype, MAX_TABLE_PREVIEW); + if (columns) rowPreview.properties = rowPreview.properties.filter(p => columns.includes(p.name)); + row.valuePreview = rowPreview; + } + return preview; + } + + private preview(obj: object, subtype: Subtype, limitOverride?: number): ObjectPreview { const preview: ObjectPreview = { type: 'object', description: describe(obj, subtype), @@ -396,7 +422,7 @@ export class ObjectRegistry { if (subtype === 'internal#entry') return preview; const isArrayLike = subtype === 'array' || subtype === 'typedarray'; - const limit = isArrayLike ? MAX_ARRAY_PREVIEW : MAX_OBJECT_PREVIEW; + const limit = limitOverride ?? (isArrayLike ? MAX_ARRAY_PREVIEW : MAX_OBJECT_PREVIEW); const add = (property: PropertyPreview) => { if (preview.properties.length >= limit) { preview.overflow = true; diff --git a/packages/devtools-anywhere/src/cdp-agent/domains/runtime/runtime.conformance.test.ts b/packages/devtools-anywhere/src/cdp-agent/domains/runtime/runtime.conformance.test.ts index 49d1290..c4b7db7 100644 --- a/packages/devtools-anywhere/src/cdp-agent/domains/runtime/runtime.conformance.test.ts +++ b/packages/devtools-anywhere/src/cdp-agent/domains/runtime/runtime.conformance.test.ts @@ -79,6 +79,28 @@ describe('Runtime.consoleAPICalled arguments', () => { } }); +describe('console.table', () => { + // The frontend draws the table from the rows' previews: they must match Chrome's whole. + const cases = { + objects: "console.table([{ a: 1, b: 'x' }, { a: 2, c: [1, 2] }])", + arrays: 'console.table([[1, 2], [3, 4]])', + objectOfObjects: 'console.table({ r1: { a: 1 }, r2: { b: { c: 1 } } })', + columns: "console.table([{ a: 1, b: 2 }, { a: 3, b: 4 }], ['b'])", + columnsNotAnArray: "console.table([{ a: 1 }], 'a')", + manyColumns: 'console.table([Object.fromEntries(Array.from({ length: 30 }, (_, i) => [`k${i}`, i]))])', + manyRows: 'console.table(Array.from({ length: 1200 }, (_, i) => ({ a: i })))', + primitive: "console.table('x', 1)", + }; + const withoutIds = (args: unknown) => JSON.parse(JSON.stringify(args, (k, v) => (k === 'objectId' ? undefined : v))); + for (const [name, expr] of Object.entries(cases)) { + test(name, async () => { + const { ours, chrome } = await logged(expr); + assert.equal(ours.type, chrome.type); + assert.deepEqual(withoutIds(ours.args), withoutIds(chrome.args)); + }); + } +}); + describe('Runtime.consoleAPICalled types', () => { const cases = { warn: "console.warn('w')", diff --git a/packages/devtools-anywhere/src/cdp-agent/domains/runtime/runtime.ts b/packages/devtools-anywhere/src/cdp-agent/domains/runtime/runtime.ts index c3ae1d8..e4e8917 100644 --- a/packages/devtools-anywhere/src/cdp-agent/domains/runtime/runtime.ts +++ b/packages/devtools-anywhere/src/cdp-agent/domains/runtime/runtime.ts @@ -11,7 +11,7 @@ import { } from '../../core/lexer'; import { CdpError } from '../../core/session'; import { parseStack } from '../../core/stack'; -import type { LogEntry } from '../../probe/types'; +import type { ConsoleEntry, LogEntry } from '../../probe/types'; import { refetch } from '../network/network'; // Indirect eval runs in the global scope, like code typed into the console. @@ -625,11 +625,24 @@ export function registerRuntime({ session, objects, nodes, probe }: Context) { }; } + /** + * Like V8, console.table of an object sends that object alone, previewed as a table; the + * columns to show, if given as an array, filter its rows. + */ + function consoleArgs(entry: ConsoleEntry) { + const [data, columns] = entry.args; + if (entry.type === 'table' && typeof data === 'object' && data !== null) { + const names = Array.isArray(columns) ? columns.map(String) : undefined; + return [objects.wrap(data, { group: 'console', table: { columns: names } })]; + } + return entry.args.map(arg => objects.wrap(arg, { generatePreview: true, group: 'console' })); + } + function emitLog(entry: LogEntry) { if (entry.kind === 'console') { session.emit('Runtime.consoleAPICalled', { type: entry.type, - args: entry.args.map(arg => objects.wrap(arg, { generatePreview: true, group: 'console' })), + args: consoleArgs(entry), executionContextId: EXECUTION_CONTEXT_ID, timestamp: entry.time, // Drop the probe's console wrapper frame. From 752a2419668609247b5b04477f8850a653d66d3d Mon Sep 17 00:00:00 2001 From: jerrychan7 <czy-ken@163.com> Date: Mon, 2 Nov 2026 02:21:25 +0800 Subject: [PATCH 19/26] feat(probe): one script tag from jsDelivr or unpkg, shell mode included From an npm CDN the probe needed the frontend's URL, and shell mode had to be turned off: shell configs were trusted on the probe's origin, which on a CDN is everyone's package, so a page's script could point the shell to any frontend published there. The URL of the script that reads a shell config is now its trust root (trust.ts). On jsDelivr or unpkg it trusts only this package and the frontend's, at its own version; URLs are compared once parsed, so dot segments are resolved, and escaped slashes or backslashes are refused. Elsewhere it still trusts its origin. With that, the probe from such a CDN loads the frontend of its version there by default, and shell mode works, without the Service Worker, which must be on the page's origin. data-no-panel goes without the panel and shell mode, now that a frontend may be found without data-devtools-frontend-url: a page from such a CDN only debugged remotely needs it, or it gets a panel too. Unit tests cover the default frontend and the trust rules, path tricks included; e2e tests play the CDN through request interception: one tag enters shell mode with the frontend of the same version, a config pointing to another package on the CDN is dropped (it was trusted before), data-no-panel mounts nothing, and the built frontend from the CDN lays out the page. The README, usage guide, design notes and changelogs say so. --- CHANGELOG.md | 1 + CHANGELOG.zh-CN.md | 1 + README.md | 8 +- README.zh-CN.md | 8 +- docs/design.md | 2 +- docs/design.zh-CN.md | 2 +- docs/usage.md | 18 +-- docs/usage.zh-CN.md | 18 +-- .../etc/devtools-anywhere.api.md | 1 + .../etc/devtools-anywhere.public.api.md | 1 + .../src/cdp-agent/probe/index.ts | 1 + .../src/cdp-agent/probe/shell.ts | 43 +++--- .../src/cdp-agent/probe/trust.test.ts | 80 +++++++++++ .../src/cdp-agent/probe/trust.ts | 76 +++++++++++ .../devtools-anywhere/src/probe-script.ts | 8 +- packages/devtools-anywhere/src/probe.ts | 9 +- tests/e2e/cdn.test.ts | 129 ++++++++++++++++++ 17 files changed, 349 insertions(+), 57 deletions(-) create mode 100644 packages/devtools-anywhere/src/cdp-agent/probe/trust.test.ts create mode 100644 packages/devtools-anywhere/src/cdp-agent/probe/trust.ts create mode 100644 tests/e2e/cdn.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index ac9d81f..dbc3eb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ All packages share one version and are released together (see DEV.md, "Releasing ## Unreleased +- From jsDelivr or unpkg, the probe needs no other attribute: it loads the frontend of its own version from there, and shell mode works (without the Service Worker). Shell configs from such a CDN are trusted only in the probe's package and the frontend's at that version, no longer anywhere on the CDN. `data-no-panel` goes without the panel: a page from such a CDN only debugged remotely (`data-remote`) needs it, or it now gets a panel too. - The docked panel is resized with a grip on the middle of its inner edge, on the page's side, in place of the bar across its whole edge: it covers neither DevTools nor more of the page than it shows, and looks the same in and out of shell mode. The undocked window keeps its bar, which moves it. - Device mode in shell mode, as in Chrome: DevTools lays out the page and itself, the toolbar's device button sizes the page as a device (scaled to fit) and its menu takes screenshots, docked or undocked. It sets the page's size only, not its pixel ratio, user agent or touch input. A grip against the middle of DevTools' edge, on the page's side, resizes them without covering DevTools, larger on touch screens. Outside shell mode the button is still the screencast's. - `Page.captureScreenshot` and `Page.getLayoutMetrics`: screenshots of the viewport, a region or the whole page, rendered by snapDOM and marked `approximate: true`, for tools and agents that drive the page over CDP. diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md index 145a053..d831ab3 100644 --- a/CHANGELOG.zh-CN.md +++ b/CHANGELOG.zh-CN.md @@ -4,6 +4,7 @@ ## Unreleased +- 探针来自 jsDelivr 或 unpkg 时,不用再写其他属性:它会从同一个 CDN 加载同版本的前端,外壳模式也能用(没有 Service Worker)。来自这类 CDN 时,外壳配置只信任探针所在的包和同版本的前端包,不再信任整个 CDN。新增 `data-no-panel`,不挂载面板:从这类 CDN 引入、只做远程调试(`data-remote`)的页面需要加上它,否则现在也会出现面板。 - 停靠的面板改用内侧边缘正中、靠页面一侧的抓手调整大小,取代原来贯穿整条边的横条:它不遮挡 DevTools,也只占页面上它看得见的那一小块,外壳模式内外样式一致。浮动窗口仍保留用来移动的横条。 - 外壳模式下新增设备模式,和 Chrome 一样:由 DevTools 排布页面和自己,工具栏上的设备按钮按设备尺寸设置页面(缩放到适合窗口),它的菜单可以截图,停靠和取消停靠时都能用。它只设置页面的尺寸,不改变像素比、UA 和触摸输入。DevTools 面板边缘正中、靠页面一侧有一个抓手,用来调整大小,不遮挡 DevTools,触屏上更大。非外壳模式下,这个按钮仍然是投屏。 - 新增 `Page.captureScreenshot` 和 `Page.getLayoutMetrics`:可以截取视口、指定区域或整个页面,由 snapDOM 绘制,并标注 `approximate: true`,供通过 CDP 操作页面的工具和 agent 使用。 diff --git a/README.md b/README.md index 377e1a5..76888b2 100644 --- a/README.md +++ b/README.md @@ -35,12 +35,10 @@ import devtoolsAnywhere from '@devtools-anywhere/vite-plugin' export default { plugins: [devtoolsAnywhere()] } ``` -**Any page**, from a CDN, first in `<head>` (shell mode is not supported from a CDN, so it is turned off): +**Any page**, from a CDN, first in `<head>`: ```html -<script src="https://cdn.jsdelivr.net/npm/devtools-anywhere@0.1.0/dist/probe.iife.js" - data-devtools-frontend-url="https://cdn.jsdelivr.net/npm/@devtools-anywhere/devtools-frontend@0.1.0/dist/anywhere_app.html" - data-default-shell-mode="off"></script> +<script src="https://cdn.jsdelivr.net/npm/devtools-anywhere@0.2.0/dist/probe.iife.js"></script> ``` Open the page and tap the **F12** button. @@ -64,7 +62,7 @@ Self-hosting, every option and the element: see the [usage guide](docs/usage.md) | `data-no-toggler` | `noToggler` | Hides the F12 button | | `data-remote` | `remote` | Connects to a remote debugging server | -Shell mode (on by default when self-hosted) puts the page in an iframe, so DevTools stay open across reloads and device mode works: see [shell mode](docs/usage.md#shell-mode). +Shell mode (on by default) puts the page in an iframe, so DevTools stay open across reloads and device mode works: see [shell mode](docs/usage.md#shell-mode). ## What works diff --git a/README.zh-CN.md b/README.zh-CN.md index 866cfe7..8c5b668 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -35,12 +35,10 @@ import devtoolsAnywhere from '@devtools-anywhere/vite-plugin' export default { plugins: [devtoolsAnywhere()] } ``` -**任意页面**,用 CDN,放在 `<head>` 里第一个(CDN 上不支持外壳模式,所以关掉): +**任意页面**,用 CDN,放在 `<head>` 里第一个: ```html -<script src="https://cdn.jsdelivr.net/npm/devtools-anywhere@0.1.0/dist/probe.iife.js" - data-devtools-frontend-url="https://cdn.jsdelivr.net/npm/@devtools-anywhere/devtools-frontend@0.1.0/dist/anywhere_app.html" - data-default-shell-mode="off"></script> +<script src="https://cdn.jsdelivr.net/npm/devtools-anywhere@0.2.0/dist/probe.iife.js"></script> ``` 打开页面,点 **F12** 按钮。 @@ -64,7 +62,7 @@ npx -p @devtools-anywhere/remote-server devtools-anywhere-remote | `data-no-toggler` | `noToggler` | 隐藏 F12 按钮 | | `data-remote` | `remote` | 连接到远程调试服务端 | -外壳模式(自己部署时默认开启)把页面放进 iframe,刷新时 DevTools 不会关闭,也能用设备模式,见[外壳模式](docs/usage.zh-CN.md#外壳模式)。 +外壳模式(默认开启)把页面放进 iframe,刷新时 DevTools 不会关闭,也能用设备模式,见[外壳模式](docs/usage.zh-CN.md#外壳模式)。 ## 能用什么 diff --git a/docs/design.md b/docs/design.md index d884221..370b22d 100644 --- a/docs/design.md +++ b/docs/design.md @@ -53,7 +53,7 @@ No standalone `devtools.html` is hosted, and there is no navigation to a blob UR 5. DevTools fills the shell and lays out the page and itself, as in Chrome: the page iframe goes where the frontend's page area is, on top of it. Dock side and sizes are DevTools' own, set with its resizer. A grip also sits against the middle of DevTools' edge, on the page's side, like a tab of the panel; it is larger on touch screens. Undocked, DevTools is a window over the page. The toolbar's device button is device mode, as in Chrome, in place of the screencast: the page iframe is sized as the device and scaled to fit, and the toolbar's menu takes screenshots. Undocked, device mode's toolbar is drawn in a page of the frontend's (`device_mode_emulation_frame.html`, as Chrome does) shown under the window. 6. The toolbar's × hides DevTools and stays in shell mode; the page fills the shell at its own size until DevTools is shown again. The lock button again clears the config and reloads the page on its own; the choice is remembered in `localStorage`, and wins over the page's `data-default-shell-mode` from then on, unless the page sets `data-shell-mode-locked`. -The stored URLs are only trusted when they have the same origin as the probe script; otherwise the config is cleared and the page loads normally. Shell mode therefore needs the probe, the bundle and the frontend served from the page's origin. +The stored URLs are only trusted where the probe script says; otherwise the config is cleared and the page loads normally. That is the probe's origin, but on an npm CDN (jsDelivr, unpkg), where anyone can publish, only the probe's package and the frontend's, at the probe's version: the URLs are compared once parsed (so `..` and `%2e%2e` are resolved), and escaped slashes are refused. Shell mode therefore needs the bundle and the frontend served with the probe, from the page's origin or from such a CDN. Inside an iframe, `viewport`, `100vh`, `position: fixed`, `window.top` and others may behave differently from a top-level page; the page's HTML is requested twice (by the top-level window, then by the iframe), and a page returned for a form POST is requested again with GET. Turn shell mode off with `data-default-shell-mode="off"` (the Vite plugin's `defaultShellMode: 'off'`), and DevTools is embedded directly in the page. A page that refuses to be framed (`X-Frame-Options`, CSP `frame-ancestors`), or that keeps navigating the shell away, is shown on its own for the rest of the session. diff --git a/docs/design.zh-CN.md b/docs/design.zh-CN.md index 67a6733..0a4b5bc 100644 --- a/docs/design.zh-CN.md +++ b/docs/design.zh-CN.md @@ -53,7 +53,7 @@ devtools-anywhere 的工作原理和设计取舍。使用方法见[使用指南] 5. DevTools 铺满外壳,由它来排布页面和自己,和 Chrome 一样:页面 iframe 叠放在前端预留的页面区域上方。停靠位置和尺寸由 DevTools 自己管理(用它的分隔条;DevTools 面板边缘正中、靠页面一侧还有一个抓手,像面板的一个小标签,触屏上更大)。取消停靠时,DevTools 是页面上方的浮动窗口。工具栏上的设备按钮是设备模式,和 Chrome 一样,取代了投屏:页面 iframe 按设备尺寸设置并缩放到适合窗口,工具栏的菜单可以截图。取消停靠时,设备模式的工具栏画在前端的一个页面(`device_mode_emulation_frame.html`,Chrome 也是这么做的)里,显示在浮动窗口下方。 6. 点工具栏上的 × 只隐藏 DevTools,仍留在外壳模式;在重新显示 DevTools 之前,页面按自己的尺寸铺满外壳。再点一次锁形按钮会清除配置,并在普通模式下重新加载页面;这个选择会记在 `localStorage` 里,之后优先于页面的 `data-default-shell-mode`,除非页面设置了 `data-shell-mode-locked`。 -只有与探针脚本同源的地址才会被采信,否则清除配置、页面照常加载。因此外壳模式要求探针、bundle 和前端都由页面所在的源提供。 +只有探针脚本信任的地址才会被采信,否则清除配置、页面照常加载。一般信任的是探针的源;但在 npm CDN(jsDelivr、unpkg)上任何人都能发布包,所以只信任探针所在的包和同版本的前端包。比较前先解析地址(`..`、`%2e%2e` 会被消解),含转义斜杠的地址一律拒绝。因此外壳模式要求 bundle 和前端与探针一起提供:来自页面所在的源,或来自上述 CDN。 页面放进 iframe 后,`viewport`、`100vh`、`position: fixed`、`window.top` 等的行为可能和直接打开时不同;页面的 HTML 会被请求两次(顶层窗口一次,iframe 一次),表单 POST 返回的页面会被用 GET 再请求一次。用 `data-default-shell-mode="off"`(Vite 插件的 `defaultShellMode: 'off'`)可以关闭外壳模式,改为把 DevTools 直接嵌在页面中。拒绝被嵌入(`X-Frame-Options`、CSP `frame-ancestors`)或反复把外壳跳走的页面,在本会话剩下的时间里会单独显示。 diff --git a/docs/usage.md b/docs/usage.md index 248afbd..f64dd33 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -27,17 +27,16 @@ Serve the two packages' `dist/`, and put the probe first in `<head>`, synchronou data-devtools-frontend-url="/devtools-frontend/dist/anywhere_app.html"></script> ``` -From a CDN, pin one exact version for both packages, and turn [shell mode](#shell-mode) off (it is not supported from a CDN): +From jsDelivr or unpkg, pin an exact version: the probe loads the frontend of that same version from there. ```html -<script src="https://cdn.jsdelivr.net/npm/devtools-anywhere@0.1.0/dist/probe.iife.js" - data-devtools-frontend-url="https://cdn.jsdelivr.net/npm/@devtools-anywhere/devtools-frontend@0.1.0/dist/anywhere_app.html" - data-default-shell-mode="off"></script> +<script src="https://cdn.jsdelivr.net/npm/devtools-anywhere@0.2.0/dist/probe.iife.js"></script> ``` | | Self-hosted | CDN (jsDelivr, unpkg) | |---|---|---| -| Shell mode | ✅ On by default | ❌ Off | +| `data-devtools-frontend-url` | Required | Optional: the frontend of the same version on the CDN | +| Shell mode | ✅ On by default | ✅ On by default, without the [Service Worker](#service-worker) | | UI languages | All, with `@devtools-anywhere/devtools-frontend-locales` in the frontend's directory | English and Chinese | - After the page loads, the probe mounts one `<devtools-anywhere>` under `<html>`, whatever the page has; a second probe only warns. @@ -47,7 +46,8 @@ From a CDN, pin one exact version for both packages, and turn [shell mode](#shel | Attribute | Vite option | Default | Effect | |---|---|---|---| -| `data-devtools-frontend-url` | `devtoolsFrontendUrl` | Script tag: none. Vite: the installed frontend | The frontend's `anywhere_app.html`. Without it, no panel is mounted | +| `data-devtools-frontend-url` | `devtoolsFrontendUrl` | Script tag from jsDelivr or unpkg: the frontend of the same version there. Other script tags: none. Vite: the installed frontend | The frontend's `anywhere_app.html`. Without one, no panel is mounted | +| `data-no-panel` | — | Off | No panel and no shell mode, whatever the frontend: for a page only [debugged remotely](#remote-debugging) | | `data-bundle` | — | Next to the probe | URL of `devtools-anywhere.iife.js` | | `data-open-immediately` | `openImmediately` | Off | Opens DevTools as the page loads | | `data-default-shell-mode` | `defaultShellMode` | `on` | `off`: no shell mode, the panel covers the page | @@ -132,7 +132,7 @@ The top-level page is replaced by a shell: the page runs in an iframe next to De | Device mode | ✅ | Screencast instead | | Layout | The page shrinks next to DevTools | The panel covers the page | -On by default for the top-level page, when the bundle and frontend (and Service Worker) are on the probe's origin. It is off when: +On by default for the top-level page, when the bundle and frontend (and Service Worker) are where the probe trusts: on its origin, or from jsDelivr or unpkg, in the probe's package and the frontend's at the same version. It is off when: | Condition | | |---|---| @@ -190,7 +190,7 @@ The server serves the probe, bundle and frontend itself, and the frontend runs o - DevTools stays connected across reloads and navigations in the tab. A page whose connection drops reconnects by itself, and stays on the list meanwhile. - Several DevTools windows can inspect one page, each in its own session. - The target list can be filtered and shows how many DevTools each page has. -- With `data-devtools-frontend-url` too, the page also has its own panel. +- With a frontend too (`data-devtools-frontend-url`, or the probe from jsDelivr or unpkg), the page also has its own panel; `data-no-panel` goes without it. - Breakpoints still need the browser's own debugger (`chrome://inspect`, Safari Web Inspector): the page runs the same in-page agent. `data-remote` with a value names the server, when the probe comes from elsewhere. It resolves like a link: @@ -205,7 +205,7 @@ In shell mode: - The page in the shell connects, not the shell. - The shell's probe decides for every page in the shell; their own `data-remote` is ignored, so a page's scripts cannot send later pages elsewhere. - With `data-service-worker`, pages it injects connect too. -- Shell mode needs the frontend on the probe's origin: not with the probe from the server and a frontend elsewhere. +- Shell mode needs the frontend where the probe trusts it: not with the probe from the server and a frontend elsewhere. ### Node.js and endpoints diff --git a/docs/usage.zh-CN.md b/docs/usage.zh-CN.md index 111a4c4..ecfb15e 100644 --- a/docs/usage.zh-CN.md +++ b/docs/usage.zh-CN.md @@ -27,17 +27,16 @@ npm i devtools-anywhere @devtools-anywhere/devtools-frontend data-devtools-frontend-url="/devtools-frontend/dist/anywhere_app.html"></script> ``` -用 CDN 时,两个包固定到同一个确切版本,并关掉[外壳模式](#外壳模式)(CDN 上不支持): +用 jsDelivr 或 unpkg 时,固定一个确切版本:探针会从同一个 CDN 加载同版本的前端。 ```html -<script src="https://cdn.jsdelivr.net/npm/devtools-anywhere@0.1.0/dist/probe.iife.js" - data-devtools-frontend-url="https://cdn.jsdelivr.net/npm/@devtools-anywhere/devtools-frontend@0.1.0/dist/anywhere_app.html" - data-default-shell-mode="off"></script> +<script src="https://cdn.jsdelivr.net/npm/devtools-anywhere@0.2.0/dist/probe.iife.js"></script> ``` | | 自己部署 | CDN(jsDelivr、unpkg) | |---|---|---| -| 外壳模式 | ✅ 默认开启 | ❌ 关闭 | +| `data-devtools-frontend-url` | 必填 | 可选:默认用 CDN 上同版本的前端 | +| 外壳模式 | ✅ 默认开启 | ✅ 默认开启,但没有 [Service Worker](#service-worker) | | 界面语言 | 全部(`@devtools-anywhere/devtools-frontend-locales` 放在前端目录下) | 英文和中文 | - 页面加载完成后,探针在 `<html>` 下挂载一个 `<devtools-anywhere>`,不管页面里有没有;第二个探针只会警告。 @@ -47,7 +46,8 @@ npm i devtools-anywhere @devtools-anywhere/devtools-frontend | 属性 | Vite 选项 | 默认值 | 作用 | |---|---|---|---| -| `data-devtools-frontend-url` | `devtoolsFrontendUrl` | script 标签:无。Vite:已安装的前端 | 前端的 `anywhere_app.html`。不写就不挂载面板 | +| `data-devtools-frontend-url` | `devtoolsFrontendUrl` | 来自 jsDelivr、unpkg 的 script 标签:CDN 上同版本的前端。其他 script 标签:无。Vite:已安装的前端 | 前端的 `anywhere_app.html`。没有前端就不挂载面板 | +| `data-no-panel` | — | 关 | 不管有没有前端,都不挂载面板、不进外壳模式:用于只做[远程调试](#远程调试)的页面 | | `data-bundle` | — | 探针同目录 | `devtools-anywhere.iife.js` 的地址 | | `data-open-immediately` | `openImmediately` | 关 | 页面加载后直接打开 DevTools | | `data-default-shell-mode` | `defaultShellMode` | `on` | `off`:不用外壳模式,面板盖在页面上 | @@ -132,7 +132,7 @@ export default { plugins: [devtoolsAnywhere()] } | 设备模式 | ✅ | 换成投屏 | | 布局 | 页面在 DevTools 旁边缩小 | 面板盖在页面上 | -bundle、前端(以及 Service Worker)与探针同源时,顶层页面默认开启外壳模式。以下情况关闭: +bundle、前端(以及 Service Worker)在探针信任的位置时,顶层页面默认开启外壳模式。信任的位置是:与探针同源;探针来自 jsDelivr 或 unpkg 时,则只限探针所在的包和同版本的前端包。以下情况关闭: | 情况 | | |---|---| @@ -190,7 +190,7 @@ npx -p @devtools-anywhere/remote-server devtools-anywhere-remote - 页面刷新或在同一标签页内跳转时,DevTools 保持连接。断线的页面会自动重连,期间仍留在列表里。 - 多个 DevTools 窗口可以同时调试一个页面,各自一个会话。 - 目标列表可以筛选,并显示每个页面连着几个 DevTools。 -- 同时写了 `data-devtools-frontend-url` 时,页面还有自己的面板。 +- 同时有前端时(写了 `data-devtools-frontend-url`,或探针来自 jsDelivr、unpkg),页面还有自己的面板;不要面板时加 `data-no-panel`。 - 断点仍要靠浏览器自带的调试器(`chrome://inspect`、Safari Web Inspector):页面里运行的仍是同一个页面内 agent。 探针来自别处时,用带值的 `data-remote` 指定服务端,按链接的规则解析: @@ -205,7 +205,7 @@ npx -p @devtools-anywhere/remote-server devtools-anywhere-remote - 连接的是外壳里的页面,不是外壳本身。 - 外壳里的所有页面由外壳的探针决定,页面自己的 `data-remote` 无效,所以页面脚本没法把之后的页面指向别的服务端。 - 配合 `data-service-worker`,被它注入的页面也会连接。 -- 外壳模式要求前端与探针同源:探针来自服务端、前端在别处时用不了。 +- 外壳模式要求前端在探针信任的位置:探针来自服务端、前端在别处时用不了。 ### Node.js 与接口 diff --git a/packages/devtools-anywhere/etc/devtools-anywhere.api.md b/packages/devtools-anywhere/etc/devtools-anywhere.api.md index f3ca1ed..97f23fb 100644 --- a/packages/devtools-anywhere/etc/devtools-anywhere.api.md +++ b/packages/devtools-anywhere/etc/devtools-anywhere.api.md @@ -33,6 +33,7 @@ export interface DevtoolsAnywhereTagNameMap { // @public export type ProbeScriptAttributes = { 'data-devtools-frontend-url'?: string; + 'data-no-panel'?: true; 'data-bundle'?: string; 'data-open-immediately'?: true; 'data-default-shell-mode'?: 'on' | 'off'; diff --git a/packages/devtools-anywhere/etc/devtools-anywhere.public.api.md b/packages/devtools-anywhere/etc/devtools-anywhere.public.api.md index cb6f6ce..240bf46 100644 --- a/packages/devtools-anywhere/etc/devtools-anywhere.public.api.md +++ b/packages/devtools-anywhere/etc/devtools-anywhere.public.api.md @@ -33,6 +33,7 @@ export interface DevtoolsAnywhereTagNameMap { // @public export type ProbeScriptAttributes = { 'data-devtools-frontend-url'?: string; + 'data-no-panel'?: true; 'data-bundle'?: string; 'data-open-immediately'?: true; 'data-default-shell-mode'?: 'on' | 'off'; diff --git a/packages/devtools-anywhere/src/cdp-agent/probe/index.ts b/packages/devtools-anywhere/src/cdp-agent/probe/index.ts index 8dc1e6e..6b53563 100644 --- a/packages/devtools-anywhere/src/cdp-agent/probe/index.ts +++ b/packages/devtools-anywhere/src/cdp-agent/probe/index.ts @@ -20,6 +20,7 @@ import { isShellPage, readShellConfig, writeShellDocument } from './shell'; export * from './types'; export * from './shell'; +export { cdnFrontendUrl } from './trust'; /** The probe keeps everything up to a coarse bound, for pages that never load the CDP agent. */ const LIMIT = 10_000; diff --git a/packages/devtools-anywhere/src/cdp-agent/probe/shell.ts b/packages/devtools-anywhere/src/cdp-agent/probe/shell.ts index 8c3974b..5e9cb16 100644 --- a/packages/devtools-anywhere/src/cdp-agent/probe/shell.ts +++ b/packages/devtools-anywhere/src/cdp-agent/probe/shell.ts @@ -8,6 +8,8 @@ // storing the config itself on the first load, unless shell mode is turned off (see // SHELL_MODE_KEY, SHELL_REFUSED_KEY). +import { type Trust, isTrusted, trustOf } from './trust'; + /** @internal */ export const SHELL_KEY = 'devtools-anywhere:shell'; /** @@ -44,44 +46,35 @@ export interface ShellConfig { /** What the shell's element takes from the page besides its URLs. @internal */ export type ShellFlags = Pick<ShellConfig, 'open' | 'locked' | 'noToggler'>; -let trustedOrigin = ''; +let trust: Trust | null = null; /** - * Sets the origin shell configs must come from: that of the script that read them (the probe, - * or the bundle). Its URL is taken while the script runs synchronously. Unset, the page's own - * origin is used. + * Sets where shell configs may point, from the URL of the script that reads them (the probe, or + * the bundle; trust.ts). Its URL is taken while the script runs synchronously. Unset, the page's + * own origin is trusted. * * @internal */ export function setShellScriptUrl(url: string) { - try { - trustedOrigin = new URL(url).origin; - } catch { - // Keep the previous origin. - } + // An invalid URL keeps the previous trust. + trust = trustOf(url) ?? trust; } /** - * True when every URL of the config is http(s) on the trusted origin. sessionStorage can be - * written by any script of the page, so a config pointing elsewhere is not trusted. Only the - * origin is checked: the frontend, bundle and probe may sit in different directories (e.g. the - * Vite plugin's base vs. a zero-config `data-devtools-frontend-url`). The remote server, which - * may be anywhere, is not kept here for that reason: the shell's probe gives it to the pages in - * the shell itself (see probe.ts). + * True when every URL of the config is trusted (trust.ts). sessionStorage can be written by any + * script of the page, so a config pointing elsewhere is not trusted. Off npm CDNs only the origin + * is checked: the frontend, bundle and probe may sit in different directories (e.g. the Vite + * plugin's base vs. a zero-config `data-devtools-frontend-url`). The remote server, which may be + * anywhere, is not kept here for that reason: the shell's probe gives it to the pages in the + * shell itself (see probe.ts). * * @internal */ export function isTrustedShellConfig(config: ShellConfig): boolean { - const origin = trustedOrigin || location.origin; - return [config.frontend, config.bundle, config.serviceWorker].every(url => { - if (url === undefined) return true; - try { - const parsed = new URL(url); - return /^https?:$/.test(parsed.protocol) && parsed.origin === origin; - } catch { - return false; - } - }); + const trusted = trust ?? { origin: location.origin }; + return [config.frontend, config.bundle, config.serviceWorker].every( + url => url === undefined || isTrusted(url, trusted) + ); } /** diff --git a/packages/devtools-anywhere/src/cdp-agent/probe/trust.test.ts b/packages/devtools-anywhere/src/cdp-agent/probe/trust.test.ts new file mode 100644 index 0000000..9f4a5d3 --- /dev/null +++ b/packages/devtools-anywhere/src/cdp-agent/probe/trust.test.ts @@ -0,0 +1,80 @@ +import { describe, test } from 'node:test'; +import assert from 'node:assert/strict'; +import { cdnFrontendUrl, isTrusted, trustOf } from './trust.ts'; + +const JSDELIVR = 'https://cdn.jsdelivr.net/npm/'; +const PROBE = `${JSDELIVR}devtools-anywhere@0.2.0/dist/probe.iife.js`; +const FRONTEND = `${JSDELIVR}@devtools-anywhere/devtools-frontend@0.2.0/dist/anywhere_app.html`; + +/** Whether a script at `script` trusts `url`. */ +const trusts = (script: string, url: string) => { + const trust = trustOf(script); + assert.ok(trust); + return isTrusted(url, trust); +}; + +describe('the frontend by default', () => { + test('on jsDelivr and unpkg, the frontend package of the same version', () => { + assert.equal(cdnFrontendUrl(PROBE), FRONTEND); + assert.equal( + cdnFrontendUrl('https://unpkg.com/devtools-anywhere@0.2.0/dist/probe.iife.js'), + 'https://unpkg.com/@devtools-anywhere/devtools-frontend@0.2.0/dist/anywhere_app.html' + ); + // A dist-tag is kept as it is. + assert.equal( + cdnFrontendUrl(`${JSDELIVR}devtools-anywhere@latest/dist/probe.iife.js`), + `${JSDELIVR}@devtools-anywhere/devtools-frontend@latest/dist/anywhere_app.html` + ); + }); + + for (const [what, script] of Object.entries({ + 'self-hosted': 'https://example.com/devtools-anywhere/dist/probe.iife.js', + 'without a version': `${JSDELIVR}devtools-anywhere/dist/probe.iife.js`, + 'another package': `${JSDELIVR}devtools-anywhere-fork@0.2.0/dist/probe.iife.js`, + 'an escaped version': `${JSDELIVR}devtools-anywhere@0.2.0%2F..%2Fx/dist/probe.iife.js`, + 'no URL': 'probe.iife.js', + })) { + test(`none ${what}`, () => assert.equal(cdnFrontendUrl(script), null)); + } +}); + +describe('what a shell config may point to', () => { + test('on an npm CDN, this package and the frontend package at the same version', () => { + assert.equal(trusts(PROBE, FRONTEND), true); + assert.equal(trusts(PROBE, `${JSDELIVR}devtools-anywhere@0.2.0/dist/devtools-anywhere.iife.js`), true); + }); + + for (const [what, url] of Object.entries({ + 'another version of the frontend': `${JSDELIVR}@devtools-anywhere/devtools-frontend@0.1.0/dist/anywhere_app.html`, + 'another version of this package': `${JSDELIVR}devtools-anywhere@0.1.0/dist/devtools-anywhere.iife.js`, + 'another package': `${JSDELIVR}evil@1.0.0/anywhere_app.html`, + 'a package that starts the same': `${JSDELIVR}devtools-anywhere@0.2.0-evil/dist/devtools-anywhere.iife.js`, + 'a scope that starts the same': `${JSDELIVR}@devtools-anywhere/devtools-frontend@0.2.0x/dist/anywhere_app.html`, + 'dot segments': `${JSDELIVR}devtools-anywhere@0.2.0/../evil@1.0.0/x.html`, + 'escaped dot segments': `${JSDELIVR}devtools-anywhere@0.2.0/%2e%2e/evil@1.0.0/x.html`, + 'escaped slashes': `${JSDELIVR}devtools-anywhere@0.2.0/..%2F..%2Fevil@1.0.0/x.html`, + 'escaped backslashes': `${JSDELIVR}devtools-anywhere@0.2.0/..%5C..%5Cevil@1.0.0/x.html`, + backslashes: `${JSDELIVR}devtools-anywhere@0.2.0\\..\\..\\evil@1.0.0/x.html`, + 'another CDN': 'https://unpkg.com/@devtools-anywhere/devtools-frontend@0.2.0/dist/anywhere_app.html', + 'plain http': FRONTEND.replace('https:', 'http:'), + credentials: FRONTEND.replace('https://', 'https://user@'), + 'not http': `javascript:alert(1)//${FRONTEND}`, + })) { + test(`not ${what}`, () => assert.equal(trusts(PROBE, url), false)); + } + + test('on an npm CDN but not this package, nothing', () => { + const other = `${JSDELIVR}evil@1.0.0/probe.iife.js`; + assert.equal(trusts(other, `${JSDELIVR}evil@1.0.0/anywhere_app.html`), false); + assert.equal(trusts(other, FRONTEND), false); + }); + + test('elsewhere, the whole origin of the script', () => { + const probe = 'https://example.com/static/probe.iife.js'; + assert.equal(trusts(probe, 'https://example.com/other/anywhere_app.html'), true); + assert.equal(trusts(probe, 'https://example.org/anywhere_app.html'), false); + assert.equal(trusts(probe, 'https://example.com:8443/anywhere_app.html'), false); + }); + + test('no URL, no trust', () => assert.equal(trustOf('probe.iife.js'), null)); +}); diff --git a/packages/devtools-anywhere/src/cdp-agent/probe/trust.ts b/packages/devtools-anywhere/src/cdp-agent/probe/trust.ts new file mode 100644 index 0000000..922cc45 --- /dev/null +++ b/packages/devtools-anywhere/src/cdp-agent/probe/trust.ts @@ -0,0 +1,76 @@ +// What a shell config may point to, from the URL of the script that reads it (the probe, or the +// bundle). sessionStorage can be written by any script of the page, so a config is trusted only +// where that script says. On a public npm CDN (jsDelivr, unpkg), where anyone can publish, its +// origin is everyone's: only this package and the frontend's, at the script's version, are +// trusted, and the frontend is found there by default. Elsewhere, the script's origin is. + +/** The CDNs whose paths are npm packages: `<prefix><name>@<version>/<file>`. */ +const NPM_CDNS = ['https://cdn.jsdelivr.net/npm/', 'https://unpkg.com/']; +const PACKAGE = 'devtools-anywhere'; +const FRONTEND_PACKAGE = '@devtools-anywhere/devtools-frontend'; +/** The frontend's entry page in its package. */ +const FRONTEND_ENTRY = 'dist/anywhere_app.html'; +/** A version or dist-tag, as in a CDN path: no `/`, nothing escaped. */ +const VERSION = /^[0-9A-Za-z.+-]+$/; + +/** Where URLs are trusted: an origin, or (on an npm CDN) URL prefixes, maybe none. */ +export type Trust = { origin: string } | { prefixes: string[] }; + +/** The package directories at the version of `script`, a URL of this package on an npm CDN. */ +function cdnPackages(script: URL): { own: string; frontend: string } | null { + const cdn = NPM_CDNS.find(prefix => script.href.startsWith(prefix)); + if (!cdn) return null; + const [name, rest] = splitOnce(script.href.slice(cdn.length), '/'); + const [pkg, version] = splitOnce(name, '@'); + if (pkg !== PACKAGE || !VERSION.test(version) || !rest) return null; + return { own: `${cdn}${PACKAGE}@${version}/`, frontend: `${cdn}${FRONTEND_PACKAGE}@${version}/` }; +} + +function splitOnce(s: string, separator: string): [string, string] { + const at = s.indexOf(separator); + return at < 0 ? [s, ''] : [s.slice(0, at), s.slice(at + 1)]; +} + +/** What a script at `scriptUrl` trusts; `null` when it is no URL. @internal */ +export function trustOf(scriptUrl: string): Trust | null { + let script: URL; + try { + script = new URL(scriptUrl); + } catch { + return null; + } + const packages = cdnPackages(script); + if (packages) return { prefixes: [packages.own, packages.frontend] }; + // Another package on the CDN, or no version: nothing there is ours. + if (NPM_CDNS.some(prefix => script.href.startsWith(prefix))) return { prefixes: [] }; + return { origin: script.origin }; +} + +/** Whether `url` is an http(s) URL that `trust` trusts. @internal */ +export function isTrusted(url: string, trust: Trust): boolean { + let parsed: URL; + try { + parsed = new URL(url); + } catch { + return false; + } + if (!/^https?:$/.test(parsed.protocol)) return false; + if ('origin' in trust) return parsed.origin === trust.origin; + // Parsed, `.` and `..` segments are resolved, `%2e` ones included; an escaped `/` or `\` is not, + // and a CDN may still take it for one. + if (/%2f|%5c/i.test(parsed.pathname)) return false; + return trust.prefixes.some(prefix => parsed.href.startsWith(prefix)); +} + +/** + * The frontend's entry page at the version of `scriptUrl`, when it is this package on an npm CDN: + * the frontend to use by default. @internal + */ +export function cdnFrontendUrl(scriptUrl: string): string | null { + try { + const packages = cdnPackages(new URL(scriptUrl)); + return packages && packages.frontend + FRONTEND_ENTRY; + } catch { + return null; + } +} diff --git a/packages/devtools-anywhere/src/probe-script.ts b/packages/devtools-anywhere/src/probe-script.ts index 3eb2ca8..ebe7355 100644 --- a/packages/devtools-anywhere/src/probe-script.ts +++ b/packages/devtools-anywhere/src/probe-script.ts @@ -5,8 +5,14 @@ * @public */ export type ProbeScriptAttributes = { - /** The frontend to mount <devtools-anywhere> with, after the page has loaded. */ + /** + * The frontend to mount <devtools-anywhere> with, after the page has loaded. By default, with the + * probe from jsDelivr or unpkg, the frontend package of the same version there; otherwise none, + * and no element. + */ 'data-devtools-frontend-url'?: string; + /** No element (and no shell mode), whatever the frontend: for a page only debugged remotely. */ + 'data-no-panel'?: true; /** Where devtools-anywhere.iife.js is, by default next to the probe. */ 'data-bundle'?: string; /** Opens DevTools right away. */ diff --git a/packages/devtools-anywhere/src/probe.ts b/packages/devtools-anywhere/src/probe.ts index 4ec3c3f..35ef0e7 100644 --- a/packages/devtools-anywhere/src/probe.ts +++ b/packages/devtools-anywhere/src/probe.ts @@ -2,6 +2,8 @@ // // <script src="…/devtools-anywhere/dist/probe.iife.js" data-devtools-frontend-url="…/anywhere_app.html"></script> // +// From jsDelivr or unpkg, `data-devtools-frontend-url` defaults to the frontend package of the same +// version there (trust.ts); `data-no-panel` goes without it, for a page only debugged remotely. // As the first script in <head>, it captures from the very start. With `data-devtools-frontend-url`, it // loads devtools-anywhere.iife.js (from `data-bundle`, by default next to this file) after // the page has loaded and mounts its own <devtools-anywhere> under <html>, whatever the page has. @@ -33,6 +35,7 @@ // in probe.iife.js) and `shell` query parameters, and kept while it answers that a shell in // another tab still uses it. The page's own workers are left alone. import { + cdnFrontendUrl, SHELL_KEY, SHELL_MODE_KEY, SHELL_PAGE_ID, @@ -54,7 +57,11 @@ if (script?.src) setShellScriptUrl(script.src); /** One of the script's attributes (probe-script.ts); `null` when it is absent. */ const attribute = (name: keyof ProbeScriptAttributes) => script?.getAttribute(name) ?? null; -const frontend = attribute('data-devtools-frontend-url'); +// Loaded from an npm CDN, the probe finds the frontend of its version there by default. +const frontend = + attribute('data-no-panel') === null + ? attribute('data-devtools-frontend-url') || (script?.src ? cdnFrontendUrl(script.src) : null) + : null; const remote = attribute('data-remote'); // A probe inlined, without a URL, has no bundle next to it. const bundle = script?.src ? new URL(attribute('data-bundle') || BUNDLE_SCRIPT, script.src).href : null; diff --git a/tests/e2e/cdn.test.ts b/tests/e2e/cdn.test.ts new file mode 100644 index 0000000..2a8c6f7 --- /dev/null +++ b/tests/e2e/cdn.test.ts @@ -0,0 +1,129 @@ +// The probe from an npm CDN (jsDelivr): one script tag, with the frontend of the same version found +// there, shell mode included; and a shell config pointing elsewhere on the CDN is not trusted. The +// CDN is played by the static server, through request interception: the probe's package from the +// build, the frontend's entry page from the fake frontend. +import { existsSync } from 'node:fs'; +import { after, before, beforeEach, test } from 'node:test'; +import assert from 'node:assert/strict'; +import type { HTTPRequest, Page } from 'puppeteer-core'; +import { launch } from '../support/browser.ts'; +import type { Env } from '../support/browser.ts'; + +const CDN = 'https://cdn.jsdelivr.net/npm/'; +const PROBE = `${CDN}devtools-anywhere@9.9.9/dist/probe.iife.js`; +const FRONTEND = `${CDN}@devtools-anywhere/devtools-frontend@9.9.9/dist/anywhere_app.html`; + +let env: Env | undefined; +let page: Page; +let url = ''; + +before(async () => { + env = await launch(); + url = env.baseUrl + '/cdn.html'; +}); +after(() => env?.close()); +beforeEach(async () => { + await page?.close(); + assert.ok(env); + page = await env.browser.newPage(); +}); + +const built = existsSync(new URL('../../packages/devtools-frontend/dist/anywhere_app.html', import.meta.url)); + +/** Serves the page at `url`, with the probe's script tag given these attributes, and the CDN. */ +async function serve(attributes = '', frontend: 'fake' | 'built' = 'fake') { + const base = env?.baseUrl ?? ''; + const files: [string, string][] = [ + [`${CDN}devtools-anywhere@9.9.9/`, `${base}/packages/devtools-anywhere/`], + frontend === 'fake' + ? [FRONTEND, `${base}/tests/e2e/frontends/fake-frontend.html`] + : [`${CDN}@devtools-anywhere/devtools-frontend@9.9.9/`, `${base}/packages/devtools-frontend/`], + [`${CDN}evil@1.0.0/anywhere_app.html`, `${base}/tests/e2e/frontends/fake-frontend.html`], + ]; + const html = `<!doctype html><html><head><meta charset="utf-8" /> + <script src="${PROBE}" ${attributes}></script> + <script>window.__pageScriptRan = true;</script> + </head><body><p>cdn</p></body></html>`; + await page.setRequestInterception(true); + page.on('request', (r: HTTPRequest) => { + const at = r.url(); + if (at === url) return void r.respond({ status: 200, contentType: 'text/html', body: html }); + const file = files.find(([prefix]) => at.startsWith(prefix)); + if (!file) return void r.continue(); + // Cross-origin, as from the CDN. + void fetch(file[1] + at.slice(file[0].length)).then(async response => + r.respond({ + status: response.status, + contentType: response.headers.get('content-type') ?? '', + headers: { 'access-control-allow-origin': '*' }, + body: Buffer.from(await response.arrayBuffer()), + }) + ); + }); +} + +/** The URL of the frontend iframe of the global element, once there. */ +const frontendSrc = () => + page + .waitForFunction(() => { + const frame = document.querySelector('devtools-anywhere')?.shadowRoot?.querySelector('iframe'); + return frame?.src || null; + }) + .then(handle => handle.jsonValue()); + +test('one script tag: the frontend of the same version on the CDN, in shell mode', async () => { + await serve('data-open-immediately'); + await page.goto(url); + await page.waitForFunction(() => document.querySelector('devtools-anywhere[data-shell]')); + const src = new URL((await frontendSrc()) ?? ''); + assert.equal(src.origin + src.pathname, FRONTEND); + const config = await page.evaluate(() => sessionStorage.getItem('devtools-anywhere:shell')); + assert.equal(JSON.parse(config ?? '{}').frontend, FRONTEND); +}); + +test('a shell config pointing to another package on the CDN is removed and ignored', async () => { + await serve('data-default-shell-mode="off"'); + await page.goto(url); + await page.waitForFunction(() => window.__pageScriptRan === true); + const evil = `${CDN}evil@1.0.0/anywhere_app.html`; + await page.evaluate( + (frontend, bundle) => + sessionStorage.setItem('devtools-anywhere:shell', JSON.stringify({ frontend, bundle, open: true })), + evil, + `${CDN}devtools-anywhere@9.9.9/dist/devtools-anywhere.iife.js` + ); + await page.reload(); + await page.waitForFunction(() => window.__pageScriptRan === true); + assert.equal(await page.evaluate(() => !!document.querySelector('devtools-anywhere[data-shell]')), false); + assert.equal(await page.evaluate(() => sessionStorage.getItem('devtools-anywhere:shell')), null); +}); + +test('data-no-panel: no element, and no shell mode', async () => { + await serve('data-no-panel'); + await page.goto(url); + await page.waitForFunction(() => window.__pageScriptRan === true && document.readyState === 'complete'); + // The bundle would have mounted the element after the load event. + await new Promise(r => setTimeout(r, 500)); + assert.equal(await page.evaluate(() => document.querySelectorAll('devtools-anywhere').length), 0); + assert.equal(await page.evaluate(() => sessionStorage.getItem('devtools-anywhere:shell')), null); +}); + +test( + 'the built frontend from the CDN lays out the page in shell mode', + { skip: built ? false : 'run `pnpm --filter @devtools-anywhere/devtools-frontend build:frontend` first' }, + async () => { + await page.setViewport({ width: 1000, height: 800 }); + await serve('data-open-immediately', 'built'); + await page.goto(url); + // The frontend, on another origin, says where the page goes: above DevTools, docked at the bottom. + await page.waitForFunction( + () => { + const frame = document.getElementById('devtools-anywhere-page'); + if (!(frame instanceof HTMLIFrameElement)) return false; + const { top, height } = frame.getBoundingClientRect(); + return top === 0 && height > 100 && height < innerHeight - 100; + }, + { timeout: 30000 } + ); + } +); From 5878c44da15991d0517ea526c1efbbb704d67aaa Mon Sep 17 00:00:00 2001 From: jerrychan7 <czy-ken@163.com> Date: Mon, 2 Nov 2026 02:28:27 +0800 Subject: [PATCH 20/26] docs(changelog): note the console.table fix 66a2a30 fixed console.table of objects, which showed a single "Value" column, but did not say so in the changelogs. --- CHANGELOG.md | 1 + CHANGELOG.zh-CN.md | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbc3eb4..8e0ec0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ All packages share one version and are released together (see DEV.md, "Releasing - From jsDelivr or unpkg, the probe needs no other attribute: it loads the frontend of its own version from there, and shell mode works (without the Service Worker). Shell configs from such a CDN are trusted only in the probe's package and the frontend's at that version, no longer anywhere on the CDN. `data-no-panel` goes without the panel: a page from such a CDN only debugged remotely (`data-remote`) needs it, or it now gets a panel too. - The docked panel is resized with a grip on the middle of its inner edge, on the page's side, in place of the bar across its whole edge: it covers neither DevTools nor more of the page than it shows, and looks the same in and out of shell mode. The undocked window keeps its bar, which moves it. - Device mode in shell mode, as in Chrome: DevTools lays out the page and itself, the toolbar's device button sizes the page as a device (scaled to fit) and its menu takes screenshots, docked or undocked. It sets the page's size only, not its pixel ratio, user agent or touch input. A grip against the middle of DevTools' edge, on the page's side, resizes them without covering DevTools, larger on touch screens. Outside shell mode the button is still the screencast's. +- `console.table` of objects shows their fields as columns, as in Chrome, instead of a single "Value" column; columns given as an array filter them. - `Page.captureScreenshot` and `Page.getLayoutMetrics`: screenshots of the viewport, a region or the whole page, rendered by snapDOM and marked `approximate: true`, for tools and agents that drive the page over CDP. ## 0.1.0 diff --git a/CHANGELOG.zh-CN.md b/CHANGELOG.zh-CN.md index d831ab3..cdd3d6b 100644 --- a/CHANGELOG.zh-CN.md +++ b/CHANGELOG.zh-CN.md @@ -7,6 +7,7 @@ - 探针来自 jsDelivr 或 unpkg 时,不用再写其他属性:它会从同一个 CDN 加载同版本的前端,外壳模式也能用(没有 Service Worker)。来自这类 CDN 时,外壳配置只信任探针所在的包和同版本的前端包,不再信任整个 CDN。新增 `data-no-panel`,不挂载面板:从这类 CDN 引入、只做远程调试(`data-remote`)的页面需要加上它,否则现在也会出现面板。 - 停靠的面板改用内侧边缘正中、靠页面一侧的抓手调整大小,取代原来贯穿整条边的横条:它不遮挡 DevTools,也只占页面上它看得见的那一小块,外壳模式内外样式一致。浮动窗口仍保留用来移动的横条。 - 外壳模式下新增设备模式,和 Chrome 一样:由 DevTools 排布页面和自己,工具栏上的设备按钮按设备尺寸设置页面(缩放到适合窗口),它的菜单可以截图,停靠和取消停靠时都能用。它只设置页面的尺寸,不改变像素比、UA 和触摸输入。DevTools 面板边缘正中、靠页面一侧有一个抓手,用来调整大小,不遮挡 DevTools,触屏上更大。非外壳模式下,这个按钮仍然是投屏。 +- `console.table` 传入对象时,和 Chrome 一样按字段分列显示,不再只有一列「Value」;以数组给出列名时只显示这些列。 - 新增 `Page.captureScreenshot` 和 `Page.getLayoutMetrics`:可以截取视口、指定区域或整个页面,由 snapDOM 绘制,并标注 `approximate: true`,供通过 CDP 操作页面的工具和 agent 使用。 ## 0.1.0 From cfbe3a7ef7d6c4c9c8949da229048d3e65d5358d Mon Sep 17 00:00:00 2001 From: jerrychan7 <czy-ken@163.com> Date: Mon, 2 Nov 2026 02:49:07 +0800 Subject: [PATCH 21/26] fix(shell): restore the page when the shell's element goes, and narrow device mode's page Found in review: - Removed from the shell for good, the element left the page iframe as the layout placed it (fixed, maybe at a device's size, scaled and clipped). ShellLayout.dispose now clears that, so the page fills the document again. - Device mode's page (the toolbox) was accepted from anywhere on the frontend's origin, which on an npm CDN is every package's. It must now sit next to the frontend's entry page, compared as trust.ts compares shell configs. - A grip's drag ends on lostpointercapture, which follows pointerup and pointercancel and also fires when the grip is removed mid-drag, so the drag always ends once. - The probe's and bundle's comments said shell configs must come from the script's origin, no longer the rule on a CDN. Device mode's e2e tests check that the element removed from the shell leaves the page unplaced, and that a page of the frontend's origin but not next to it is refused as the toolbox; both fail without the fix. --- .../devtools-anywhere/src/cdp-agent/index.ts | 1 + packages/devtools-anywhere/src/element.ts | 11 ++++++---- packages/devtools-anywhere/src/grip.ts | 9 ++++----- packages/devtools-anywhere/src/iife.ts | 2 +- packages/devtools-anywhere/src/probe.ts | 2 +- .../devtools-anywhere/src/shell-layout.ts | 2 ++ tests/e2e/device-mode.test.ts | 20 +++++++++++++++++++ 7 files changed, 36 insertions(+), 11 deletions(-) diff --git a/packages/devtools-anywhere/src/cdp-agent/index.ts b/packages/devtools-anywhere/src/cdp-agent/index.ts index 7ff2491..8202899 100644 --- a/packages/devtools-anywhere/src/cdp-agent/index.ts +++ b/packages/devtools-anywhere/src/cdp-agent/index.ts @@ -35,6 +35,7 @@ export { storeShellConfig, storeShellOpen, } from './probe/shell'; +export { isTrusted } from './probe/trust'; export { FRAME_ID, LOADER_ID } from './core/context'; export type { SnapdomLoader } from './domains/page/render'; diff --git a/packages/devtools-anywhere/src/element.ts b/packages/devtools-anywhere/src/element.ts index 7c42547..91615e2 100644 --- a/packages/devtools-anywhere/src/element.ts +++ b/packages/devtools-anywhere/src/element.ts @@ -5,6 +5,7 @@ import { createCdpSession, isShellDocument, isShellPage, + isTrusted, markOwn, OWN_ATTRIBUTE, readShellConfig, @@ -371,14 +372,16 @@ export class DevtoolsAnywhereElement extends HTMLElement { } else if (request.type === 'split') { this.shellLayout?.splitArea(request); } else if (request.type === 'toolbox') { - // Only a page of the frontend's own origin. - let url: URL; + // Only a page next to the frontend's: on a CDN, its origin is every package's. + let url: string; + let frontendDir: string; try { - url = new URL(request.url, this.loadedUrl); + url = new URL(request.url, this.loadedUrl).href; + frontendDir = new URL('.', this.loadedUrl).href; } catch { return; } - if (url.origin === this.frontendOrigin) this.shellLayout?.showToolbox(url.href); + if (isTrusted(url, { prefixes: [frontendDir] })) this.shellLayout?.showToolbox(url); } }; diff --git a/packages/devtools-anywhere/src/grip.ts b/packages/devtools-anywhere/src/grip.ts index 55c8be9..e6daf87 100644 --- a/packages/devtools-anywhere/src/grip.ts +++ b/packages/devtools-anywhere/src/grip.ts @@ -106,15 +106,14 @@ export class Grip { el.setPointerCapture(down.pointerId); this.options.onStart?.(down); const move = (e: PointerEvent) => this.options.onMove(e, down); - const up = () => { + // Released, or the capture lost (the grip removed mid-drag): a single end either way. + const end = () => { el.removeEventListener('pointermove', move); - el.removeEventListener('pointerup', up); - el.removeEventListener('pointercancel', up); + el.removeEventListener('lostpointercapture', end); this.options.onEnd?.(); }; el.addEventListener('pointermove', move); - el.addEventListener('pointerup', up); - el.addEventListener('pointercancel', up); + el.addEventListener('lostpointercapture', end); }); } } diff --git a/packages/devtools-anywhere/src/iife.ts b/packages/devtools-anywhere/src/iife.ts index 2ee5b69..cc120e4 100644 --- a/packages/devtools-anywhere/src/iife.ts +++ b/packages/devtools-anywhere/src/iife.ts @@ -19,7 +19,7 @@ import { connectRemote } from './remote'; // again itself: only the first copy runs. if (!('DevtoolsAnywhere' in window)) { const script = document.currentScript as HTMLScriptElement | null; - // Shell configs must come from this script's origin. + // Shell configs may only point where this script trusts (trust.ts). if (script?.src) setShellScriptUrl(script.src); const probe = installProbe(); if (script?.src) { diff --git a/packages/devtools-anywhere/src/probe.ts b/packages/devtools-anywhere/src/probe.ts index 35ef0e7..8407461 100644 --- a/packages/devtools-anywhere/src/probe.ts +++ b/packages/devtools-anywhere/src/probe.ts @@ -52,7 +52,7 @@ import type { ProbeScriptAttributes } from './probe-script'; import { SHELL_QUERY, SHELL_REMOTE } from './shell-messages'; const script = document.currentScript as HTMLScriptElement | null; -// Shell configs must come from this script's origin. +// Shell configs may only point where this script trusts (trust.ts). if (script?.src) setShellScriptUrl(script.src); /** One of the script's attributes (probe-script.ts); `null` when it is absent. */ diff --git a/packages/devtools-anywhere/src/shell-layout.ts b/packages/devtools-anywhere/src/shell-layout.ts index 4630c1c..8d6694a 100644 --- a/packages/devtools-anywhere/src/shell-layout.ts +++ b/packages/devtools-anywhere/src/shell-layout.ts @@ -79,6 +79,8 @@ export class ShellLayout { this.grip.dispose(); this.pageArea.remove(); this.removeToolbox(); + // Not left at the device's size, or scaled: the page fills the document again. + place(this.page, {}); } /** DevTools was shown or hidden, or docked elsewhere. */ diff --git a/tests/e2e/device-mode.test.ts b/tests/e2e/device-mode.test.ts index 69c0c61..54c0929 100644 --- a/tests/e2e/device-mode.test.ts +++ b/tests/e2e/device-mode.test.ts @@ -350,6 +350,26 @@ test('on a touch screen, a handle on the edge between the page and DevTools resi }); }); +test("device mode's page is only one next to the frontend's", { skip }, async () => { + // Same origin as the frontend, but not its page: refused. + await ( + await frontend() + ).evaluate(() => + parent.postMessage({ devtoolsAnywhere: { type: 'toolbox', url: '/tests/e2e/pages/device-mode.html' } }, '*') + ); + await new Promise(r => setTimeout(r, 300)); + assert.equal(await page.evaluate(() => document.querySelectorAll('body > iframe[title="Device mode"]').length), 0); +}); + +test('removed from the shell, the element leaves the page filling it, not placed', { skip }, async () => { + const style = await page.evaluate(() => { + document.querySelector('devtools-anywhere')?.remove(); + const frame = document.getElementById('devtools-anywhere-page'); + return frame && { position: frame.style.position, transform: frame.style.transform, height: frame.style.height }; + }); + assert.deepEqual(style, { position: '', transform: '', height: '' }); +}); + test('no uncaught errors', { skip }, () => { assert.deepEqual(errors, []); }); From 37a2212feddb0451696655b33ceaf8f75e526db3 Mon Sep 17 00:00:00 2001 From: jerrychan7 <czy-ken@163.com> Date: Mon, 2 Nov 2026 02:49:08 +0800 Subject: [PATCH 22/26] test: restore what the tests patch even when they fail, and prove the CDN probe ran Found in review: - Device mode's tests patched OffscreenCanvas and anchor clicks, the shell's matchMedia and the dock side, and restored them only once their checks passed: a failure left the later tests on a patched page. They restore in finally now. - The CDN tests that check nothing happens (a config to another package dropped, data-no-panel) also passed when the probe never ran. They now wait for the probe's element, or for the bundle with data-remote, instead of a fixed delay. A CDN file that fails to load aborts its request rather than leaving it unanswered. - The trust rules are checked on unpkg and at a dist-tag too. --- .../src/cdp-agent/probe/trust.test.ts | 11 ++ tests/e2e/cdn.test.ts | 30 +++-- tests/e2e/device-mode.test.ts | 121 ++++++++++-------- 3 files changed, 95 insertions(+), 67 deletions(-) diff --git a/packages/devtools-anywhere/src/cdp-agent/probe/trust.test.ts b/packages/devtools-anywhere/src/cdp-agent/probe/trust.test.ts index 9f4a5d3..5c0ba45 100644 --- a/packages/devtools-anywhere/src/cdp-agent/probe/trust.test.ts +++ b/packages/devtools-anywhere/src/cdp-agent/probe/trust.test.ts @@ -63,6 +63,17 @@ describe('what a shell config may point to', () => { test(`not ${what}`, () => assert.equal(trusts(PROBE, url), false)); } + test('on unpkg too, and at a dist-tag, the same', () => { + const unpkg = 'https://unpkg.com/'; + const probe = `${unpkg}devtools-anywhere@0.2.0/dist/probe.iife.js`; + assert.equal(trusts(probe, `${unpkg}@devtools-anywhere/devtools-frontend@0.2.0/dist/anywhere_app.html`), true); + assert.equal(trusts(probe, `${unpkg}evil@1.0.0/anywhere_app.html`), false); + assert.equal(trusts(probe, FRONTEND), false); + const latest = `${JSDELIVR}devtools-anywhere@latest/dist/probe.iife.js`; + assert.equal(trusts(latest, `${JSDELIVR}@devtools-anywhere/devtools-frontend@latest/dist/anywhere_app.html`), true); + assert.equal(trusts(latest, FRONTEND), false); + }); + test('on an npm CDN but not this package, nothing', () => { const other = `${JSDELIVR}evil@1.0.0/probe.iife.js`; assert.equal(trusts(other, `${JSDELIVR}evil@1.0.0/anywhere_app.html`), false); diff --git a/tests/e2e/cdn.test.ts b/tests/e2e/cdn.test.ts index 2a8c6f7..40c1016 100644 --- a/tests/e2e/cdn.test.ts +++ b/tests/e2e/cdn.test.ts @@ -51,14 +51,17 @@ async function serve(attributes = '', frontend: 'fake' | 'built' = 'fake') { const file = files.find(([prefix]) => at.startsWith(prefix)); if (!file) return void r.continue(); // Cross-origin, as from the CDN. - void fetch(file[1] + at.slice(file[0].length)).then(async response => - r.respond({ - status: response.status, - contentType: response.headers.get('content-type') ?? '', - headers: { 'access-control-allow-origin': '*' }, - body: Buffer.from(await response.arrayBuffer()), - }) - ); + void fetch(file[1] + at.slice(file[0].length)) + .then(async response => + r.respond({ + status: response.status, + contentType: response.headers.get('content-type') ?? '', + headers: { 'access-control-allow-origin': '*' }, + body: Buffer.from(await response.arrayBuffer()), + }) + ) + // A request that fails, rather than one never answered. + .catch(() => r.abort()); }); } @@ -93,17 +96,18 @@ test('a shell config pointing to another package on the CDN is removed and ignor `${CDN}devtools-anywhere@9.9.9/dist/devtools-anywhere.iife.js` ); await page.reload(); - await page.waitForFunction(() => window.__pageScriptRan === true); + // The probe ran: its own element, out of shell mode. + await page.waitForFunction(() => document.querySelector('devtools-anywhere')); assert.equal(await page.evaluate(() => !!document.querySelector('devtools-anywhere[data-shell]')), false); assert.equal(await page.evaluate(() => sessionStorage.getItem('devtools-anywhere:shell')), null); }); test('data-no-panel: no element, and no shell mode', async () => { - await serve('data-no-panel'); + // Only debugged remotely, at a server that is not there. + await serve('data-no-panel data-remote="http://127.0.0.1:9/"'); await page.goto(url); - await page.waitForFunction(() => window.__pageScriptRan === true && document.readyState === 'complete'); - // The bundle would have mounted the element after the load event. - await new Promise(r => setTimeout(r, 500)); + // The bundle has loaded, and would have mounted the element as it did. + await page.waitForFunction(() => 'DevtoolsAnywhere' in window); assert.equal(await page.evaluate(() => document.querySelectorAll('devtools-anywhere').length), 0); assert.equal(await page.evaluate(() => sessionStorage.getItem('devtools-anywhere:shell')), null); }); diff --git a/tests/e2e/device-mode.test.ts b/tests/e2e/device-mode.test.ts index 54c0929..62faea4 100644 --- a/tests/e2e/device-mode.test.ts +++ b/tests/e2e/device-mode.test.ts @@ -186,19 +186,23 @@ test('device mode sizes the page as the device, scaled to fit, and takes its scr saved.name = this.download; }; }); - await runAction('emulation.capture-screenshot'); - const saved = await waitFor( - () => - fe.evaluate(() => { - const value: { name?: string; width?: number; height?: number } = Reflect.get(window, '__saved'); - return value.name ? { name: value.name, width: value.width ?? 0, height: value.height ?? 0 } : null; - }), - 'the screenshot' - ); - await fe.evaluate(() => { - const restore: unknown = Reflect.get(window, '__restore'); - if (typeof restore === 'function') restore(); - }); + let saved: { name: string; width: number; height: number }; + try { + await runAction('emulation.capture-screenshot'); + saved = await waitFor( + () => + fe.evaluate(() => { + const value: { name?: string; width?: number; height?: number } = Reflect.get(window, '__saved'); + return value.name ? { name: value.name, width: value.width ?? 0, height: value.height ?? 0 } : null; + }), + 'the screenshot' + ); + } finally { + await fe.evaluate(() => { + const restore: unknown = Reflect.get(window, '__restore'); + if (typeof restore === 'function') restore(); + }); + } assert.match(saved.name, /\(iPhone SE\)\.png$/); assert.equal(saved.width, 375); near(saved.height, 667, 'screenshot height'); @@ -223,21 +227,24 @@ test('hidden, the page fills the shell at its own size; shown again, device mode test('undocked, device mode draws into its own page under the floating panel', { skip }, async () => { await dock('undocked'); - const toolbox = await waitFor(() => page.frames().find(f => f.url().includes('device_mode_emulation_frame.html'))); - // Its toolbar is there, and the page goes where it says. - // Another device than before, so that the page's size comes from this toolbar. - await waitFor(() => pickDevice(toolbox, 'iPhone 16').then(() => true), "the toolbox's toolbar"); - const where = await waitFor(async () => { - const [state, spot] = [await pageState(), await placeholder(toolbox).catch(() => null)]; - return spot && - state.viewport.width === 393 && - Math.abs(state.rect.left - spot.left) <= 2 && - Math.abs(state.rect.width - spot.width) <= 2 - ? { state, spot } - : null; - }, 'the page in the toolbox'); - near(where.state.rect.width, where.spot.width, 'width'); - await dock('bottom'); + try { + const toolbox = await waitFor(() => page.frames().find(f => f.url().includes('device_mode_emulation_frame.html'))); + // Its toolbar is there, and the page goes where it says. + // Another device than before, so that the page's size comes from this toolbar. + await waitFor(() => pickDevice(toolbox, 'iPhone 16').then(() => true), "the toolbox's toolbar"); + const where = await waitFor(async () => { + const [state, spot] = [await pageState(), await placeholder(toolbox).catch(() => null)]; + return spot && + state.viewport.width === 393 && + Math.abs(state.rect.left - spot.left) <= 2 && + Math.abs(state.rect.width - spot.width) <= 2 + ? { state, spot } + : null; + }, 'the page in the toolbox'); + near(where.state.rect.width, where.spot.width, 'width'); + } finally { + await dock('bottom'); + } await waitFor(async () => !page.frames().some(f => f.url().includes('device_mode_emulation_frame.html'))); await waitFor(async () => (await pageState()).rect.height < 800); }); @@ -246,16 +253,19 @@ test('docked to the right, the page goes where the frontend says', { skip }, asy await dock('right'); const fe = await frontend(); // The page left of DevTools, where the placeholder is. - await waitFor(async () => { - const [state, spot] = [await pageState(), await placeholder(fe)]; - return ( - state.viewport.width === 393 && - spot.left < 500 && - Math.abs(state.rect.left - spot.left) <= 2 && - Math.abs(state.rect.top - spot.top) <= 2 - ); - }, 'the page docked right'); - await dock('bottom'); + try { + await waitFor(async () => { + const [state, spot] = [await pageState(), await placeholder(fe)]; + return ( + state.viewport.width === 393 && + spot.left < 500 && + Math.abs(state.rect.left - spot.left) <= 2 && + Math.abs(state.rect.top - spot.top) <= 2 + ); + }, 'the page docked right'); + } finally { + await dock('bottom'); + } }); test("in device mode, the grip stays on DevTools' edge, not on the device's screen", { skip }, async () => { @@ -332,22 +342,25 @@ test('on a touch screen, a handle on the edge between the page and DevTools resi const { left, top, width, height } = el.getBoundingClientRect(); return { x: left + width / 2, y: top + height / 2 }; }); - const start = await waitFor(handle, 'the touch handle'); - await page.mouse.move(start.x, start.y); - await page.mouse.down(); - await page.mouse.move(start.x, start.y - 150, { steps: 5 }); - await page.mouse.up(); - // DevTools took the room (as much as its minimum sizes allow): the edge, and the handle on it, - // moved up. - await waitFor(async () => { - const now = await handle(); - return now && now.y < start.y - 100; - }, 'the handle to move up'); - await page.evaluate(() => { - const original: unknown = Reflect.get(window, '__matchMedia'); - if (original instanceof Function) window.matchMedia = query => Reflect.apply(original, window, [query]); - window.dispatchEvent(new Event('resize')); - }); + try { + const start = await waitFor(handle, 'the touch handle'); + await page.mouse.move(start.x, start.y); + await page.mouse.down(); + await page.mouse.move(start.x, start.y - 150, { steps: 5 }); + await page.mouse.up(); + // DevTools took the room (as much as its minimum sizes allow): the edge, and the handle on it, + // moved up. + await waitFor(async () => { + const now = await handle(); + return now && now.y < start.y - 100; + }, 'the handle to move up'); + } finally { + await page.evaluate(() => { + const original: unknown = Reflect.get(window, '__matchMedia'); + if (original instanceof Function) window.matchMedia = query => Reflect.apply(original, window, [query]); + window.dispatchEvent(new Event('resize')); + }); + } }); test("device mode's page is only one next to the frontend's", { skip }, async () => { From ccb73eb1495640a20d8e4c100d901533ee7a7bd5 Mon Sep 17 00:00:00 2001 From: jerrychan7 <czy-ken@163.com> Date: Mon, 2 Nov 2026 11:28:38 +0800 Subject: [PATCH 23/26] refactor(element): split element.ts into an element/ directory element.ts had grown to 1103 lines, past the size check's warning. Its parts that do not need the element's private state now live next to it: - style.ts: the shadow root's style sheet. - registry.ts: which element is which (global, the shell's, its F12 button in the shell's page, placed by the page) and the internal events between them; elements of this window's class are now known by a WeakSet filled in the constructor instead of instanceof, so the togglers need not import the class. - togglers.ts: elements with data-devtools-anywhere-toggle. - panel.ts: the panel's remembered sizes and position, and dragging the bar, the grip and the corner. - request.ts: the checks on requests from the frontend. element/index.ts keeps the element itself (690 lines). No change in behavior; DEV.md and comments point to the new path. --- DEV.md | 2 +- DEV.zh-CN.md | 2 +- .../src/{element.ts => element/index.ts} | 509 ++---------------- .../devtools-anywhere/src/element/panel.ts | 183 +++++++ .../devtools-anywhere/src/element/registry.ts | 49 ++ .../devtools-anywhere/src/element/request.ts | 29 + .../devtools-anywhere/src/element/style.ts | 94 ++++ .../devtools-anywhere/src/element/togglers.ts | 128 +++++ packages/devtools-anywhere/src/probe.ts | 2 +- .../devtools-anywhere/src/shell-messages.ts | 2 +- 10 files changed, 534 insertions(+), 466 deletions(-) rename packages/devtools-anywhere/src/{element.ts => element/index.ts} (56%) create mode 100644 packages/devtools-anywhere/src/element/panel.ts create mode 100644 packages/devtools-anywhere/src/element/registry.ts create mode 100644 packages/devtools-anywhere/src/element/request.ts create mode 100644 packages/devtools-anywhere/src/element/style.ts create mode 100644 packages/devtools-anywhere/src/element/togglers.ts diff --git a/DEV.md b/DEV.md index 2882cb2..dcf3f49 100644 --- a/DEV.md +++ b/DEV.md @@ -123,7 +123,7 @@ What two pieces of code must agree on is written once, where it is owned, and im |---|---|---| | Relay messages (hello, pagehide, detach, ping), hello's params, the replaced-page close code | `packages/relay-core/src/protocol.ts` | `remote.ts`, `connection.ts`, remote-server's `relay.ts` | | Frontend ↔ `<devtools-anywhere>`: requests, dock sides, hash parameters, our scripts' file names | `packages/devtools-anywhere/src/embedder.ts` | the element, the build; `anywhere_app` through a copy made by the frontend build | -| Shell mode's messages between the page and its Service Worker | `packages/devtools-anywhere/src/shell-messages.ts`, and the agent's `probe/messages.ts` | `element.ts`, `probe.ts`, `service-worker.ts`, the probe | +| Shell mode's messages between the page and its Service Worker | `packages/devtools-anywhere/src/shell-messages.ts`, and the agent's `probe/messages.ts` | `element/index.ts`, `probe.ts`, `service-worker.ts`, the probe | | `window.DevtoolsAnywhere`, the API the bundle exposes (`RemoteOptions` included) | `packages/devtools-anywhere/src/api.ts` | `iife.ts`, `probe.ts`, `tests/globals.d.ts` | | The probe script's `data-*` attributes | `packages/devtools-anywhere/src/probe-script.ts` (`ProbeScriptAttributes`, exported as `@public`) | `probe.ts`, the Vite plugin, remote-server | diff --git a/DEV.zh-CN.md b/DEV.zh-CN.md index bb4aa1b..9d61492 100644 --- a/DEV.zh-CN.md +++ b/DEV.zh-CN.md @@ -123,7 +123,7 @@ experiments/ 验证记录 |---|---|---| | 中继消息(hello、pagehide、detach、ping)、hello 的参数、页面被顶替时的关闭码 | `packages/relay-core/src/protocol.ts` | `remote.ts`、`connection.ts`、remote-server 的 `relay.ts` | | 前端 ↔ `<devtools-anywhere>`:请求、停靠位置、hash 参数、我们自己脚本的文件名 | `packages/devtools-anywhere/src/embedder.ts` | 元素、构建脚本;`anywhere_app` 通过前端构建时复制的副本使用 | -| 外壳模式下页面与 Service Worker 之间的消息 | `packages/devtools-anywhere/src/shell-messages.ts`,以及 agent 的 `probe/messages.ts` | `element.ts`、`probe.ts`、`service-worker.ts`、探针 | +| 外壳模式下页面与 Service Worker 之间的消息 | `packages/devtools-anywhere/src/shell-messages.ts`,以及 agent 的 `probe/messages.ts` | `element/index.ts`、`probe.ts`、`service-worker.ts`、探针 | | `window.DevtoolsAnywhere`,bundle 暴露的 API(含 `RemoteOptions`) | `packages/devtools-anywhere/src/api.ts` | `iife.ts`、`probe.ts`、`tests/globals.d.ts` | | 探针脚本的 `data-*` 属性 | `packages/devtools-anywhere/src/probe-script.ts`(`ProbeScriptAttributes`,以 `@public` 导出) | `probe.ts`、Vite 插件、remote-server | diff --git a/packages/devtools-anywhere/src/element.ts b/packages/devtools-anywhere/src/element/index.ts similarity index 56% rename from packages/devtools-anywhere/src/element.ts rename to packages/devtools-anywhere/src/element/index.ts index 91615e2..175ac04 100644 --- a/packages/devtools-anywhere/src/element.ts +++ b/packages/devtools-anywhere/src/element/index.ts @@ -3,7 +3,6 @@ import { SHELL_KEY, SHELL_PAGE_ID, createCdpSession, - isShellDocument, isShellPage, isTrusted, markOwn, @@ -15,24 +14,49 @@ import { storeShellOpen, type CdpSession, type SnapdomLoader, -} from './cdp-agent'; -import { connectFrontend, relayShell } from './connection'; -import { SHELL_HANDSHAKE } from './shell-messages'; -import { Grip } from './grip'; -import { ShellLayout } from './shell-layout'; -import type { GlobalElementConfig } from './api'; -import type { DevtoolsAnywhereAttributes, TogglerAttributes } from './public-types'; +} from '../cdp-agent'; +import { connectFrontend, relayShell } from '../connection'; +import { SHELL_HANDSHAKE } from '../shell-messages'; +import type { Grip } from '../grip'; +import { isDockSide, isEmbedderRequest } from './request'; +import { + createPanelGrip, + DOCK_KEY, + GRIP_EDGE, + readStored, + saveFloat, + setupCorner, + setupMover, + sizePanel, + store, +} from './panel'; +import { ShellLayout } from '../shell-layout'; +import { STYLE } from './style'; +import { + CONTROL_EVENT, + customElementsLoading, + globalConfigs, + globalElements, + globals, + inactiveGlobals, + isCustom, + isShellElement, + ownElements, + shellTogglers, + STATE_EVENT, +} from './registry'; +import { controlElement, installTogglers, refreshTogglers, shellElement } from './togglers'; +import type { GlobalElementConfig } from '../api'; +import type { DevtoolsAnywhereAttributes } from '../public-types'; import { - DOCK_SIDES, HIDDEN_NOTICE, PROBE_SCRIPT, SNAPDOM_SCRIPT, REQUEST_KEY, type DockSide, - type EmbedderRequest, type FrontendNotice, type FrontendHash, -} from './embedder'; +} from '../embedder'; let bundleUrl = ''; @@ -54,71 +78,6 @@ let loadSnapdom: SnapdomLoader = () => Promise.reject(new Error('snapDOM: the bu /** The CDP session of a page, with the screencast's renderer. */ export const createPageSession = () => createCdpSession({ loadSnapdom: () => loadSnapdom() }); -// Sizes are remembered as percentages of the viewport, so they fit any screen and orientation. -const HEIGHT_KEY = 'devtools-anywhere:height%'; -const WIDTH_KEY = 'devtools-anywhere:width%'; -const FLOAT_KEY = 'devtools-anywhere:float%'; -/** Viewport units that follow the browser's collapsing toolbars on mobile, where supported. */ -const VH = typeof CSS !== 'undefined' && CSS.supports('height', '1dvh') ? 'dvh' : 'vh'; -const VW = typeof CSS !== 'undefined' && CSS.supports('width', '1dvw') ? 'dvw' : 'vw'; -/** `part` as a percentage of `whole`, to one decimal. */ -const percent = (part: number, whole: number) => Math.round((part / whole) * 1000) / 10; -const DOCK_KEY = 'devtools-anywhere:dock'; -const MIN_SIZE = 80; -/** The panel's inner edge, next to the page, for each docked side. */ -const GRIP_EDGE = { bottom: 'top', right: 'left', left: 'right' } as const; - -/** - * localStorage, which throws when it is unavailable: Android WebViews without DOM storage, - * Safari blocking all cookies, sandboxed iframes. Sizes and dock side are only preferences. - */ -function readStored(key: string): string | null { - try { - return localStorage.getItem(key); - } catch { - return null; - } -} -function store(key: string, value: string) { - try { - localStorage.setItem(key, value); - } catch {} -} -/** A finite number from a stored value, else undefined. */ -const finite = (value: unknown) => (typeof value === 'number' && Number.isFinite(value) ? value : undefined); - -const isDockSide = (value: unknown): value is DockSide => DOCK_SIDES.some(side => side === value); - -/** - * Checks the fields of each type of request from the frontend, which is built separately: a type - * added to EmbedderRequest (embedder.ts) does not compile here until it is checked. - */ -const REQUEST_CHECKS: { [T in EmbedderRequest['type']]: (request: object) => boolean } = { - dock: request => 'side' in request && isDockSide(request.side), - close: () => true, - 'keep-open': request => 'keep' in request && typeof request.keep === 'boolean', - theme: request => ['toolbar', 'divider', 'icon'].every(key => typeof Reflect.get(request, key) === 'string'), - 'page-bounds': request => ['x', 'y', 'width', 'height'].every(key => finite(Reflect.get(request, key)) !== undefined), - split: request => ['x', 'y', 'width', 'height'].every(key => finite(Reflect.get(request, key)) !== undefined), - toolbox: request => 'url' in request && typeof request.url === 'string', -}; -const isRequestType = (type: unknown): type is EmbedderRequest['type'] => - typeof type === 'string' && Object.hasOwn(REQUEST_CHECKS, type); -const isEmbedderRequest = (value: unknown): value is EmbedderRequest => - typeof value === 'object' && - value !== null && - 'type' in value && - isRequestType(value.type) && - REQUEST_CHECKS[value.type](value); - -/** Position and size of the undocked panel, in percent of the viewport. */ -interface FloatRect { - left: number; - top: number; - width: number; - height: number; -} - /** * Whether the browser has the top layer for popovers. There the toggler and the panel are above * everything the page draws, whatever its z-index; elsewhere they fall back to the highest z-index. @@ -130,100 +89,6 @@ function setPopoverOpen(el: HTMLElement, open: boolean) { if (el.matches(':popover-open') !== open) el.togglePopover(open); } -const STYLE = ` -:host { all: initial; display: block; } -:host([hidden]) { display: none; } -/* Both go into the top layer as popovers (see TOP_LAYER): undo the browser's popover style. */ -.toggler, .panel { - inset: auto; width: auto; height: auto; margin: 0; padding: 0; border: 0; overflow: visible; color: inherit; -} -.toggler { - position: fixed; z-index: 2147483647; - right: calc(16px + env(safe-area-inset-right, 0px)); bottom: calc(16px + env(safe-area-inset-bottom, 0px)); - width: 44px; height: 44px; border: 0; border-radius: 50%; - background: #1a73e8; color: #fff; font: 600 13px/44px system-ui, sans-serif; - box-shadow: 0 2px 6px rgba(0, 0, 0, .3); cursor: pointer; touch-action: manipulation; -} -.toggler[hidden] { display: none; } -.panel { - position: fixed; left: 0; right: 0; bottom: 0; z-index: 2147483647; - display: none; flex-direction: column; box-sizing: border-box; - background: #fff; border-top: 1px solid var(--bar-border, #cacdd1); - /* Clear of the screen's cutouts and rounded corners (a page with viewport-fit=cover has them): - the home indicator below, and the notch on the sides in landscape. */ - padding: 0 env(safe-area-inset-right, 0px) env(safe-area-inset-bottom, 0px) env(safe-area-inset-left, 0px); -} -.panel[data-open] { display: flex; } -/* The undocked window's bar, which moves it; it takes the DevTools toolbar's colors, which the - frontend reports (theme request), as does the grip that resizes the docked panel (grip.ts). */ -.bar { - flex: none; position: relative; height: 16px; background: var(--bar-background, #f1f3f4); - cursor: move; touch-action: none; display: none; -} -[data-dock=undocked] .bar { display: block; } -.bar::before { - content: ''; position: absolute; left: 50%; top: 6px; width: 32px; height: 4px; - margin-left: -16px; border-radius: 2px; background: var(--bar-handle, #bdc1c6); -} -.bar button { - position: absolute; top: 0; width: 24px; height: 16px; padding: 0; border: 0; - background: none; color: var(--bar-icon, #5f6368); font: 14px/16px system-ui, sans-serif; cursor: pointer; -} -/* DevTools' own toolbar has a close button, except when undocked: then the bar has one. */ -.close { right: 4px; } -/* The undocked window's resize corner, for touch screens where CSS resize does not work. */ -.corner { - position: absolute; right: 0; bottom: 0; width: 16px; height: 16px; display: none; - cursor: nwse-resize; touch-action: none; -} -[data-dock=undocked] .corner { display: block; } -iframe { flex: 1; min-width: 0; min-height: 0; border: 0; } -.dragging iframe { pointer-events: none; } - -/* Docked to a side: full height, with the bar as a vertical strip on the inner edge. */ -.panel[data-dock=right], .panel[data-dock=left] { - top: 0; border-top: 0; padding-top: env(safe-area-inset-top, 0px); -} -/* No inset on the inner edge, which is in the middle of the screen. */ -.panel[data-dock=right] { - left: auto; flex-direction: row; border-left: 1px solid var(--bar-border, #cacdd1); padding-left: 0; -} -.panel[data-dock=left] { - right: auto; flex-direction: row-reverse; border-right: 1px solid var(--bar-border, #cacdd1); padding-right: 0; -} - -/* Undocked: a window over the page, moved by its bar and resized from its corner. */ -.panel[data-dock=undocked] { - right: auto; bottom: auto; border: 1px solid var(--bar-border, #cacdd1); border-radius: 6px; overflow: hidden; - box-shadow: 0 4px 16px rgba(0, 0, 0, .3); resize: both; padding: 0; -} - -/* Touch screens: targets large enough for a finger. */ -@media (pointer: coarse) { - .bar { height: 28px; } - .bar::before { top: 12px; } - .bar button { width: 44px; height: 28px; line-height: 28px; font-size: 18px; } - .corner { width: 32px; height: 32px; } -} - -/* An element placed by the page: a box in the page's flow, sized by its CSS, always loaded. */ -.panel[data-custom] { - position: static; display: flex; width: 100%; height: 100%; border: 0; padding: 0; -} -[data-custom] .bar, .toggler[data-custom] { display: none; } - -/* Shell mode: the panel takes its place next to the page iframe, unless undocked. */ -:host([data-shell]) { display: flex; flex: none; } -:host([data-shell]) .panel { position: static; flex: 1 1 auto; } -:host([data-shell]) .panel[data-dock=undocked] { position: fixed; } -/* Laid out by the frontend (shell-layout.ts): it fills the shell, the page iframe on top. */ -:host([data-laid-out]) { position: fixed; inset: 0; z-index: 0; } -:host([data-laid-out]) .panel { - position: absolute; inset: 0; width: auto !important; height: auto !important; border: 0; -} -:host([data-laid-out]) .bar { display: none; } -`; - /** * `<devtools-anywhere devtools-frontend-url="…/anywhere_app.html">` * @@ -282,6 +147,7 @@ export class DevtoolsAnywhereElement extends HTMLElement { constructor() { super(); + ownElements.add(this); const root = this.attachShadow({ mode: 'open' }); root.innerHTML = `<style>${STYLE}</style> <button class="toggler" part="toggler" popover="manual" aria-label="Open DevTools">F12</button> @@ -308,17 +174,18 @@ export class DevtoolsAnywhereElement extends HTMLElement { }); part('.close').addEventListener('click', () => this.setOpen(false)); this.addEventListener(CONTROL_EVENT, this.onControl); - this.setupMover(part<HTMLElement>('.bar')); - this.grip = this.createGrip(); + const relayout = () => this.layout(); + setupMover(part<HTMLElement>('.bar'), this.panel, relayout); + this.grip = createPanelGrip(this.panel, () => this.dock, relayout); this.panel.append(this.grip.element); - this.setupCorner(part<HTMLElement>('.corner')); + setupCorner(part<HTMLElement>('.corner'), this.panel, relayout); // The undocked panel is resized by the browser (CSS resize): remember its size once it // settles, not on every frame of the drag. let saveTimer: ReturnType<typeof setTimeout> | undefined; new ResizeObserver(() => { clearTimeout(saveTimer); saveTimer = setTimeout(() => { - if (this.dock === 'undocked' && this.isOpen && this.panel.offsetWidth) this.saveFloat(); + if (this.dock === 'undocked' && this.isOpen && this.panel.offsetWidth) saveFloat(this.panel); }, 300); }).observe(this.panel); } @@ -490,7 +357,7 @@ export class DevtoolsAnywhereElement extends HTMLElement { return ( this.getAttribute('devtools-frontend-url') || this.config.frontend || - probeFrontend || + globals.probeFrontend || (isShellPage() && readShellConfig()?.frontend) || '' ); @@ -625,14 +492,14 @@ export class DevtoolsAnywhereElement extends HTMLElement { if (this.custom) return this.connectCustom(); // Only the first global element connected works, even once it is gone. if (globalElements.has(this) && !this.firstGlobal) { - if (globalTaken) { + if (globals.taken) { if (!inactiveGlobals.has(this)) console.warn('devtools-anywhere: the page has a global <devtools-anywhere> already'); inactiveGlobals.add(this); this.style.setProperty('display', 'none', 'important'); return; } - globalTaken = this.firstGlobal = true; + globals.taken = this.firstGlobal = true; } // The options, once: the probe's, or in the shell the shell config's, kept for its reloads. if (this.openState === null) { @@ -729,22 +596,8 @@ export class DevtoolsAnywhereElement extends HTMLElement { /** Sizes the panel for its dock side; in shell mode, also lays out the shell around it. */ private layout() { - const style = this.panel.style; - style.width = style.height = style.left = style.top = ''; const dock = this.dock; - if (dock === 'bottom') { - const stored = Number(readStored(HEIGHT_KEY)); - style.height = stored ? `${stored}${VH}` : `50${VH}`; - } else if (dock === 'undocked') { - const rect = this.floatRect(); - style.left = `${rect.left}${VW}`; - style.top = `${rect.top}${VH}`; - style.width = `${rect.width}${VW}`; - style.height = `${rect.height}${VH}`; - } else { - const stored = Number(readStored(WIDTH_KEY)); - style.width = stored ? `${stored}${VW}` : `50${VW}`; - } + sizePanel(this.panel, dock); // Docked, the grip on the panel's inner edge resizes it; in the shell, the shell's grip does. if (dock !== 'undocked') this.grip.place(GRIP_EDGE[dock], 'outside'); this.grip.show(dock !== 'undocked' && !isShellElement(this) && !this.panel.hasAttribute('data-custom')); @@ -755,38 +608,6 @@ export class DevtoolsAnywhereElement extends HTMLElement { } } - /** The undocked panel's last position and size, kept inside the viewport. */ - private floatRect(): FloatRect { - let stored: unknown = null; - try { - stored = JSON.parse(readStored(FLOAT_KEY) || 'null'); - } catch {} - // Written by an older version or by the page itself: only finite numbers are used. - const field = (key: keyof FloatRect) => - typeof stored === 'object' && stored ? finite(Reflect.get(stored, key)) : undefined; - const width = Math.min(field('width') || 80, 100); - const height = Math.min(field('height') || 60, 100); - const clamp = (value: number | undefined, fallback: number, max: number) => - Math.min(Math.max(value ?? fallback, 0), max); - return { - left: clamp(field('left'), (100 - width) / 2, 100 - width), - top: clamp(field('top'), (100 - height) / 2, 100 - height), - width, - height, - }; - } - - private saveFloat() { - const { left, top, width, height } = this.panel.getBoundingClientRect(); - const rect: FloatRect = { - left: percent(left, innerWidth), - top: percent(top, innerHeight), - width: percent(width, innerWidth), - height: percent(height, innerHeight), - }; - store(FLOAT_KEY, JSON.stringify(rect)); - } - private loadFrontend() { const frontend = this.frontendUrl; const pageElement = document.getElementById(SHELL_PAGE_ID); @@ -817,112 +638,16 @@ export class DevtoolsAnywhereElement extends HTMLElement { } satisfies FrontendHash); this.frame.src = `${url.href.split('#')[0]}#?${hash}`; } - - /** Dragging the bar moves the undocked panel. */ - private setupMover(bar: HTMLElement) { - bar.addEventListener('pointerdown', down => { - if ((down.target as HTMLElement).closest('button')) return; - down.preventDefault(); - bar.setPointerCapture(down.pointerId); - this.panel.classList.add('dragging'); - const start = this.panel.getBoundingClientRect(); - const move = (e: PointerEvent) => { - const left = start.left + e.clientX - down.clientX; - const top = start.top + e.clientY - down.clientY; - // The whole window stays in the viewport, so its resize corner stays reachable; a - // window larger than the viewport keeps its top left corner in view. - this.panel.style.left = `${Math.max(Math.min(left, innerWidth - start.width), 0)}px`; - this.panel.style.top = `${Math.max(Math.min(top, innerHeight - start.height), 0)}px`; - }; - const up = () => { - this.panel.classList.remove('dragging'); - this.saveFloat(); - // From the pixels of the drag to the remembered percentages. - this.layout(); - bar.removeEventListener('pointermove', move); - bar.removeEventListener('pointerup', up); - bar.removeEventListener('pointercancel', up); - }; - bar.addEventListener('pointermove', move); - bar.addEventListener('pointerup', up); - bar.addEventListener('pointercancel', up); - }); - } - - /** The grip on the docked panel's inner edge, on the page's side: dragging it resizes the panel. */ - private createGrip() { - const clampSize = (size: number, max: number) => Math.min(Math.max(size, MIN_SIZE), max - 16); - return new Grip({ - edge: 'top', - side: 'outside', - title: 'Drag to resize', - onStart: () => this.panel.classList.add('dragging'), - onMove: e => { - const style = this.panel.style; - if (this.dock === 'bottom') style.height = `${clampSize(innerHeight - e.clientY, innerHeight)}px`; - else if (this.dock === 'right') style.width = `${clampSize(innerWidth - e.clientX, innerWidth)}px`; - else if (this.dock === 'left') style.width = `${clampSize(e.clientX, innerWidth)}px`; - }, - onEnd: () => { - this.panel.classList.remove('dragging'); - if (this.dock === 'bottom') store(HEIGHT_KEY, String(percent(this.panel.offsetHeight, innerHeight))); - else store(WIDTH_KEY, String(percent(this.panel.offsetWidth, innerWidth))); - // From the pixels of the drag to the remembered percentages. - this.layout(); - }, - }); - } - - /** Dragging the corner resizes the undocked panel, within the viewport. */ - private setupCorner(corner: HTMLElement) { - corner.addEventListener('pointerdown', down => { - down.preventDefault(); - corner.setPointerCapture(down.pointerId); - this.panel.classList.add('dragging'); - const start = this.panel.getBoundingClientRect(); - const move = (e: PointerEvent) => { - const width = start.width + e.clientX - down.clientX; - const height = start.height + e.clientY - down.clientY; - this.panel.style.width = `${Math.min(Math.max(width, MIN_SIZE), innerWidth - start.left)}px`; - this.panel.style.height = `${Math.min(Math.max(height, MIN_SIZE), innerHeight - start.top)}px`; - }; - const up = () => { - this.panel.classList.remove('dragging'); - this.saveFloat(); - this.layout(); - corner.removeEventListener('pointermove', move); - corner.removeEventListener('pointerup', up); - corner.removeEventListener('pointercancel', up); - }; - corner.addEventListener('pointermove', move); - corner.addEventListener('pointerup', up); - corner.addEventListener('pointercancel', up); - }); - } } -/** - * The global elements, created for the probe (createGlobalElement), whether one has connected, and - * those connected after it, which do nothing. - */ -const globalElements = new WeakSet<Element>(); -let globalTaken = false; -const inactiveGlobals = new WeakSet<Element>(); -/** The probe's frontend URL, for the elements placed by the page without one. */ -let probeFrontend = ''; -/** The connected elements placed by the page, each with what loads its frontend. */ -const customElementsLoading = new Map<Element, () => void>(); -/** The probe's options for each global element, taken when it first connects. */ -const globalConfigs = new WeakMap<Element, GlobalElementConfig>(); - /** Creates a global element with the probe's options: only the first one connected works. @internal */ export function createGlobalElement(config: GlobalElementConfig): DevtoolsAnywhereElement | null { try { const element = new DevtoolsAnywhereElement(); globalElements.add(element); globalConfigs.set(element, config); - if (config.frontend && !probeFrontend) { - probeFrontend = config.frontend; + if (config.frontend && !globals.probeFrontend) { + globals.probeFrontend = config.frontend; customElementsLoading.forEach(load => load()); } return element; @@ -933,9 +658,6 @@ export function createGlobalElement(config: GlobalElementConfig): DevtoolsAnywhe } } -/** The F12 buttons of the shell's element, in the shell's page (mountShellToggler). */ -const shellTogglers = new WeakSet<Element>(); - /** * In the shell's page iframe: mounts the shell element's F12 button in the page, under <html> like * the global element, so that the page's `::part(toggler)` styles reach it. The shell config @@ -956,143 +678,6 @@ export function mountShellToggler() { else mount(); } -/** What a toggler fires on an element placed by the page, which has no open state. */ -const TOGGLE_REQUEST_EVENT = 'devtools-toggle-request'; -/** Whether `el` is an element placed by the page (of this window). */ -const isCustom = (el: Element) => - el instanceof DevtoolsAnywhereElement && !globalElements.has(el) && !shellTogglers.has(el) && !isShellElement(el); -/** - * Whether `el` is the shell's element: one in the shell document the probe wrote (not created - * for the probe), maybe of another window. Its document is marked as the shell's when the probe writes the - * shell; a `shell` attribute no longer makes an element the shell's. - */ -const isShellElement = (el: Element) => !globalElements.has(el) && isShellDocument(el.ownerDocument); - -/** - * Fired on the global element when it opens or closes, for the togglers of the shell's page: an - * internal convention, not part of the API, which has only their aria-expanded. A page of the - * same origin can fire it too: it is not meant to be trusted. - */ -const STATE_EVENT = 'devtools-anywhere-state'; - -/** - * The event a toggler controls its element with (see controlElement): an internal convention, not - * part of the API. A page of the same origin can fire it too: it is not meant to be trusted. - */ -const CONTROL_EVENT = 'devtools-anywhere-control'; - -/** Elements with this attribute open and close DevTools when clicked (see togglerTarget). */ -const TOGGLE_ATTRIBUTE: keyof TogglerAttributes = 'data-devtools-anywhere-toggle'; -/** The tag the togglers look for, once they are installed. */ -let togglerTag = ''; -/** Toggler values already warned about. */ -const warned = new Set<string>(); - -/** - * The element a toggler controls: the element whose id is the attribute's value, or without one - * the first global element, or in the shell's page iframe the shell's element. - */ -function togglerTarget(toggler: Element, warn: boolean): Element | null { - const id = toggler.getAttribute(TOGGLE_ATTRIBUTE) ?? ''; - const doc = toggler.ownerDocument; - let target: Element | null | undefined = null; - if (isShellPage()) { - // An element the page placed in the shell's page: the toggler names it. Without a value, the - // shell's element, of the shell window's class: it is toggled through an event. - const named = id ? doc.getElementById(id) : null; - if (named && customElementsLoading.has(named)) return named; - if (!id) return shellElement(); - } else { - // Without a value, the global element (or the shell's); global elements after the first do - // nothing: they are not targets. - target = id - ? doc.getElementById(id) - : [...doc.querySelectorAll(togglerTag)].find( - el => (globalElements.has(el) || isShellElement(el)) && !inactiveGlobals.has(el) - ); - } - if (target instanceof DevtoolsAnywhereElement && !inactiveGlobals.has(target)) return target; - if (warn && !warned.has(id)) { - warned.add(id); - console.warn(`devtools-anywhere: no <${togglerTag}>${id ? ` with id "${id}"` : ''} for ${TOGGLE_ATTRIBUTE}`); - } - return null; -} - -/** In the shell's page iframe: the shell's element, of the shell window's class. */ -function shellElement(): Element | null { - const shellDocument = window.frameElement?.ownerDocument; - return (shellDocument && [...shellDocument.querySelectorAll(togglerTag)].find(isShellElement)) ?? null; -} - -/** - * Asks a toggler's element, maybe of another window, whether it is open, toggling it first when - * `toggle`. Undefined when it does not answer. - */ -function controlElement(target: Element, toggle: boolean): boolean | undefined { - const detail: { toggle: boolean; open?: boolean } = { toggle }; - target.dispatchEvent(new CustomEvent(CONTROL_EVENT, { detail })); - return detail.open; -} - -/** Sets aria-expanded on a toggler from the state of its element. */ -function refreshToggler(toggler: Element, warn = false) { - const target = togglerTarget(toggler, warn); - // An element placed by the page has no open state. - if (target && isCustom(target)) return target; - const open = target && controlElement(target, false); - if (open !== undefined && open !== null) toggler.setAttribute('aria-expanded', String(open)); - return target; -} - -function refreshTogglers(doc: Document) { - if (!togglerTag) return; - for (const toggler of doc.querySelectorAll(`[${TOGGLE_ATTRIBUTE}]`)) refreshToggler(toggler); -} - -/** Makes any element with TOGGLE_ATTRIBUTE, including those added later, a toggler. */ -function installTogglers(tag: string) { - if (togglerTag) return; - togglerTag = tag; - document.addEventListener( - 'click', - event => { - const clicked = event.target instanceof Element ? event.target : null; - const toggler = clicked?.closest(`[${TOGGLE_ATTRIBUTE}]`); - // A label's click is followed by one on its control: that one toggles, once. - const control = clicked?.closest('label')?.control; - if (!toggler || (control && control !== clicked && toggler.contains(control))) return; - const target = toggler && refreshToggler(toggler, true); - if (!target) return; - // An element placed by the page only tells its listeners, which decide. - if (isCustom(target)) target.dispatchEvent(new Event(TOGGLE_REQUEST_EVENT)); - else controlElement(target, true); - refreshToggler(toggler); - }, - true - ); - const refresh = () => refreshTogglers(document); - if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', refresh); - else refresh(); - // In the shell's page iframe, the togglers follow the shell's element while this page is shown. - if (!isShellPage()) return; - const shellDocument = window.frameElement?.ownerDocument; - let shell: Element | null = null; - const listen = () => { - shell = togglerTarget(document.documentElement, false); - shell?.addEventListener(STATE_EVENT, refresh); - refresh(); - }; - // The shell parses its element after its bundle, which may load after this page has run. - if (shellDocument?.readyState === 'loading') shellDocument.addEventListener('DOMContentLoaded', listen); - else listen(); - window.addEventListener('pagehide', () => { - shellDocument?.removeEventListener('DOMContentLoaded', listen); - shell?.removeEventListener(STATE_EVENT, refresh); - }); - window.addEventListener('pageshow', event => event.persisted && listen()); -} - /** * Registers the element under `tag` unless something is already registered there, and makes * elements with `data-devtools-anywhere-toggle` open and close it. @internal diff --git a/packages/devtools-anywhere/src/element/panel.ts b/packages/devtools-anywhere/src/element/panel.ts new file mode 100644 index 0000000..47170a4 --- /dev/null +++ b/packages/devtools-anywhere/src/element/panel.ts @@ -0,0 +1,183 @@ +// The panel's size and place, the user's: docked, its size along the page; undocked, its +// position and size, dragged by the bar and the corner. Remembered in localStorage as percentages +// of the viewport, so they fit any screen and orientation. +import { Grip } from '../grip'; +import { finite } from './request'; +import type { DockSide } from '../embedder'; + +const HEIGHT_KEY = 'devtools-anywhere:height%'; +const WIDTH_KEY = 'devtools-anywhere:width%'; +const FLOAT_KEY = 'devtools-anywhere:float%'; +/** Viewport units that follow the browser's collapsing toolbars on mobile, where supported. */ +const VH = typeof CSS !== 'undefined' && CSS.supports('height', '1dvh') ? 'dvh' : 'vh'; +const VW = typeof CSS !== 'undefined' && CSS.supports('width', '1dvw') ? 'dvw' : 'vw'; +/** `part` as a percentage of `whole`, to one decimal. */ +const percent = (part: number, whole: number) => Math.round((part / whole) * 1000) / 10; +export const DOCK_KEY = 'devtools-anywhere:dock'; +const MIN_SIZE = 80; +/** The panel's inner edge, next to the page, for each docked side. */ +export const GRIP_EDGE = { bottom: 'top', right: 'left', left: 'right' } as const; + +/** + * localStorage, which throws when it is unavailable: Android WebViews without DOM storage, + * Safari blocking all cookies, sandboxed iframes. Sizes and dock side are only preferences. + */ +export function readStored(key: string): string | null { + try { + return localStorage.getItem(key); + } catch { + return null; + } +} +export function store(key: string, value: string) { + try { + localStorage.setItem(key, value); + } catch {} +} + +/** Position and size of the undocked panel, in percent of the viewport. */ +interface FloatRect { + left: number; + top: number; + width: number; + height: number; +} + +/** Sizes the panel for its dock side, from what is remembered. */ +export function sizePanel(panel: HTMLElement, dock: DockSide) { + const style = panel.style; + style.width = style.height = style.left = style.top = ''; + if (dock === 'bottom') { + const stored = Number(readStored(HEIGHT_KEY)); + style.height = stored ? `${stored}${VH}` : `50${VH}`; + } else if (dock === 'undocked') { + const rect = floatRect(); + style.left = `${rect.left}${VW}`; + style.top = `${rect.top}${VH}`; + style.width = `${rect.width}${VW}`; + style.height = `${rect.height}${VH}`; + } else { + const stored = Number(readStored(WIDTH_KEY)); + style.width = stored ? `${stored}${VW}` : `50${VW}`; + } +} + +/** The undocked panel's last position and size, kept inside the viewport. */ +function floatRect(): FloatRect { + let stored: unknown = null; + try { + stored = JSON.parse(readStored(FLOAT_KEY) || 'null'); + } catch {} + // Written by an older version or by the page itself: only finite numbers are used. + const field = (key: keyof FloatRect) => + typeof stored === 'object' && stored ? finite(Reflect.get(stored, key)) : undefined; + const width = Math.min(field('width') || 80, 100); + const height = Math.min(field('height') || 60, 100); + const clamp = (value: number | undefined, fallback: number, max: number) => + Math.min(Math.max(value ?? fallback, 0), max); + return { + left: clamp(field('left'), (100 - width) / 2, 100 - width), + top: clamp(field('top'), (100 - height) / 2, 100 - height), + width, + height, + }; +} + +/** Remembers the undocked panel's position and size. */ +export function saveFloat(panel: HTMLElement) { + const { left, top, width, height } = panel.getBoundingClientRect(); + const rect: FloatRect = { + left: percent(left, innerWidth), + top: percent(top, innerHeight), + width: percent(width, innerWidth), + height: percent(height, innerHeight), + }; + store(FLOAT_KEY, JSON.stringify(rect)); +} + +/** + * Dragging the bar moves the undocked panel; `relayout` then goes from the pixels of the drag to + * the remembered percentages. + */ +export function setupMover(bar: HTMLElement, panel: HTMLElement, relayout: () => void) { + bar.addEventListener('pointerdown', down => { + if ((down.target as HTMLElement).closest('button')) return; + down.preventDefault(); + bar.setPointerCapture(down.pointerId); + panel.classList.add('dragging'); + const start = panel.getBoundingClientRect(); + const move = (e: PointerEvent) => { + const left = start.left + e.clientX - down.clientX; + const top = start.top + e.clientY - down.clientY; + // The whole window stays in the viewport, so its resize corner stays reachable; a + // window larger than the viewport keeps its top left corner in view. + panel.style.left = `${Math.max(Math.min(left, innerWidth - start.width), 0)}px`; + panel.style.top = `${Math.max(Math.min(top, innerHeight - start.height), 0)}px`; + }; + const up = () => { + panel.classList.remove('dragging'); + saveFloat(panel); + relayout(); + bar.removeEventListener('pointermove', move); + bar.removeEventListener('pointerup', up); + bar.removeEventListener('pointercancel', up); + }; + bar.addEventListener('pointermove', move); + bar.addEventListener('pointerup', up); + bar.addEventListener('pointercancel', up); + }); +} + +/** + * The grip on the docked panel's inner edge, on the page's side: dragging it resizes the panel. + * `relayout` then goes from the pixels of the drag to the remembered percentages. + */ +export function createPanelGrip(panel: HTMLElement, dock: () => DockSide, relayout: () => void) { + const clampSize = (size: number, max: number) => Math.min(Math.max(size, MIN_SIZE), max - 16); + return new Grip({ + edge: 'top', + side: 'outside', + title: 'Drag to resize', + onStart: () => panel.classList.add('dragging'), + onMove: e => { + const style = panel.style; + const side = dock(); + if (side === 'bottom') style.height = `${clampSize(innerHeight - e.clientY, innerHeight)}px`; + else if (side === 'right') style.width = `${clampSize(innerWidth - e.clientX, innerWidth)}px`; + else if (side === 'left') style.width = `${clampSize(e.clientX, innerWidth)}px`; + }, + onEnd: () => { + panel.classList.remove('dragging'); + if (dock() === 'bottom') store(HEIGHT_KEY, String(percent(panel.offsetHeight, innerHeight))); + else store(WIDTH_KEY, String(percent(panel.offsetWidth, innerWidth))); + relayout(); + }, + }); +} + +/** Dragging the corner resizes the undocked panel, within the viewport. */ +export function setupCorner(corner: HTMLElement, panel: HTMLElement, relayout: () => void) { + corner.addEventListener('pointerdown', down => { + down.preventDefault(); + corner.setPointerCapture(down.pointerId); + panel.classList.add('dragging'); + const start = panel.getBoundingClientRect(); + const move = (e: PointerEvent) => { + const width = start.width + e.clientX - down.clientX; + const height = start.height + e.clientY - down.clientY; + panel.style.width = `${Math.min(Math.max(width, MIN_SIZE), innerWidth - start.left)}px`; + panel.style.height = `${Math.min(Math.max(height, MIN_SIZE), innerHeight - start.top)}px`; + }; + const up = () => { + panel.classList.remove('dragging'); + saveFloat(panel); + relayout(); + corner.removeEventListener('pointermove', move); + corner.removeEventListener('pointerup', up); + corner.removeEventListener('pointercancel', up); + }; + corner.addEventListener('pointermove', move); + corner.addEventListener('pointerup', up); + corner.addEventListener('pointercancel', up); + }); +} diff --git a/packages/devtools-anywhere/src/element/registry.ts b/packages/devtools-anywhere/src/element/registry.ts new file mode 100644 index 0000000..6717985 --- /dev/null +++ b/packages/devtools-anywhere/src/element/registry.ts @@ -0,0 +1,49 @@ +// Which `<devtools-anywhere>` is which: the probe's (global), the shell's, its F12 button in the +// shell's page, or one placed by the page; and the events between them and their togglers. +import { isShellDocument } from '../cdp-agent'; +import type { GlobalElementConfig } from '../api'; + +/** Every element of this window's class, whatever tag it is registered under. */ +export const ownElements = new WeakSet<Element>(); +/** + * The global elements, created for the probe (createGlobalElement), whether one has connected, and + * those connected after it, which do nothing. + */ +export const globalElements = new WeakSet<Element>(); +export const inactiveGlobals = new WeakSet<Element>(); +/** The connected elements placed by the page, each with what loads its frontend. */ +export const customElementsLoading = new Map<Element, () => void>(); +/** The probe's options for each global element, taken when it first connects. */ +export const globalConfigs = new WeakMap<Element, GlobalElementConfig>(); + +/** + * Whether a global element has connected; and the probe's frontend URL, for the elements placed by + * the page without one. + */ +export const globals = { taken: false, probeFrontend: '' }; + +/** The F12 buttons of the shell's element, in the shell's page (mountShellToggler). */ +export const shellTogglers = new WeakSet<Element>(); + +/** Whether `el` is an element placed by the page (of this window). */ +export const isCustom = (el: Element) => + ownElements.has(el) && !globalElements.has(el) && !shellTogglers.has(el) && !isShellElement(el); +/** + * Whether `el` is the shell's element: one in the shell document the probe wrote (not created + * for the probe), maybe of another window. Its document is marked as the shell's when the probe writes the + * shell; a `shell` attribute no longer makes an element the shell's. + */ +export const isShellElement = (el: Element) => !globalElements.has(el) && isShellDocument(el.ownerDocument); + +/** + * Fired on the global element when it opens or closes, for the togglers of the shell's page: an + * internal convention, not part of the API, which has only their aria-expanded. A page of the + * same origin can fire it too: it is not meant to be trusted. + */ +export const STATE_EVENT = 'devtools-anywhere-state'; + +/** + * The event a toggler controls its element with (see controlElement): an internal convention, not + * part of the API. A page of the same origin can fire it too: it is not meant to be trusted. + */ +export const CONTROL_EVENT = 'devtools-anywhere-control'; diff --git a/packages/devtools-anywhere/src/element/request.ts b/packages/devtools-anywhere/src/element/request.ts new file mode 100644 index 0000000..b140454 --- /dev/null +++ b/packages/devtools-anywhere/src/element/request.ts @@ -0,0 +1,29 @@ +// Requests from the frontend (embedder.ts), checked: it is built separately, and any window can post. +import { DOCK_SIDES, type DockSide, type EmbedderRequest } from '../embedder'; + +/** A finite number from a stored value, else undefined. */ +export const finite = (value: unknown) => (typeof value === 'number' && Number.isFinite(value) ? value : undefined); + +export const isDockSide = (value: unknown): value is DockSide => DOCK_SIDES.some(side => side === value); + +/** + * Checks the fields of each type of request from the frontend, which is built separately: a type + * added to EmbedderRequest (embedder.ts) does not compile here until it is checked. + */ +const REQUEST_CHECKS: { [T in EmbedderRequest['type']]: (request: object) => boolean } = { + dock: request => 'side' in request && isDockSide(request.side), + close: () => true, + 'keep-open': request => 'keep' in request && typeof request.keep === 'boolean', + theme: request => ['toolbar', 'divider', 'icon'].every(key => typeof Reflect.get(request, key) === 'string'), + 'page-bounds': request => ['x', 'y', 'width', 'height'].every(key => finite(Reflect.get(request, key)) !== undefined), + split: request => ['x', 'y', 'width', 'height'].every(key => finite(Reflect.get(request, key)) !== undefined), + toolbox: request => 'url' in request && typeof request.url === 'string', +}; +const isRequestType = (type: unknown): type is EmbedderRequest['type'] => + typeof type === 'string' && Object.hasOwn(REQUEST_CHECKS, type); +export const isEmbedderRequest = (value: unknown): value is EmbedderRequest => + typeof value === 'object' && + value !== null && + 'type' in value && + isRequestType(value.type) && + REQUEST_CHECKS[value.type](value); diff --git a/packages/devtools-anywhere/src/element/style.ts b/packages/devtools-anywhere/src/element/style.ts new file mode 100644 index 0000000..49a8651 --- /dev/null +++ b/packages/devtools-anywhere/src/element/style.ts @@ -0,0 +1,94 @@ +/** The shadow root style of `<devtools-anywhere>`. */ +export const STYLE = ` +:host { all: initial; display: block; } +:host([hidden]) { display: none; } +/* Both go into the top layer as popovers (see TOP_LAYER): undo the browser's popover style. */ +.toggler, .panel { + inset: auto; width: auto; height: auto; margin: 0; padding: 0; border: 0; overflow: visible; color: inherit; +} +.toggler { + position: fixed; z-index: 2147483647; + right: calc(16px + env(safe-area-inset-right, 0px)); bottom: calc(16px + env(safe-area-inset-bottom, 0px)); + width: 44px; height: 44px; border: 0; border-radius: 50%; + background: #1a73e8; color: #fff; font: 600 13px/44px system-ui, sans-serif; + box-shadow: 0 2px 6px rgba(0, 0, 0, .3); cursor: pointer; touch-action: manipulation; +} +.toggler[hidden] { display: none; } +.panel { + position: fixed; left: 0; right: 0; bottom: 0; z-index: 2147483647; + display: none; flex-direction: column; box-sizing: border-box; + background: #fff; border-top: 1px solid var(--bar-border, #cacdd1); + /* Clear of the screen's cutouts and rounded corners (a page with viewport-fit=cover has them): + the home indicator below, and the notch on the sides in landscape. */ + padding: 0 env(safe-area-inset-right, 0px) env(safe-area-inset-bottom, 0px) env(safe-area-inset-left, 0px); +} +.panel[data-open] { display: flex; } +/* The undocked window's bar, which moves it; it takes the DevTools toolbar's colors, which the + frontend reports (theme request), as does the grip that resizes the docked panel (grip.ts). */ +.bar { + flex: none; position: relative; height: 16px; background: var(--bar-background, #f1f3f4); + cursor: move; touch-action: none; display: none; +} +[data-dock=undocked] .bar { display: block; } +.bar::before { + content: ''; position: absolute; left: 50%; top: 6px; width: 32px; height: 4px; + margin-left: -16px; border-radius: 2px; background: var(--bar-handle, #bdc1c6); +} +.bar button { + position: absolute; top: 0; width: 24px; height: 16px; padding: 0; border: 0; + background: none; color: var(--bar-icon, #5f6368); font: 14px/16px system-ui, sans-serif; cursor: pointer; +} +/* DevTools' own toolbar has a close button, except when undocked: then the bar has one. */ +.close { right: 4px; } +/* The undocked window's resize corner, for touch screens where CSS resize does not work. */ +.corner { + position: absolute; right: 0; bottom: 0; width: 16px; height: 16px; display: none; + cursor: nwse-resize; touch-action: none; +} +[data-dock=undocked] .corner { display: block; } +iframe { flex: 1; min-width: 0; min-height: 0; border: 0; } +.dragging iframe { pointer-events: none; } + +/* Docked to a side: full height, with the bar as a vertical strip on the inner edge. */ +.panel[data-dock=right], .panel[data-dock=left] { + top: 0; border-top: 0; padding-top: env(safe-area-inset-top, 0px); +} +/* No inset on the inner edge, which is in the middle of the screen. */ +.panel[data-dock=right] { + left: auto; flex-direction: row; border-left: 1px solid var(--bar-border, #cacdd1); padding-left: 0; +} +.panel[data-dock=left] { + right: auto; flex-direction: row-reverse; border-right: 1px solid var(--bar-border, #cacdd1); padding-right: 0; +} + +/* Undocked: a window over the page, moved by its bar and resized from its corner. */ +.panel[data-dock=undocked] { + right: auto; bottom: auto; border: 1px solid var(--bar-border, #cacdd1); border-radius: 6px; overflow: hidden; + box-shadow: 0 4px 16px rgba(0, 0, 0, .3); resize: both; padding: 0; +} + +/* Touch screens: targets large enough for a finger. */ +@media (pointer: coarse) { + .bar { height: 28px; } + .bar::before { top: 12px; } + .bar button { width: 44px; height: 28px; line-height: 28px; font-size: 18px; } + .corner { width: 32px; height: 32px; } +} + +/* An element placed by the page: a box in the page's flow, sized by its CSS, always loaded. */ +.panel[data-custom] { + position: static; display: flex; width: 100%; height: 100%; border: 0; padding: 0; +} +[data-custom] .bar, .toggler[data-custom] { display: none; } + +/* Shell mode: the panel takes its place next to the page iframe, unless undocked. */ +:host([data-shell]) { display: flex; flex: none; } +:host([data-shell]) .panel { position: static; flex: 1 1 auto; } +:host([data-shell]) .panel[data-dock=undocked] { position: fixed; } +/* Laid out by the frontend (shell-layout.ts): it fills the shell, the page iframe on top. */ +:host([data-laid-out]) { position: fixed; inset: 0; z-index: 0; } +:host([data-laid-out]) .panel { + position: absolute; inset: 0; width: auto !important; height: auto !important; border: 0; +} +:host([data-laid-out]) .bar { display: none; } +`; diff --git a/packages/devtools-anywhere/src/element/togglers.ts b/packages/devtools-anywhere/src/element/togglers.ts new file mode 100644 index 0000000..32de278 --- /dev/null +++ b/packages/devtools-anywhere/src/element/togglers.ts @@ -0,0 +1,128 @@ +// Elements with `data-devtools-anywhere-toggle` open and close a `<devtools-anywhere>` when clicked. +import { isShellPage } from '../cdp-agent'; +import type { TogglerAttributes } from '../public-types'; +import { + CONTROL_EVENT, + customElementsLoading, + globalElements, + inactiveGlobals, + isCustom, + isShellElement, + ownElements, + STATE_EVENT, +} from './registry'; + +/** What a toggler fires on an element placed by the page, which has no open state. */ +const TOGGLE_REQUEST_EVENT = 'devtools-toggle-request'; + +/** Elements with this attribute open and close DevTools when clicked (see togglerTarget). */ +const TOGGLE_ATTRIBUTE: keyof TogglerAttributes = 'data-devtools-anywhere-toggle'; +/** The tag the togglers look for, once they are installed. */ +let togglerTag = ''; +/** Toggler values already warned about. */ +const warned = new Set<string>(); + +/** + * The element a toggler controls: the element whose id is the attribute's value, or without one + * the first global element, or in the shell's page iframe the shell's element. + */ +export function togglerTarget(toggler: Element, warn: boolean): Element | null { + const id = toggler.getAttribute(TOGGLE_ATTRIBUTE) ?? ''; + const doc = toggler.ownerDocument; + let target: Element | null | undefined = null; + if (isShellPage()) { + // An element the page placed in the shell's page: the toggler names it. Without a value, the + // shell's element, of the shell window's class: it is toggled through an event. + const named = id ? doc.getElementById(id) : null; + if (named && customElementsLoading.has(named)) return named; + if (!id) return shellElement(); + } else { + // Without a value, the global element (or the shell's); global elements after the first do + // nothing: they are not targets. + target = id + ? doc.getElementById(id) + : [...doc.querySelectorAll(togglerTag)].find( + el => (globalElements.has(el) || isShellElement(el)) && !inactiveGlobals.has(el) + ); + } + if (target && ownElements.has(target) && !inactiveGlobals.has(target)) return target; + if (warn && !warned.has(id)) { + warned.add(id); + console.warn(`devtools-anywhere: no <${togglerTag}>${id ? ` with id "${id}"` : ''} for ${TOGGLE_ATTRIBUTE}`); + } + return null; +} + +/** In the shell's page iframe: the shell's element, of the shell window's class. */ +export function shellElement(): Element | null { + const shellDocument = window.frameElement?.ownerDocument; + return (shellDocument && [...shellDocument.querySelectorAll(togglerTag)].find(isShellElement)) ?? null; +} + +/** + * Asks a toggler's element, maybe of another window, whether it is open, toggling it first when + * `toggle`. Undefined when it does not answer. + */ +export function controlElement(target: Element, toggle: boolean): boolean | undefined { + const detail: { toggle: boolean; open?: boolean } = { toggle }; + target.dispatchEvent(new CustomEvent(CONTROL_EVENT, { detail })); + return detail.open; +} + +/** Sets aria-expanded on a toggler from the state of its element. */ +function refreshToggler(toggler: Element, warn = false) { + const target = togglerTarget(toggler, warn); + // An element placed by the page has no open state. + if (target && isCustom(target)) return target; + const open = target && controlElement(target, false); + if (open !== undefined && open !== null) toggler.setAttribute('aria-expanded', String(open)); + return target; +} + +export function refreshTogglers(doc: Document) { + if (!togglerTag) return; + for (const toggler of doc.querySelectorAll(`[${TOGGLE_ATTRIBUTE}]`)) refreshToggler(toggler); +} + +/** Makes any element with TOGGLE_ATTRIBUTE, including those added later, a toggler. */ +export function installTogglers(tag: string) { + if (togglerTag) return; + togglerTag = tag; + document.addEventListener( + 'click', + event => { + const clicked = event.target instanceof Element ? event.target : null; + const toggler = clicked?.closest(`[${TOGGLE_ATTRIBUTE}]`); + // A label's click is followed by one on its control: that one toggles, once. + const control = clicked?.closest('label')?.control; + if (!toggler || (control && control !== clicked && toggler.contains(control))) return; + const target = toggler && refreshToggler(toggler, true); + if (!target) return; + // An element placed by the page only tells its listeners, which decide. + if (isCustom(target)) target.dispatchEvent(new Event(TOGGLE_REQUEST_EVENT)); + else controlElement(target, true); + refreshToggler(toggler); + }, + true + ); + const refresh = () => refreshTogglers(document); + if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', refresh); + else refresh(); + // In the shell's page iframe, the togglers follow the shell's element while this page is shown. + if (!isShellPage()) return; + const shellDocument = window.frameElement?.ownerDocument; + let shell: Element | null = null; + const listen = () => { + shell = togglerTarget(document.documentElement, false); + shell?.addEventListener(STATE_EVENT, refresh); + refresh(); + }; + // The shell parses its element after its bundle, which may load after this page has run. + if (shellDocument?.readyState === 'loading') shellDocument.addEventListener('DOMContentLoaded', listen); + else listen(); + window.addEventListener('pagehide', () => { + shellDocument?.removeEventListener('DOMContentLoaded', listen); + shell?.removeEventListener(STATE_EVENT, refresh); + }); + window.addEventListener('pageshow', event => event.persisted && listen()); +} diff --git a/packages/devtools-anywhere/src/probe.ts b/packages/devtools-anywhere/src/probe.ts index 8407461..50efe25 100644 --- a/packages/devtools-anywhere/src/probe.ts +++ b/packages/devtools-anywhere/src/probe.ts @@ -180,7 +180,7 @@ const hasApi = <K extends 'connectRemote' | 'createGlobalElement'>( // In the shell's top window the probe has replaced the document with the shell already. The page in // the shell has the bundle from installProbe() already and only connects to the remote server: the shell -// shows DevTools, and the elements the page places take the shell's frontend (see element.ts). +// shows DevTools, and the elements the page places take the shell's frontend (see element/index.ts). if (script && bundle && (frontend || remoteUrl || shellPage) && !shellTop) { probe.ownUrls.add(bundle); diff --git a/packages/devtools-anywhere/src/shell-messages.ts b/packages/devtools-anywhere/src/shell-messages.ts index aa33565..4205e00 100644 --- a/packages/devtools-anywhere/src/shell-messages.ts +++ b/packages/devtools-anywhere/src/shell-messages.ts @@ -1,5 +1,5 @@ // Messages of shell mode between the page and its Service Worker (devtools-anywhere-service-worker.js), which -// service-worker.ts, element.ts and probe.ts all use. Constants only: service-worker.ts is built for a worker. +// service-worker.ts, element/index.ts and probe.ts all use. Constants only: service-worker.ts is built for a worker. /** Posted by the shell to the worker: the client it comes from is a shell. */ export const SHELL_HANDSHAKE = 'devtools-anywhere:shell-handshake'; From 89ed730b4c891228cdb412c05c1cf61a401b1e4e Mon Sep 17 00:00:00 2001 From: jerrychan7 <czy-ken@163.com> Date: Mon, 2 Nov 2026 12:25:49 +0800 Subject: [PATCH 24/26] test(input): ignore a mouse release Chrome lets through before touch emulation On CI, Chrome sometimes answers Input.dispatchMouseEvent's mouseReleased with touch emulation for the mouse just enabled by sending the page a pointerup and a mouseup, as for a real mouse: 'a release or a move without a touch sends nothing' failed twice in a row on PR #1 and once on main before, and never locally. Chrome's side now ignores those as it already did the hover's pointerover and mouseover; ours still has to send nothing at all. --- .../cdp-agent/domains/input/input.conformance.test.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/devtools-anywhere/src/cdp-agent/domains/input/input.conformance.test.ts b/packages/devtools-anywhere/src/cdp-agent/domains/input/input.conformance.test.ts index 6b707f7..7003bd2 100644 --- a/packages/devtools-anywhere/src/cdp-agent/domains/input/input.conformance.test.ts +++ b/packages/devtools-anywhere/src/cdp-agent/domains/input/input.conformance.test.ts @@ -341,11 +341,12 @@ test('a release or a move without a touch sends nothing', async () => { } await side.send('Emulation.setEmitTouchEventsForMouse', { enabled: false }); }; - // Chrome's hover from earlier tests sometimes gets a pointerover and a mouseover through: - // only what a touch would send is compared. - const touchy = (events: string[]) => events.filter(e => !/^(pointer|mouse)over:/.test(e)); + // Chrome's hover from earlier tests sometimes gets a pointerover and a mouseover through, and on + // CI a release sometimes reaches the page as a mouse release (pointerup, mouseup) before touch + // emulation takes it: on Chrome's side only what a touch would send is compared. Ours sends nothing. + const touchy = (events: string[]) => events.filter(e => !/^(pointer|mouse)(over|up):/.test(e)); assert.deepEqual(touchy((await run(t.chrome, steps('chrome'))).events), []); - assert.deepEqual(touchy((await run(t.ours, steps('ours'))).events), []); + assert.deepEqual((await run(t.ours, steps('ours'))).events, []); assert.deepEqual(responses['ours'], responses['chrome']); assert.deepEqual(responses['chrome'], [{}, {}, {}, {}]); }); From 4d307d69adeae4ee71b7b56c0d9d2d149a443efa Mon Sep 17 00:00:00 2001 From: jerrychan7 <czy-ken@163.com> Date: Mon, 2 Nov 2026 12:31:37 +0800 Subject: [PATCH 25/26] test(input): compare only the press in the auxclick test On CI, Chrome sometimes sends no pointerout for the field the test before clicked, where ours does: 'a middle click fires auxclick' failed twice in three runs on PR #1. What it checks is the press and auxclick: the hover's pointerover/out and mouseover are left out of the comparison. --- .../domains/input/input.conformance.test.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/devtools-anywhere/src/cdp-agent/domains/input/input.conformance.test.ts b/packages/devtools-anywhere/src/cdp-agent/domains/input/input.conformance.test.ts index 7003bd2..3b86c43 100644 --- a/packages/devtools-anywhere/src/cdp-agent/domains/input/input.conformance.test.ts +++ b/packages/devtools-anywhere/src/cdp-agent/domains/input/input.conformance.test.ts @@ -166,8 +166,10 @@ test('a keyDown with text (Puppeteer style) fires keypress and types', async () assert.equal(result.value, 'h'); }); +// Where Chrome's hover was after the test before is not always where ours was (on CI, Chrome +// sometimes sends no pointerout for the field it last clicked): only the press is compared. test('a middle click fires auxclick', async () => { - const result = await same(async side => { + const steps = async (side: Side) => { await side.send('Input.dispatchMouseEvent', { type: 'mouseMoved', x: 50, y: 30 }); await side.send('Input.dispatchMouseEvent', { type: 'mousePressed', @@ -185,8 +187,12 @@ test('a middle click fires auxclick', async () => { buttons: 0, clickCount: 1, }); - }); - assert.ok(result.events.includes('auxclick:btn')); + }; + const pressed = (events: string[]) => events.filter(e => !/^(pointer|mouse)(over|out):/.test(e)); + const chrome = pressed((await run(t.chrome, steps)).events); + const ours = pressed((await run(t.ours, steps)).events); + assert.deepEqual(ours, chrome); + assert.ok(ours.includes('auxclick:btn')); }); // Off the page, Chrome sends the events to the document, the release included, and clicks it From 20c0fe06b8529b7b181d2c60f2b0d783488bbb72 Mon Sep 17 00:00:00 2001 From: jerrychan7 <czy-ken@163.com> Date: Mon, 2 Nov 2026 12:37:29 +0800 Subject: [PATCH 26/26] test(e2e): click the Sources panel's controls once they are laid out On CI the Watch pane's prompt was sometimes in the DOM but not laid out yet when clicked, and Puppeteer threw 'Node is either not clickable or not an Element'. The test now waits for a box before each click there (add watch, the prompt, the line number). --- tests/e2e/real-frontend.test.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/e2e/real-frontend.test.ts b/tests/e2e/real-frontend.test.ts index e7f48b4..590163f 100644 --- a/tests/e2e/real-frontend.test.ts +++ b/tests/e2e/real-frontend.test.ts @@ -237,14 +237,16 @@ test('the Sources panel shows source and Watch, without breakpoints or pausing c // Watch stays: without pausing, its expressions are evaluated in the global scope. assert.equal(await (await query('[aria-label="Watch"]')).evaluate(el => el === null), false, 'Watch'); + /** The element, once laid out: there is a point to click on. */ + const laidOut = async (el: ElementHandle<Element> | null) => (el && (await el.boundingBox()) ? el : null); const addWatch = async () => // asElement() is typed as a handle to any Node; the page function returns an Element or null. (await query('[aria-label="Add watch expression"]')).asElement() as ElementHandle<Element> | null; - await (await waitFor(addWatch)).click(); + await (await waitFor(async () => laidOut(await addWatch()))).click(); const prompt = async () => // asElement() is typed as a handle to any Node; the page function returns an Element or null. (await query('.text-prompt[contenteditable]')).asElement() as ElementHandle<Element> | null; - await (await waitFor(prompt)).click(); + await (await waitFor(async () => laidOut(await prompt()))).click(); await frontend.page().keyboard.type('document.title'); await frontend.page().keyboard.press('Enter'); await waitFor(async () => (await text()).includes('document.title: "real frontend"')); @@ -253,7 +255,7 @@ test('the Sources panel shows source and Watch, without breakpoints or pausing c const lineNumber = async () => // asElement() is typed as a handle to any Node; the page function returns an Element or null. (await query('.cm-lineNumbers .cm-gutterElement:nth-child(2)')).asElement() as ElementHandle<Element> | null; - await (await waitFor(lineNumber)).click(); + await (await waitFor(async () => laidOut(await lineNumber()))).click(); await new Promise(r => setTimeout(r, 500)); assert.equal(await (await query('.cm-breakpoint')).evaluate(el => el === null), true); const breakpoints = await frontend.evaluate(async () => {