diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9f58ca2c974..a38a2c84bef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -151,23 +151,39 @@ jobs: - name: Build packages run: npm run build --workspace @puppeteer-test/test - name: Setup cache for Chrome binary + if: ${{ matrix.suite != 'chrome-bidi' }} uses: actions/cache@v3 with: path: ~/.cache/puppeteer/chrome key: ${{ runner.os }}-Chrome-${{ hashFiles('packages/puppeteer-core/src/revisions.ts') }}-${{ hashFiles('packages/puppeteer/src/node/install.ts') }} - name: Install Chrome + if: ${{ matrix.suite != 'chrome-bidi' }} run: npm run postinstall + - name: Setup cache for Chrome Canary binary + if: ${{ matrix.suite == 'chrome-bidi' }} + uses: actions/cache@v3 + with: + path: ~/.cache/puppeteer/chrome-canary + key: ${{ runner.os }}-Chrome-Canary-${{ hashFiles('package.json') }} + - name: Install Chrome Canary + if: ${{ matrix.suite == 'chrome-bidi' }} + id: browser + run: node tools/download_chrome_bidi.mjs ~/.cache/puppeteer/chrome-canary - name: Tests types run: npm run test-types - name: Run all tests (for non-Linux) if: ${{ matrix.os != 'ubuntu-latest' }} run: npm run test -- --test-suite ${{ matrix.suite }} --save-stats-to /tmp/artifacts/${{ github.event_name }}_INSERTID.json + env: + PUPPETEER_EXECUTABLE_PATH: ${{ steps.browser.outputs.executablePath }} - name: Install linux dependencies. if: ${{ matrix.os == 'ubuntu-latest' }} run: sudo apt-get install xvfb - name: Run all tests (for Linux) if: ${{ matrix.os == 'ubuntu-latest' }} run: xvfb-run --auto-servernum npm run test -- --test-suite ${{ matrix.suite }} --save-stats-to /tmp/artifacts/${{ github.event_name }}_INSERTID.json + env: + PUPPETEER_EXECUTABLE_PATH: ${{ steps.browser.outputs.executablePath }} - uses: actions/upload-artifact@v3 if: always() with: diff --git a/package-lock.json b/package-lock.json index fb8d217e0b6..8d7fe082b99 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2735,9 +2735,9 @@ "license": "ISC" }, "node_modules/chromium-bidi": { - "version": "0.4.9", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.9.tgz", - "integrity": "sha512-u3DC6XwgLCA9QJ5ak1voPslCmacQdulZNCPsI3qNXxSnEcZS7DFIbww+5RM2bznMEje7cc0oydavRLRvOIZtHw==", + "version": "0.4.10", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.10.tgz", + "integrity": "sha512-ngdRIq/f5G3nIOz1M0MtCABCTezr79MBCrJ09K2xRk+hTZQGTH8JIeFbgQmVvNPBMQblh7ROfJnSzsE07YpFfg==", "dependencies": { "mitt": "3.0.0" }, @@ -9839,7 +9839,7 @@ "license": "Apache-2.0", "dependencies": { "@puppeteer/browsers": "1.3.0", - "chromium-bidi": "0.4.9", + "chromium-bidi": "0.4.10", "cross-fetch": "3.1.6", "debug": "4.3.4", "devtools-protocol": "0.0.1120988", @@ -11836,9 +11836,9 @@ "version": "1.1.4" }, "chromium-bidi": { - "version": "0.4.9", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.9.tgz", - "integrity": "sha512-u3DC6XwgLCA9QJ5ak1voPslCmacQdulZNCPsI3qNXxSnEcZS7DFIbww+5RM2bznMEje7cc0oydavRLRvOIZtHw==", + "version": "0.4.10", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.10.tgz", + "integrity": "sha512-ngdRIq/f5G3nIOz1M0MtCABCTezr79MBCrJ09K2xRk+hTZQGTH8JIeFbgQmVvNPBMQblh7ROfJnSzsE07YpFfg==", "requires": { "mitt": "3.0.0" } @@ -15144,7 +15144,7 @@ "version": "file:packages/puppeteer-core", "requires": { "@puppeteer/browsers": "1.3.0", - "chromium-bidi": "0.4.9", + "chromium-bidi": "0.4.10", "cross-fetch": "3.1.6", "debug": "4.3.4", "devtools-protocol": "0.0.1120988", diff --git a/packages/puppeteer-core/package.json b/packages/puppeteer-core/package.json index 24ab503d60a..fdffcbcfa03 100644 --- a/packages/puppeteer-core/package.json +++ b/packages/puppeteer-core/package.json @@ -132,7 +132,7 @@ "author": "The Chromium Authors", "license": "Apache-2.0", "dependencies": { - "chromium-bidi": "0.4.9", + "chromium-bidi": "0.4.10", "cross-fetch": "3.1.6", "debug": "4.3.4", "devtools-protocol": "0.0.1120988", diff --git a/packages/puppeteer-core/src/common/Browser.ts b/packages/puppeteer-core/src/common/Browser.ts index bffd9577c63..fc3222b19cf 100644 --- a/packages/puppeteer-core/src/common/Browser.ts +++ b/packages/puppeteer-core/src/common/Browser.ts @@ -90,7 +90,7 @@ export class CDPBrowser extends BrowserBase { #targetFilterCallback: TargetFilterCallback; #isPageTargetCallback!: IsPageTargetCallback; #defaultContext: CDPBrowserContext; - #contexts: Map; + #contexts = new Map(); #screenshotTaskQueue: TaskQueue; #targetManager: TargetManager; @@ -143,7 +143,6 @@ export class CDPBrowser extends BrowserBase { ); } this.#defaultContext = new CDPBrowserContext(this.#connection, this); - this.#contexts = new Map(); for (const contextId of contextIds) { this.#contexts.set( contextId, diff --git a/packages/puppeteer-core/src/common/bidi/BrowserContext.ts b/packages/puppeteer-core/src/common/bidi/BrowserContext.ts index 9d2c30156bf..123686beaae 100644 --- a/packages/puppeteer-core/src/common/bidi/BrowserContext.ts +++ b/packages/puppeteer-core/src/common/bidi/BrowserContext.ts @@ -61,7 +61,7 @@ export class BrowserContext extends BrowserContextBase { const {result} = await this.#connection.send('browsingContext.create', { type: 'tab', }); - const page = await Page._create(this.#connection, result); + const page = new Page(this.#connection, result); if (this.#defaultViewport) { try { await page.setViewport(this.#defaultViewport); @@ -81,6 +81,6 @@ export class BrowserContext extends BrowserContextBase { debugError(error); }); } - this.#pages = new Map(); + this.#pages.clear(); } } diff --git a/packages/puppeteer-core/src/common/bidi/Page.ts b/packages/puppeteer-core/src/common/bidi/Page.ts index 768aaed502d..8b09cf38793 100644 --- a/packages/puppeteer-core/src/common/bidi/Page.ts +++ b/packages/puppeteer-core/src/common/bidi/Page.ts @@ -108,32 +108,26 @@ export class Page extends PageBase { ], ]); - constructor(connection: Connection, info: Bidi.BrowsingContext.Info) { + constructor(connection: Connection, info: {context: string}) { super(); this.#connection = connection; this.#networkManager = new NetworkManager(connection, this); + this.#onFrameAttached({ + ...info, + url: 'about:blank', + children: [], + }); - this.#handleFrameTree(info); + for (const [event, subscriber] of this.#subscribedEvents) { + this.#connection.on(event, subscriber); + } for (const [event, subscriber] of this.#networkManagerEvents) { this.#networkManager.on(event, subscriber); } } - static async _create( - connection: Connection, - info: Bidi.BrowsingContext.Info - ): Promise { - const page = new Page(connection, info); - - for (const [event, subscriber] of page.#subscribedEvents) { - connection.on(event, subscriber); - } - - return page; - } - override mainFrame(): Frame { const mainFrame = this.#frameTree.getMainFrame(); assert(mainFrame, 'Requesting main frame too early!'); @@ -144,28 +138,14 @@ export class Page extends PageBase { return Array.from(this.#frameTree.frames()); } - frame(frameId: string): Frame | null { - return this.#frameTree.getById(frameId) || null; + frame(frameId?: string): Frame | null { + return this.#frameTree.getById(frameId ?? '') || null; } childFrames(frameId: string): Frame[] { return this.#frameTree.childFrames(frameId); } - #handleFrameTree(info: Bidi.BrowsingContext.Info): void { - if (info) { - this.#onFrameAttached(info); - } - - if (!info.children) { - return; - } - - for (const child of info.children) { - this.#handleFrameTree(child); - } - } - #onFrameAttached(info: Bidi.BrowsingContext.Info): void { if ( !this.frame(info.context) && @@ -177,6 +157,7 @@ export class Page extends PageBase { info ); this.#connection.registerBrowsingContexts(context); + const frame = new Frame(this, context, info.parent); this.#frameTree.addFrame(frame); @@ -195,10 +176,10 @@ export class Page extends PageBase { for (const child of frame.childFrames()) { this.#removeFramesRecursively(child); } - } - frame = await this.#frameTree.waitForFrame(frameId); - this.emit(FrameManagerEmittedEvents.FrameNavigated, frame); + frame = await this.#frameTree.waitForFrame(frameId); + this.emit(FrameManagerEmittedEvents.FrameNavigated, frame); + } } #onFrameDetached(info: Bidi.BrowsingContext.Info): void { @@ -219,6 +200,9 @@ export class Page extends PageBase { } #onLogEntryAdded(event: Bidi.Log.LogEntry): void { + if (!this.frame(event.source.context)) { + return; + } if (isConsoleLogEntry(event)) { const args = event.args.map(arg => { return getBidiHandle(this.mainFrame().context(), arg); diff --git a/tools/download_chrome_bidi.mjs b/tools/download_chrome_bidi.mjs new file mode 100644 index 00000000000..5e27f58028c --- /dev/null +++ b/tools/download_chrome_bidi.mjs @@ -0,0 +1,58 @@ +/* eslint-disable no-console */ + +/** + * Copyright 2023 Google LLC. + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Installs a browser defined in `.browser` for Chromium-BiDi using + * `@puppeteer/browsers` to the directory provided as the first argument + * (default: cwd). The executable path is written to the `executablePath` output + * param for GitHub actions. + * + * Examples: + * - `node install-browser.mjs` + * - `node install-browser.mjs /tmp/cache` + */ + +import fs from 'fs/promises'; + +import actions from '@actions/core'; +import {install, computeExecutablePath} from '@puppeteer/browsers'; + +try { + const browserSpec = ( + await fs.readFile('node_modules/chromium-bidi/.browser', 'utf-8') + ).trim(); + const cacheDir = process.argv[2] || process.cwd(); + // See .browser for the format. + const browser = browserSpec.split('@')[0]; + const buildId = browserSpec.split('@')[1]; + await install({ + browser, + buildId, + cacheDir, + }); + const executablePath = computeExecutablePath({ + cacheDir, + browser, + buildId, + }); + actions.setOutput('executablePath', executablePath); + console.log(executablePath); +} catch (err) { + actions.setFailed(`Failed to download the browser: ${err.message}`); +}