forked from mirrors/gecko-dev
Bug 1817934 - [puppeteer] Vendor Puppeteer v19.7.2. r=webdriver-reviewers,jdescottes
Differential Revision: https://phabricator.services.mozilla.com/D170799
This commit is contained in:
parent
13840898bb
commit
33c84b1801
213 changed files with 7845 additions and 35529 deletions
|
|
@ -117,10 +117,14 @@ _OPT\.OBJ/
|
||||||
|
|
||||||
# Ignore node_module directories and npm artifacts
|
# Ignore node_module directories and npm artifacts
|
||||||
^remote/test/puppeteer/.*\.tsbuildinfo
|
^remote/test/puppeteer/.*\.tsbuildinfo
|
||||||
|
^remote/test/puppeteer/.*/lib/
|
||||||
|
^remote/test/puppeteer/.*/node_modules/
|
||||||
|
^remote/test/puppeteer/.*/\.wireit/
|
||||||
|
^remote/test/puppeteer/\.devcontainer/
|
||||||
^remote/test/puppeteer/\.github
|
^remote/test/puppeteer/\.github
|
||||||
^remote/test/puppeteer/\.husky
|
^remote/test/puppeteer/\.husky
|
||||||
|
^remote/test/puppeteer/\.wireit/
|
||||||
^remote/test/puppeteer/coverage/
|
^remote/test/puppeteer/coverage/
|
||||||
^remote/test/puppeteer/.devcontainer/
|
|
||||||
^remote/test/puppeteer/docker/
|
^remote/test/puppeteer/docker/
|
||||||
^remote/test/puppeteer/docs/puppeteer-core\.api\.json
|
^remote/test/puppeteer/docs/puppeteer-core\.api\.json
|
||||||
^remote/test/puppeteer/docs/puppeteer\.api\.json
|
^remote/test/puppeteer/docs/puppeteer\.api\.json
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ generated/
|
||||||
|
|
||||||
# IDE Artifacts
|
# IDE Artifacts
|
||||||
.vscode
|
.vscode
|
||||||
|
!.vscode/extensions.json
|
||||||
|
!.vscode/*.template.json
|
||||||
.devcontainer
|
.devcontainer
|
||||||
|
|
||||||
# Misc
|
# Misc
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,14 @@ module.exports = {
|
||||||
],
|
],
|
||||||
'import/extensions': ['error', 'ignorePackages'],
|
'import/extensions': ['error', 'ignorePackages'],
|
||||||
|
|
||||||
|
'import/order': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
'newlines-between': 'always',
|
||||||
|
alphabetize: {order: 'asc', caseInsensitive: true},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
'no-restricted-syntax': [
|
'no-restricted-syntax': [
|
||||||
'error',
|
'error',
|
||||||
// Don't allow underscored declarations on camelCased variables/properties.
|
// Don't allow underscored declarations on camelCased variables/properties.
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ generated/
|
||||||
# IDE Artifacts
|
# IDE Artifacts
|
||||||
.vscode/*
|
.vscode/*
|
||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
|
!.vscode/*.template.json
|
||||||
.devcontainer
|
.devcontainer
|
||||||
|
|
||||||
# Misc
|
# Misc
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"packages/puppeteer": "19.6.0",
|
"packages/puppeteer": "19.7.2",
|
||||||
"packages/puppeteer-core": "19.6.0",
|
"packages/puppeteer-core": "19.7.2",
|
||||||
"packages/testserver": "0.6.0",
|
"packages/testserver": "0.6.0",
|
||||||
"packages/ng-schematics": "0.1.0"
|
"packages/ng-schematics": "0.1.0"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,9 +44,17 @@ npm i puppeteer
|
||||||
```
|
```
|
||||||
|
|
||||||
When you install Puppeteer, it automatically downloads a recent version of
|
When you install Puppeteer, it automatically downloads a recent version of
|
||||||
Chromium (~170MB macOS, ~282MB Linux, ~280MB Windows) that is
|
Chromium (~170MB macOS, ~282MB Linux, ~280MB Windows) that is [guaranteed to
|
||||||
[guaranteed to work](https://pptr.dev/faq#q-why-doesnt-puppeteer-vxxx-work-with-chromium-vyyy)
|
work](https://pptr.dev/faq#q-why-doesnt-puppeteer-vxxx-work-with-chromium-vyyy)
|
||||||
with Puppeteer. For a version of Puppeteer without installation, see
|
with Puppeteer. The browser is downloaded to the `$HOME/.cache/puppeteer` folder
|
||||||
|
by default (starting with Puppeteer v19.0.0).
|
||||||
|
|
||||||
|
If you deploy a project using Puppeteer to a hosting provider, such as Render or
|
||||||
|
Heroku, you might need to reconfigure the location of the cache to be within
|
||||||
|
your project folder (see an example below) because not all hosting providers
|
||||||
|
include `$HOME/.cache` into the project's deployment.
|
||||||
|
|
||||||
|
For a version of Puppeteer without the browser installation, see
|
||||||
[`puppeteer-core`](#puppeteer-core).
|
[`puppeteer-core`](#puppeteer-core).
|
||||||
|
|
||||||
#### Configuration
|
#### Configuration
|
||||||
|
|
@ -149,7 +157,7 @@ import puppeteer from 'puppeteer';
|
||||||
await page.waitForSelector(searchResultSelector);
|
await page.waitForSelector(searchResultSelector);
|
||||||
await page.click(searchResultSelector);
|
await page.click(searchResultSelector);
|
||||||
|
|
||||||
// Localte the full title with a unique string
|
// Locate the full title with a unique string
|
||||||
const textSelector = await page.waitForSelector(
|
const textSelector = await page.waitForSelector(
|
||||||
'text/Customize and automate'
|
'text/Customize and automate'
|
||||||
);
|
);
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -5,6 +5,6 @@ origin:
|
||||||
description: Headless Chrome Node API
|
description: Headless Chrome Node API
|
||||||
license: Apache-2.0
|
license: Apache-2.0
|
||||||
name: puppeteer
|
name: puppeteer
|
||||||
release: e13e9647fc0d917da94af8851a09ed318fb0e07c
|
release: puppeteer-v19.7.2
|
||||||
url: /Users/alexandraborovova/Projects/puppeteer
|
url: https://github.com/puppeteer/puppeteer.git
|
||||||
schema: 1
|
schema: 1
|
||||||
|
|
|
||||||
469
remote/test/puppeteer/package-lock.json
generated
469
remote/test/puppeteer/package-lock.json
generated
|
|
@ -19,7 +19,7 @@
|
||||||
"@microsoft/api-extractor": "7.33.7",
|
"@microsoft/api-extractor": "7.33.7",
|
||||||
"@microsoft/api-extractor-model": "7.25.3",
|
"@microsoft/api-extractor-model": "7.25.3",
|
||||||
"@pptr/testserver": "file:packages/testserver",
|
"@pptr/testserver": "file:packages/testserver",
|
||||||
"@rollup/plugin-commonjs": "24.0.0",
|
"@rollup/plugin-commonjs": "24.0.1",
|
||||||
"@rollup/plugin-node-resolve": "15.0.1",
|
"@rollup/plugin-node-resolve": "15.0.1",
|
||||||
"@types/debug": "4.1.7",
|
"@types/debug": "4.1.7",
|
||||||
"@types/diff": "5.0.2",
|
"@types/diff": "5.0.2",
|
||||||
|
|
@ -40,7 +40,6 @@
|
||||||
"@typescript-eslint/eslint-plugin": "5.46.1",
|
"@typescript-eslint/eslint-plugin": "5.46.1",
|
||||||
"@typescript-eslint/parser": "5.46.1",
|
"@typescript-eslint/parser": "5.46.1",
|
||||||
"c8": "7.12.0",
|
"c8": "7.12.0",
|
||||||
"chromium-bidi": "0.4.3",
|
|
||||||
"commonmark": "0.30.0",
|
"commonmark": "0.30.0",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"diff": "5.1.0",
|
"diff": "5.1.0",
|
||||||
|
|
@ -70,8 +69,7 @@
|
||||||
"pngjs": "6.0.0",
|
"pngjs": "6.0.0",
|
||||||
"prettier": "2.8.1",
|
"prettier": "2.8.1",
|
||||||
"puppeteer": "file:packages/puppeteer",
|
"puppeteer": "file:packages/puppeteer",
|
||||||
"rollup": "2.79.1",
|
"rollup": "3.12.1",
|
||||||
"rollup-plugin-dts": "4.2.2",
|
|
||||||
"semver": "7.3.8",
|
"semver": "7.3.8",
|
||||||
"sinon": "15.0.1",
|
"sinon": "15.0.1",
|
||||||
"source-map-support": "0.5.21",
|
"source-map-support": "0.5.21",
|
||||||
|
|
@ -103,11 +101,11 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@angular-devkit/architect": {
|
"node_modules/@angular-devkit/architect": {
|
||||||
"version": "0.1501.2",
|
"version": "0.1501.6",
|
||||||
"resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1501.2.tgz",
|
"resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1501.6.tgz",
|
||||||
"integrity": "sha512-AfORVGLN0FBIUXO3FkfGOKu+Gz6oJjF8Bu8cPn27duiI0wszxGNY3fATKwbSg7JcKx1oQS/G7RjyC5OiTA6a0Q==",
|
"integrity": "sha512-u07zZFlfrg0Qn4mu5M9Nz0pH2Yd2028XF/73980PsZMxwkSm4diF08v4bHk3UyR7yPT7phwvt4znj6ryZhx1gw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular-devkit/core": "15.1.2",
|
"@angular-devkit/core": "15.1.6",
|
||||||
"rxjs": "6.6.7"
|
"rxjs": "6.6.7"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
@ -116,10 +114,10 @@
|
||||||
"yarn": ">= 1.13.0"
|
"yarn": ">= 1.13.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@angular-devkit/architect/node_modules/@angular-devkit/core": {
|
"node_modules/@angular-devkit/core": {
|
||||||
"version": "15.1.2",
|
"version": "15.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-15.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-15.1.6.tgz",
|
||||||
"integrity": "sha512-wkLZYvTZt30Ge6Z83Gxsr6mO1TIHCu3SImdE0zwW63EdU9o1NYkU74z1D9VUZ9Up7uHi1cHs/dssbxUuZ4eWOA==",
|
"integrity": "sha512-jGgxyRjecVf6lEyqDxz7ltMEndNPxIg720pk6r40fgsu0dU8w9vjJSJe7k0XdJiXVRcN6wZa/J5nO/xcwWVIsA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ajv": "8.12.0",
|
"ajv": "8.12.0",
|
||||||
"ajv-formats": "2.1.1",
|
"ajv-formats": "2.1.1",
|
||||||
|
|
@ -141,7 +139,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@angular-devkit/architect/node_modules/ajv": {
|
"node_modules/@angular-devkit/core/node_modules/ajv": {
|
||||||
"version": "8.12.0",
|
"version": "8.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
|
||||||
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
|
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
|
||||||
|
|
@ -156,7 +154,7 @@
|
||||||
"url": "https://github.com/sponsors/epoberezkin"
|
"url": "https://github.com/sponsors/epoberezkin"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@angular-devkit/architect/node_modules/source-map": {
|
"node_modules/@angular-devkit/core/node_modules/source-map": {
|
||||||
"version": "0.7.4",
|
"version": "0.7.4",
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
|
||||||
"integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
|
"integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
|
||||||
|
|
@ -1386,14 +1384,18 @@
|
||||||
"resolved": "test",
|
"resolved": "test",
|
||||||
"link": true
|
"link": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@puppeteer/browsers": {
|
||||||
|
"resolved": "packages/browsers",
|
||||||
|
"link": true
|
||||||
|
},
|
||||||
"node_modules/@puppeteer/ng-schematics": {
|
"node_modules/@puppeteer/ng-schematics": {
|
||||||
"resolved": "packages/ng-schematics",
|
"resolved": "packages/ng-schematics",
|
||||||
"link": true
|
"link": true
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/plugin-commonjs": {
|
"node_modules/@rollup/plugin-commonjs": {
|
||||||
"version": "24.0.0",
|
"version": "24.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-24.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-24.0.1.tgz",
|
||||||
"integrity": "sha512-0w0wyykzdyRRPHOb0cQt14mIBLujfAv6GgP6g8nvg/iBxEm112t3YPPq+Buqe2+imvElTka+bjNlJ/gB56TD8g==",
|
"integrity": "sha512-15LsiWRZk4eOGqvrJyu3z3DaBu5BhXIMeWnijSRvd8irrrg9SHpQ1pH+BUK4H6Z9wL9yOxZJMTLU+Au86XHxow==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@rollup/pluginutils": "^5.0.1",
|
"@rollup/pluginutils": "^5.0.1",
|
||||||
|
|
@ -1421,18 +1423,6 @@
|
||||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/plugin-commonjs/node_modules/magic-string": {
|
|
||||||
"version": "0.27.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz",
|
|
||||||
"integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@jridgewell/sourcemap-codec": "^1.4.13"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/plugin-node-resolve": {
|
"node_modules/@rollup/plugin-node-resolve": {
|
||||||
"version": "15.0.1",
|
"version": "15.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.0.1.tgz",
|
||||||
|
|
@ -2673,13 +2663,14 @@
|
||||||
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
|
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
|
||||||
},
|
},
|
||||||
"node_modules/chromium-bidi": {
|
"node_modules/chromium-bidi": {
|
||||||
"version": "0.4.3",
|
"version": "0.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.4.tgz",
|
||||||
"integrity": "sha512-A40H1rdpJqkTdnGhnYDzMhtDdIbkXNFj2wgIfivMXL7LyHFDmBtv1hdyycDhnxtYunbPLDZtTs/n+ZT5j7Vnew==",
|
"integrity": "sha512-4BX5cSaponuvVT1+SbLYTOAgDoVtX/Khoc9UsbFJ/AsPVUeFAM3RiIDFI6XFhLYMi9WmVJqh1ZH+dRpNKkKwiQ==",
|
||||||
"dev": true,
|
"dependencies": {
|
||||||
|
"mitt": "3.0.0"
|
||||||
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"devtools-protocol": "*",
|
"devtools-protocol": "*"
|
||||||
"mitt": "*"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cli-cursor": {
|
"node_modules/cli-cursor": {
|
||||||
|
|
@ -2717,7 +2708,6 @@
|
||||||
"version": "8.0.1",
|
"version": "8.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
||||||
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
|
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"string-width": "^4.2.0",
|
"string-width": "^4.2.0",
|
||||||
"strip-ansi": "^6.0.1",
|
"strip-ansi": "^6.0.1",
|
||||||
|
|
@ -3036,9 +3026,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/devtools-protocol": {
|
"node_modules/devtools-protocol": {
|
||||||
"version": "0.0.1082910",
|
"version": "0.0.1094867",
|
||||||
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1082910.tgz",
|
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1094867.tgz",
|
||||||
"integrity": "sha512-RqoZ2GmqaNxyx+99L/RemY5CkwG9D0WEfOKxekwCRXOGrDCep62ngezEJUVMq6rISYQ+085fJnWDQqGHlxVNww=="
|
"integrity": "sha512-pmMDBKiRVjh0uKK6CT1WqZmM3hBVSgD+N2MrgyV1uNizAZMw4tx6i/RTc+/uCsKSCmg0xXx7arCP/OFcIwTsiQ=="
|
||||||
},
|
},
|
||||||
"node_modules/diff": {
|
"node_modules/diff": {
|
||||||
"version": "5.1.0",
|
"version": "5.1.0",
|
||||||
|
|
@ -5617,9 +5607,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/json5": {
|
"node_modules/json5": {
|
||||||
"version": "2.2.1",
|
"version": "2.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||||
"integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
|
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"json5": "lib/cli.js"
|
"json5": "lib/cli.js"
|
||||||
|
|
@ -5851,12 +5841,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/magic-string": {
|
"node_modules/magic-string": {
|
||||||
"version": "0.26.7",
|
"version": "0.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.7.tgz",
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz",
|
||||||
"integrity": "sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==",
|
"integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"sourcemap-codec": "^1.4.8"
|
"@jridgewell/sourcemap-codec": "^1.4.13"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
|
|
@ -6049,8 +6039,7 @@
|
||||||
"node_modules/mitt": {
|
"node_modules/mitt": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz",
|
||||||
"integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==",
|
"integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/mkdirp-classic": {
|
"node_modules/mkdirp-classic": {
|
||||||
"version": "0.5.3",
|
"version": "0.5.3",
|
||||||
|
|
@ -7363,55 +7352,21 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "2.79.1",
|
"version": "3.12.1",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.12.1.tgz",
|
||||||
"integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==",
|
"integrity": "sha512-t9elERrz2i4UU9z7AwISj3CQcXP39cWxgRWLdf4Tm6aKm1eYrqHIgjzXBgb67GNY1sZckTFFi0oMozh3/S++Ig==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"rollup": "dist/bin/rollup"
|
"rollup": "dist/bin/rollup"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.0.0"
|
"node": ">=14.18.0",
|
||||||
|
"npm": ">=8.0.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"fsevents": "~2.3.2"
|
"fsevents": "~2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rollup-plugin-dts": {
|
|
||||||
"version": "4.2.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/rollup-plugin-dts/-/rollup-plugin-dts-4.2.2.tgz",
|
|
||||||
"integrity": "sha512-A3g6Rogyko/PXeKoUlkjxkP++8UDVpgA7C+Tdl77Xj4fgEaIjPSnxRmR53EzvoYy97VMVwLAOcWJudaVAuxneQ==",
|
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
|
||||||
"magic-string": "^0.26.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=v12.22.11"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/Swatinem"
|
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"@babel/code-frame": "^7.16.7"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"rollup": "^2.55",
|
|
||||||
"typescript": "^4.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/rollup-plugin-dts/node_modules/@babel/code-frame": {
|
|
||||||
"version": "7.18.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
|
|
||||||
"integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==",
|
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/highlight": "^7.18.6"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/run-async": {
|
"node_modules/run-async": {
|
||||||
"version": "2.4.1",
|
"version": "2.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
|
||||||
|
|
@ -8040,9 +7995,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/tsconfig-paths/node_modules/json5": {
|
"node_modules/tsconfig-paths/node_modules/json5": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
|
||||||
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
|
"integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"minimist": "^1.2.0"
|
"minimist": "^1.2.0"
|
||||||
|
|
@ -8192,7 +8147,7 @@
|
||||||
"version": "4.9.4",
|
"version": "4.9.4",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz",
|
||||||
"integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==",
|
"integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==",
|
||||||
"dev": true,
|
"devOptional": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
"tsserver": "bin/tsserver"
|
"tsserver": "bin/tsserver"
|
||||||
|
|
@ -8469,10 +8424,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/yargs": {
|
"node_modules/yargs": {
|
||||||
"version": "17.6.0",
|
"version": "17.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.0.tgz",
|
||||||
"integrity": "sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==",
|
"integrity": "sha512-dwqOPg5trmrre9+v8SUo2q/hAwyKoVfu8OC1xPHKJGNdxAvPl4sKxL4vBnh3bQz/ZvvGAFeA5H3ou2kcOY8sQQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cliui": "^8.0.1",
|
"cliui": "^8.0.1",
|
||||||
"escalade": "^3.1.1",
|
"escalade": "^3.1.1",
|
||||||
|
|
@ -8480,7 +8434,7 @@
|
||||||
"require-directory": "^2.1.1",
|
"require-directory": "^2.1.1",
|
||||||
"string-width": "^4.2.3",
|
"string-width": "^4.2.3",
|
||||||
"y18n": "^5.0.5",
|
"y18n": "^5.0.5",
|
||||||
"yargs-parser": "^21.0.0"
|
"yargs-parser": "^21.1.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
|
|
@ -8543,7 +8497,6 @@
|
||||||
"version": "21.1.1",
|
"version": "21.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
|
||||||
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
|
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
|
|
@ -8606,14 +8559,62 @@
|
||||||
"url": "https://github.com/sponsors/colinhacks"
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"packages/browsers": {
|
||||||
|
"name": "@puppeteer/browsers",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"debug": "4.3.4",
|
||||||
|
"extract-zip": "2.0.1",
|
||||||
|
"https-proxy-agent": "5.0.1",
|
||||||
|
"progress": "2.0.3",
|
||||||
|
"proxy-from-env": "1.1.0",
|
||||||
|
"tar-fs": "2.1.1",
|
||||||
|
"unbzip2-stream": "1.4.3",
|
||||||
|
"yargs": "17.7.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"browsers": "lib/cjs/browsers.js"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^14.15.0",
|
||||||
|
"@types/yargs": "17.0.22"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": ">= 4.7.4"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"packages/browsers/node_modules/@types/node": {
|
||||||
|
"version": "14.18.36",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.36.tgz",
|
||||||
|
"integrity": "sha512-FXKWbsJ6a1hIrRxv+FoukuHnGTgEzKYGi7kilfMae96AL9UNkPFNWJEEYWzdRI9ooIkbr4AKldyuSTLql06vLQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"packages/browsers/node_modules/@types/yargs": {
|
||||||
|
"version": "17.0.22",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz",
|
||||||
|
"integrity": "sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/yargs-parser": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"packages/ng-schematics": {
|
"packages/ng-schematics": {
|
||||||
"name": "@puppeteer/ng-schematics",
|
"name": "@puppeteer/ng-schematics",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular-devkit/architect": "^0.1501.2",
|
"@angular-devkit/architect": "^0.1501.6",
|
||||||
"@angular-devkit/core": "^15.1.2",
|
"@angular-devkit/core": "^15.1.6",
|
||||||
"@angular-devkit/schematics": "^15.1.2"
|
"@angular-devkit/schematics": "^15.1.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@schematics/angular": "^14.2.8",
|
"@schematics/angular": "^14.2.8",
|
||||||
|
|
@ -8623,37 +8624,12 @@
|
||||||
"node": ">=14.1.0"
|
"node": ">=14.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packages/ng-schematics/node_modules/@angular-devkit/core": {
|
|
||||||
"version": "15.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-15.1.2.tgz",
|
|
||||||
"integrity": "sha512-wkLZYvTZt30Ge6Z83Gxsr6mO1TIHCu3SImdE0zwW63EdU9o1NYkU74z1D9VUZ9Up7uHi1cHs/dssbxUuZ4eWOA==",
|
|
||||||
"dependencies": {
|
|
||||||
"ajv": "8.12.0",
|
|
||||||
"ajv-formats": "2.1.1",
|
|
||||||
"jsonc-parser": "3.2.0",
|
|
||||||
"rxjs": "6.6.7",
|
|
||||||
"source-map": "0.7.4"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^14.20.0 || ^16.13.0 || >=18.10.0",
|
|
||||||
"npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
|
|
||||||
"yarn": ">= 1.13.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"chokidar": "^3.5.2"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"chokidar": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"packages/ng-schematics/node_modules/@angular-devkit/schematics": {
|
"packages/ng-schematics/node_modules/@angular-devkit/schematics": {
|
||||||
"version": "15.1.2",
|
"version": "15.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-15.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-15.1.6.tgz",
|
||||||
"integrity": "sha512-HjJPm+4SS5TdAHHvdpXLv25wsvwVOn5RYs0A9MazTndlm80ct3PKeYUgakNDRFjRj8uORNlJMKmQIIhUSDjFsw==",
|
"integrity": "sha512-cwmJFpS43zrdlmfwfHIxG/Nzg5rzFdtKrHx64ZXxNFm6JdyK2JTs/qrHUwv1FYWAcqhdiHn+00jYklMmvsvPOA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular-devkit/core": "15.1.2",
|
"@angular-devkit/core": "15.1.6",
|
||||||
"jsonc-parser": "3.2.0",
|
"jsonc-parser": "3.2.0",
|
||||||
"magic-string": "0.27.0",
|
"magic-string": "0.27.0",
|
||||||
"ora": "5.4.1",
|
"ora": "5.4.1",
|
||||||
|
|
@ -8671,21 +8647,6 @@
|
||||||
"integrity": "sha512-qelS/Ra6sacc4loe/3MSjXNL1dNQ/GjxNHVzuChwMfmk7HuycRLVQN2qNY3XahK+fZc5E2szqQSKUyAF0E+2bg==",
|
"integrity": "sha512-qelS/Ra6sacc4loe/3MSjXNL1dNQ/GjxNHVzuChwMfmk7HuycRLVQN2qNY3XahK+fZc5E2szqQSKUyAF0E+2bg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"packages/ng-schematics/node_modules/ajv": {
|
|
||||||
"version": "8.12.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
|
|
||||||
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
|
|
||||||
"dependencies": {
|
|
||||||
"fast-deep-equal": "^3.1.1",
|
|
||||||
"json-schema-traverse": "^1.0.0",
|
|
||||||
"require-from-string": "^2.0.2",
|
|
||||||
"uri-js": "^4.2.2"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/epoberezkin"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"packages/ng-schematics/node_modules/magic-string": {
|
"packages/ng-schematics/node_modules/magic-string": {
|
||||||
"version": "0.27.0",
|
"version": "0.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz",
|
||||||
|
|
@ -8697,16 +8658,8 @@
|
||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packages/ng-schematics/node_modules/source-map": {
|
|
||||||
"version": "0.7.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
|
|
||||||
"integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"packages/puppeteer": {
|
"packages/puppeteer": {
|
||||||
"version": "19.6.0",
|
"version": "19.7.2",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
@ -8714,19 +8667,20 @@
|
||||||
"https-proxy-agent": "5.0.1",
|
"https-proxy-agent": "5.0.1",
|
||||||
"progress": "2.0.3",
|
"progress": "2.0.3",
|
||||||
"proxy-from-env": "1.1.0",
|
"proxy-from-env": "1.1.0",
|
||||||
"puppeteer-core": "19.6.0"
|
"puppeteer-core": "19.7.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14.1.0"
|
"node": ">=14.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packages/puppeteer-core": {
|
"packages/puppeteer-core": {
|
||||||
"version": "19.6.0",
|
"version": "19.7.2",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"chromium-bidi": "0.4.4",
|
||||||
"cross-fetch": "3.1.5",
|
"cross-fetch": "3.1.5",
|
||||||
"debug": "4.3.4",
|
"debug": "4.3.4",
|
||||||
"devtools-protocol": "0.0.1082910",
|
"devtools-protocol": "0.0.1094867",
|
||||||
"extract-zip": "2.0.1",
|
"extract-zip": "2.0.1",
|
||||||
"https-proxy-agent": "5.0.1",
|
"https-proxy-agent": "5.0.1",
|
||||||
"proxy-from-env": "1.1.0",
|
"proxy-from-env": "1.1.0",
|
||||||
|
|
@ -8737,6 +8691,14 @@
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=14.1.0"
|
"node": ">=14.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": ">= 4.7.4"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packages/puppeteer/node_modules/argparse": {
|
"packages/puppeteer/node_modules/argparse": {
|
||||||
|
|
@ -8982,26 +8944,26 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@angular-devkit/architect": {
|
"@angular-devkit/architect": {
|
||||||
"version": "0.1501.2",
|
"version": "0.1501.6",
|
||||||
"resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1501.2.tgz",
|
"resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1501.6.tgz",
|
||||||
"integrity": "sha512-AfORVGLN0FBIUXO3FkfGOKu+Gz6oJjF8Bu8cPn27duiI0wszxGNY3fATKwbSg7JcKx1oQS/G7RjyC5OiTA6a0Q==",
|
"integrity": "sha512-u07zZFlfrg0Qn4mu5M9Nz0pH2Yd2028XF/73980PsZMxwkSm4diF08v4bHk3UyR7yPT7phwvt4znj6ryZhx1gw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@angular-devkit/core": "15.1.2",
|
"@angular-devkit/core": "15.1.6",
|
||||||
"rxjs": "6.6.7"
|
"rxjs": "6.6.7"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
|
||||||
"@angular-devkit/core": {
|
"@angular-devkit/core": {
|
||||||
"version": "15.1.2",
|
"version": "15.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-15.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-15.1.6.tgz",
|
||||||
"integrity": "sha512-wkLZYvTZt30Ge6Z83Gxsr6mO1TIHCu3SImdE0zwW63EdU9o1NYkU74z1D9VUZ9Up7uHi1cHs/dssbxUuZ4eWOA==",
|
"integrity": "sha512-jGgxyRjecVf6lEyqDxz7ltMEndNPxIg720pk6r40fgsu0dU8w9vjJSJe7k0XdJiXVRcN6wZa/J5nO/xcwWVIsA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"ajv": "8.12.0",
|
"ajv": "8.12.0",
|
||||||
"ajv-formats": "2.1.1",
|
"ajv-formats": "2.1.1",
|
||||||
"jsonc-parser": "3.2.0",
|
"jsonc-parser": "3.2.0",
|
||||||
"rxjs": "6.6.7",
|
"rxjs": "6.6.7",
|
||||||
"source-map": "0.7.4"
|
"source-map": "0.7.4"
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
"dependencies": {
|
||||||
"ajv": {
|
"ajv": {
|
||||||
"version": "8.12.0",
|
"version": "8.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
|
||||||
|
|
@ -10007,34 +9969,54 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@puppeteer/browsers": {
|
||||||
|
"version": "file:packages/browsers",
|
||||||
|
"requires": {
|
||||||
|
"@types/node": "^14.15.0",
|
||||||
|
"@types/yargs": "17.0.22",
|
||||||
|
"debug": "4.3.4",
|
||||||
|
"extract-zip": "2.0.1",
|
||||||
|
"https-proxy-agent": "5.0.1",
|
||||||
|
"progress": "2.0.3",
|
||||||
|
"proxy-from-env": "1.1.0",
|
||||||
|
"tar-fs": "2.1.1",
|
||||||
|
"unbzip2-stream": "1.4.3",
|
||||||
|
"yargs": "17.7.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": {
|
||||||
|
"version": "14.18.36",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.36.tgz",
|
||||||
|
"integrity": "sha512-FXKWbsJ6a1hIrRxv+FoukuHnGTgEzKYGi7kilfMae96AL9UNkPFNWJEEYWzdRI9ooIkbr4AKldyuSTLql06vLQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"@types/yargs": {
|
||||||
|
"version": "17.0.22",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.22.tgz",
|
||||||
|
"integrity": "sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g==",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"@types/yargs-parser": "*"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"@puppeteer/ng-schematics": {
|
"@puppeteer/ng-schematics": {
|
||||||
"version": "file:packages/ng-schematics",
|
"version": "file:packages/ng-schematics",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@angular-devkit/architect": "^0.1501.2",
|
"@angular-devkit/architect": "^0.1501.6",
|
||||||
"@angular-devkit/core": "^15.1.2",
|
"@angular-devkit/core": "^15.1.6",
|
||||||
"@angular-devkit/schematics": "^15.1.2",
|
"@angular-devkit/schematics": "^15.1.6",
|
||||||
"@schematics/angular": "^14.2.8",
|
"@schematics/angular": "^14.2.8",
|
||||||
"@types/node": "^14.15.0"
|
"@types/node": "^14.15.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular-devkit/core": {
|
|
||||||
"version": "15.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-15.1.2.tgz",
|
|
||||||
"integrity": "sha512-wkLZYvTZt30Ge6Z83Gxsr6mO1TIHCu3SImdE0zwW63EdU9o1NYkU74z1D9VUZ9Up7uHi1cHs/dssbxUuZ4eWOA==",
|
|
||||||
"requires": {
|
|
||||||
"ajv": "8.12.0",
|
|
||||||
"ajv-formats": "2.1.1",
|
|
||||||
"jsonc-parser": "3.2.0",
|
|
||||||
"rxjs": "6.6.7",
|
|
||||||
"source-map": "0.7.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@angular-devkit/schematics": {
|
"@angular-devkit/schematics": {
|
||||||
"version": "15.1.2",
|
"version": "15.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-15.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-15.1.6.tgz",
|
||||||
"integrity": "sha512-HjJPm+4SS5TdAHHvdpXLv25wsvwVOn5RYs0A9MazTndlm80ct3PKeYUgakNDRFjRj8uORNlJMKmQIIhUSDjFsw==",
|
"integrity": "sha512-cwmJFpS43zrdlmfwfHIxG/Nzg5rzFdtKrHx64ZXxNFm6JdyK2JTs/qrHUwv1FYWAcqhdiHn+00jYklMmvsvPOA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@angular-devkit/core": "15.1.2",
|
"@angular-devkit/core": "15.1.6",
|
||||||
"jsonc-parser": "3.2.0",
|
"jsonc-parser": "3.2.0",
|
||||||
"magic-string": "0.27.0",
|
"magic-string": "0.27.0",
|
||||||
"ora": "5.4.1",
|
"ora": "5.4.1",
|
||||||
|
|
@ -10047,17 +10029,6 @@
|
||||||
"integrity": "sha512-qelS/Ra6sacc4loe/3MSjXNL1dNQ/GjxNHVzuChwMfmk7HuycRLVQN2qNY3XahK+fZc5E2szqQSKUyAF0E+2bg==",
|
"integrity": "sha512-qelS/Ra6sacc4loe/3MSjXNL1dNQ/GjxNHVzuChwMfmk7HuycRLVQN2qNY3XahK+fZc5E2szqQSKUyAF0E+2bg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"ajv": {
|
|
||||||
"version": "8.12.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
|
|
||||||
"integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
|
|
||||||
"requires": {
|
|
||||||
"fast-deep-equal": "^3.1.1",
|
|
||||||
"json-schema-traverse": "^1.0.0",
|
|
||||||
"require-from-string": "^2.0.2",
|
|
||||||
"uri-js": "^4.2.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"magic-string": {
|
"magic-string": {
|
||||||
"version": "0.27.0",
|
"version": "0.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz",
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz",
|
||||||
|
|
@ -10065,18 +10036,13 @@
|
||||||
"requires": {
|
"requires": {
|
||||||
"@jridgewell/sourcemap-codec": "^1.4.13"
|
"@jridgewell/sourcemap-codec": "^1.4.13"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"source-map": {
|
|
||||||
"version": "0.7.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
|
|
||||||
"integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA=="
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@rollup/plugin-commonjs": {
|
"@rollup/plugin-commonjs": {
|
||||||
"version": "24.0.0",
|
"version": "24.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-24.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-24.0.1.tgz",
|
||||||
"integrity": "sha512-0w0wyykzdyRRPHOb0cQt14mIBLujfAv6GgP6g8nvg/iBxEm112t3YPPq+Buqe2+imvElTka+bjNlJ/gB56TD8g==",
|
"integrity": "sha512-15LsiWRZk4eOGqvrJyu3z3DaBu5BhXIMeWnijSRvd8irrrg9SHpQ1pH+BUK4H6Z9wL9yOxZJMTLU+Au86XHxow==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"@rollup/pluginutils": "^5.0.1",
|
"@rollup/pluginutils": "^5.0.1",
|
||||||
|
|
@ -10092,15 +10058,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
||||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
|
||||||
"magic-string": {
|
|
||||||
"version": "0.27.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz",
|
|
||||||
"integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"@jridgewell/sourcemap-codec": "^1.4.13"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -11056,11 +11013,12 @@
|
||||||
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
|
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
|
||||||
},
|
},
|
||||||
"chromium-bidi": {
|
"chromium-bidi": {
|
||||||
"version": "0.4.3",
|
"version": "0.4.4",
|
||||||
"resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.4.tgz",
|
||||||
"integrity": "sha512-A40H1rdpJqkTdnGhnYDzMhtDdIbkXNFj2wgIfivMXL7LyHFDmBtv1hdyycDhnxtYunbPLDZtTs/n+ZT5j7Vnew==",
|
"integrity": "sha512-4BX5cSaponuvVT1+SbLYTOAgDoVtX/Khoc9UsbFJ/AsPVUeFAM3RiIDFI6XFhLYMi9WmVJqh1ZH+dRpNKkKwiQ==",
|
||||||
"dev": true,
|
"requires": {
|
||||||
"requires": {}
|
"mitt": "3.0.0"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"cli-cursor": {
|
"cli-cursor": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
|
|
@ -11085,7 +11043,6 @@
|
||||||
"version": "8.0.1",
|
"version": "8.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
||||||
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
|
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"string-width": "^4.2.0",
|
"string-width": "^4.2.0",
|
||||||
"strip-ansi": "^6.0.1",
|
"strip-ansi": "^6.0.1",
|
||||||
|
|
@ -11320,9 +11277,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"devtools-protocol": {
|
"devtools-protocol": {
|
||||||
"version": "0.0.1082910",
|
"version": "0.0.1094867",
|
||||||
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1082910.tgz",
|
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1094867.tgz",
|
||||||
"integrity": "sha512-RqoZ2GmqaNxyx+99L/RemY5CkwG9D0WEfOKxekwCRXOGrDCep62ngezEJUVMq6rISYQ+085fJnWDQqGHlxVNww=="
|
"integrity": "sha512-pmMDBKiRVjh0uKK6CT1WqZmM3hBVSgD+N2MrgyV1uNizAZMw4tx6i/RTc+/uCsKSCmg0xXx7arCP/OFcIwTsiQ=="
|
||||||
},
|
},
|
||||||
"diff": {
|
"diff": {
|
||||||
"version": "5.1.0",
|
"version": "5.1.0",
|
||||||
|
|
@ -13126,9 +13083,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"json5": {
|
"json5": {
|
||||||
"version": "2.2.1",
|
"version": "2.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||||
"integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
|
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"jsonc-parser": {
|
"jsonc-parser": {
|
||||||
|
|
@ -13318,12 +13275,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"magic-string": {
|
"magic-string": {
|
||||||
"version": "0.26.7",
|
"version": "0.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.7.tgz",
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz",
|
||||||
"integrity": "sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==",
|
"integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"sourcemap-codec": "^1.4.8"
|
"@jridgewell/sourcemap-codec": "^1.4.13"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"make-dir": {
|
"make-dir": {
|
||||||
|
|
@ -13460,8 +13417,7 @@
|
||||||
"mitt": {
|
"mitt": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz",
|
||||||
"integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==",
|
"integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"mkdirp-classic": {
|
"mkdirp-classic": {
|
||||||
"version": "0.5.3",
|
"version": "0.5.3",
|
||||||
|
|
@ -14130,7 +14086,7 @@
|
||||||
"https-proxy-agent": "5.0.1",
|
"https-proxy-agent": "5.0.1",
|
||||||
"progress": "2.0.3",
|
"progress": "2.0.3",
|
||||||
"proxy-from-env": "1.1.0",
|
"proxy-from-env": "1.1.0",
|
||||||
"puppeteer-core": "19.6.0"
|
"puppeteer-core": "19.7.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"argparse": {
|
"argparse": {
|
||||||
|
|
@ -14162,9 +14118,10 @@
|
||||||
"puppeteer-core": {
|
"puppeteer-core": {
|
||||||
"version": "file:packages/puppeteer-core",
|
"version": "file:packages/puppeteer-core",
|
||||||
"requires": {
|
"requires": {
|
||||||
|
"chromium-bidi": "0.4.4",
|
||||||
"cross-fetch": "3.1.5",
|
"cross-fetch": "3.1.5",
|
||||||
"debug": "4.3.4",
|
"debug": "4.3.4",
|
||||||
"devtools-protocol": "0.0.1082910",
|
"devtools-protocol": "0.0.1094867",
|
||||||
"extract-zip": "2.0.1",
|
"extract-zip": "2.0.1",
|
||||||
"https-proxy-agent": "5.0.1",
|
"https-proxy-agent": "5.0.1",
|
||||||
"proxy-from-env": "1.1.0",
|
"proxy-from-env": "1.1.0",
|
||||||
|
|
@ -14482,36 +14439,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rollup": {
|
"rollup": {
|
||||||
"version": "2.79.1",
|
"version": "3.12.1",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.12.1.tgz",
|
||||||
"integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==",
|
"integrity": "sha512-t9elERrz2i4UU9z7AwISj3CQcXP39cWxgRWLdf4Tm6aKm1eYrqHIgjzXBgb67GNY1sZckTFFi0oMozh3/S++Ig==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"fsevents": "~2.3.2"
|
"fsevents": "~2.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rollup-plugin-dts": {
|
|
||||||
"version": "4.2.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/rollup-plugin-dts/-/rollup-plugin-dts-4.2.2.tgz",
|
|
||||||
"integrity": "sha512-A3g6Rogyko/PXeKoUlkjxkP++8UDVpgA7C+Tdl77Xj4fgEaIjPSnxRmR53EzvoYy97VMVwLAOcWJudaVAuxneQ==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"@babel/code-frame": "^7.16.7",
|
|
||||||
"magic-string": "^0.26.1"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/code-frame": {
|
|
||||||
"version": "7.18.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
|
|
||||||
"integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==",
|
|
||||||
"dev": true,
|
|
||||||
"optional": true,
|
|
||||||
"requires": {
|
|
||||||
"@babel/highlight": "^7.18.6"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"run-async": {
|
"run-async": {
|
||||||
"version": "2.4.1",
|
"version": "2.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
|
||||||
|
|
@ -14981,9 +14916,9 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"json5": {
|
"json5": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
|
||||||
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
|
"integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"minimist": "^1.2.0"
|
"minimist": "^1.2.0"
|
||||||
|
|
@ -15090,7 +15025,7 @@
|
||||||
"version": "4.9.4",
|
"version": "4.9.4",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz",
|
||||||
"integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==",
|
"integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==",
|
||||||
"dev": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
"unbox-primitive": {
|
"unbox-primitive": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
|
|
@ -15300,10 +15235,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"yargs": {
|
"yargs": {
|
||||||
"version": "17.6.0",
|
"version": "17.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.0.tgz",
|
||||||
"integrity": "sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==",
|
"integrity": "sha512-dwqOPg5trmrre9+v8SUo2q/hAwyKoVfu8OC1xPHKJGNdxAvPl4sKxL4vBnh3bQz/ZvvGAFeA5H3ou2kcOY8sQQ==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"cliui": "^8.0.1",
|
"cliui": "^8.0.1",
|
||||||
"escalade": "^3.1.1",
|
"escalade": "^3.1.1",
|
||||||
|
|
@ -15311,14 +15245,13 @@
|
||||||
"require-directory": "^2.1.1",
|
"require-directory": "^2.1.1",
|
||||||
"string-width": "^4.2.3",
|
"string-width": "^4.2.3",
|
||||||
"y18n": "^5.0.5",
|
"y18n": "^5.0.5",
|
||||||
"yargs-parser": "^21.0.0"
|
"yargs-parser": "^21.1.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"yargs-parser": {
|
"yargs-parser": {
|
||||||
"version": "21.1.1",
|
"version": "21.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
|
||||||
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
|
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="
|
||||||
"dev": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -7,13 +7,14 @@
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"bisect": "tsx tools/bisect.ts",
|
"bisect": "tsx tools/bisect.ts",
|
||||||
"build": "npm run build --workspaces --if-present",
|
"build": "wireit",
|
||||||
|
"build:docs": "wireit",
|
||||||
"check:pinned-deps": "tsx tools/ensure-pinned-deps",
|
"check:pinned-deps": "tsx tools/ensure-pinned-deps",
|
||||||
"check": "npm run check --workspaces --if-present && run-p check:*",
|
"check": "npm run check --workspaces --if-present && run-p check:*",
|
||||||
"clean": "npm run clean --workspaces --if-present && rimraf **/.wireit",
|
"clean": "rimraf **/.wireit && npm run clean --workspaces --if-present",
|
||||||
"commitlint": "commitlint --from=HEAD~1",
|
"commitlint": "commitlint --from=HEAD~1",
|
||||||
"debug": "mocha --inspect-brk",
|
"debug": "mocha --inspect-brk",
|
||||||
"docs": "run-s build generate:markdown",
|
"docs": "run-s build:docs generate:markdown",
|
||||||
"format:eslint": "eslint --ext js --ext ts --fix .",
|
"format:eslint": "eslint --ext js --ext ts --fix .",
|
||||||
"format:prettier": "prettier --write .",
|
"format:prettier": "prettier --write .",
|
||||||
"format": "run-s format:*",
|
"format": "run-s format:*",
|
||||||
|
|
@ -25,17 +26,93 @@
|
||||||
"prepare": "husky install",
|
"prepare": "husky install",
|
||||||
"test-install": "npm run test --workspace @puppeteer-test/installation",
|
"test-install": "npm run test --workspace @puppeteer-test/installation",
|
||||||
"test-types": "tsd -t packages/puppeteer",
|
"test-types": "tsd -t packages/puppeteer",
|
||||||
"test:chrome:headful": "npm test -- --test-suite chrome-headful",
|
"test:chrome:headful": "wireit",
|
||||||
"test:chrome:headless-chrome": "npm test -- --test-suite chrome-new-headless",
|
"test:chrome:new-headless": "wireit",
|
||||||
"test:chrome:headless": "npm test -- --test-suite chrome-headless",
|
"test:chrome:headless": "wireit",
|
||||||
"test:chrome:bidi": "npm test -- --test-suite chrome-bidi",
|
"test:chrome:bidi": "wireit",
|
||||||
"test:chrome": "run-s test:chrome:*",
|
"test:chrome": "wireit",
|
||||||
"test:firefox:bidi": "npm test -- --test-suite firefox-bidi",
|
"test:firefox:bidi": "wireit",
|
||||||
"test:firefox:headful": "npm test -- --test-suite firefox-headful",
|
"test:firefox:headful": "wireit",
|
||||||
"test:firefox:headless": "npm test -- --test-suite firefox-headless",
|
"test:firefox:headless": "wireit",
|
||||||
"test:firefox": "run-s test:firefox:*",
|
"test:firefox": "wireit",
|
||||||
"test": "cross-env PUPPETEER_DEFERRED_PROMISE_DEBUG_TIMEOUT=20000 node tools/mochaRunner/lib/main.js"
|
"test": "cross-env PUPPETEER_DEFERRED_PROMISE_DEBUG_TIMEOUT=20000 node tools/mochaRunner/lib/main.js"
|
||||||
},
|
},
|
||||||
|
"wireit": {
|
||||||
|
"build": {
|
||||||
|
"dependencies": [
|
||||||
|
"./packages/browsers:build",
|
||||||
|
"./packages/ng-schematics:build",
|
||||||
|
"./packages/puppeteer-core:build",
|
||||||
|
"./packages/puppeteer:build",
|
||||||
|
"./packages/testserver:build",
|
||||||
|
"./test:build",
|
||||||
|
"./test/installation:build"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"build:docs": {
|
||||||
|
"dependencies": [
|
||||||
|
"./packages/puppeteer:build:docs",
|
||||||
|
"./packages/puppeteer-core:build:docs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"test:chrome:headful": {
|
||||||
|
"command": "npm test -- --test-suite chrome-headful",
|
||||||
|
"dependencies": [
|
||||||
|
"./test:build"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"test:chrome:headless": {
|
||||||
|
"command": "npm test -- --test-suite chrome-headless",
|
||||||
|
"dependencies": [
|
||||||
|
"./test:build"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"test:chrome:new-headless": {
|
||||||
|
"command": "npm test -- --test-suite chrome-new-headless",
|
||||||
|
"dependencies": [
|
||||||
|
"./test:build"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"test:chrome:bidi": {
|
||||||
|
"command": "npm test -- --test-suite chrome-bidi",
|
||||||
|
"dependencies": [
|
||||||
|
"./test:build"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"test:firefox:headful": {
|
||||||
|
"command": "npm test -- --test-suite firefox-headful",
|
||||||
|
"dependencies": [
|
||||||
|
"./test:build"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"test:firefox:headless": {
|
||||||
|
"command": "npm test -- --test-suite firefox-headless",
|
||||||
|
"dependencies": [
|
||||||
|
"./test:build"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"test:firefox:bidi": {
|
||||||
|
"command": "npm test -- --test-suite firefox-bidi",
|
||||||
|
"dependencies": [
|
||||||
|
"./test:build"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"test:chrome": {
|
||||||
|
"dependencies": [
|
||||||
|
"test:chrome:headful",
|
||||||
|
"test:chrome:headless",
|
||||||
|
"test:chrome:new-headless",
|
||||||
|
"test:chrome:bidi"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"test:firefox": {
|
||||||
|
"dependencies": [
|
||||||
|
"test:firefox:headful",
|
||||||
|
"test:firefox:headless",
|
||||||
|
"test:firefox:bidi"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@actions/core": "1.10.0",
|
"@actions/core": "1.10.0",
|
||||||
"@commitlint/cli": "17.3.0",
|
"@commitlint/cli": "17.3.0",
|
||||||
|
|
@ -44,7 +121,7 @@
|
||||||
"@microsoft/api-extractor": "7.33.7",
|
"@microsoft/api-extractor": "7.33.7",
|
||||||
"@microsoft/api-extractor-model": "7.25.3",
|
"@microsoft/api-extractor-model": "7.25.3",
|
||||||
"@pptr/testserver": "file:packages/testserver",
|
"@pptr/testserver": "file:packages/testserver",
|
||||||
"@rollup/plugin-commonjs": "24.0.0",
|
"@rollup/plugin-commonjs": "24.0.1",
|
||||||
"@rollup/plugin-node-resolve": "15.0.1",
|
"@rollup/plugin-node-resolve": "15.0.1",
|
||||||
"@types/debug": "4.1.7",
|
"@types/debug": "4.1.7",
|
||||||
"@types/diff": "5.0.2",
|
"@types/diff": "5.0.2",
|
||||||
|
|
@ -65,7 +142,6 @@
|
||||||
"@typescript-eslint/eslint-plugin": "5.46.1",
|
"@typescript-eslint/eslint-plugin": "5.46.1",
|
||||||
"@typescript-eslint/parser": "5.46.1",
|
"@typescript-eslint/parser": "5.46.1",
|
||||||
"c8": "7.12.0",
|
"c8": "7.12.0",
|
||||||
"chromium-bidi": "0.4.3",
|
|
||||||
"commonmark": "0.30.0",
|
"commonmark": "0.30.0",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"diff": "5.1.0",
|
"diff": "5.1.0",
|
||||||
|
|
@ -95,8 +171,7 @@
|
||||||
"pngjs": "6.0.0",
|
"pngjs": "6.0.0",
|
||||||
"prettier": "2.8.1",
|
"prettier": "2.8.1",
|
||||||
"puppeteer": "file:packages/puppeteer",
|
"puppeteer": "file:packages/puppeteer",
|
||||||
"rollup": "2.79.1",
|
"rollup": "3.12.1",
|
||||||
"rollup-plugin-dts": "4.2.2",
|
|
||||||
"semver": "7.3.8",
|
"semver": "7.3.8",
|
||||||
"sinon": "15.0.1",
|
"sinon": "15.0.1",
|
||||||
"source-map-support": "0.5.21",
|
"source-map-support": "0.5.21",
|
||||||
|
|
|
||||||
6
remote/test/puppeteer/packages/browsers/.mocharc.cjs
Normal file
6
remote/test/puppeteer/packages/browsers/.mocharc.cjs
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
module.exports = {
|
||||||
|
logLevel: 'debug',
|
||||||
|
spec: 'test/build/**/*.spec.js',
|
||||||
|
exit: !!process.env.CI,
|
||||||
|
reporter: 'spec',
|
||||||
|
};
|
||||||
3
remote/test/puppeteer/packages/browsers/README.md
Normal file
3
remote/test/puppeteer/packages/browsers/README.md
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# @puppeteer/browsers
|
||||||
|
|
||||||
|
TODO
|
||||||
87
remote/test/puppeteer/packages/browsers/package.json
Normal file
87
remote/test/puppeteer/packages/browsers/package.json
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
{
|
||||||
|
"name": "@puppeteer/browsers",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "Download and launch browsers",
|
||||||
|
"scripts": {
|
||||||
|
"build": "wireit",
|
||||||
|
"build:test": "wireit",
|
||||||
|
"clean": "tsc --build --clean && rimraf lib",
|
||||||
|
"test": "wireit"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"@puppeteer/browsers": "lib/cjs/browsers.js"
|
||||||
|
},
|
||||||
|
"wireit": {
|
||||||
|
"build": {
|
||||||
|
"command": "tsc -b",
|
||||||
|
"files": [
|
||||||
|
"src/**/*.ts",
|
||||||
|
"tsconfig.json"
|
||||||
|
],
|
||||||
|
"output": [
|
||||||
|
"lib/**"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"build:test": {
|
||||||
|
"command": "tsc -b test/src/tsconfig.json",
|
||||||
|
"files": [
|
||||||
|
"test/**/*.ts",
|
||||||
|
"test/src/tsconfig.json"
|
||||||
|
],
|
||||||
|
"output": [
|
||||||
|
"test/build/**"
|
||||||
|
],
|
||||||
|
"dependencies": [
|
||||||
|
"build"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"test": {
|
||||||
|
"command": "mocha",
|
||||||
|
"files": [
|
||||||
|
".mocharc.cjs"
|
||||||
|
],
|
||||||
|
"dependencies": [
|
||||||
|
"build:test"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"puppeteer",
|
||||||
|
"browsers"
|
||||||
|
],
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/puppeteer/puppeteer/tree/main/packages/browsers"
|
||||||
|
},
|
||||||
|
"author": "The Chromium Authors",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.1.0"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"lib",
|
||||||
|
"!*.tsbuildinfo"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"debug": "4.3.4",
|
||||||
|
"extract-zip": "2.0.1",
|
||||||
|
"https-proxy-agent": "5.0.1",
|
||||||
|
"progress": "2.0.3",
|
||||||
|
"proxy-from-env": "1.1.0",
|
||||||
|
"tar-fs": "2.1.1",
|
||||||
|
"unbzip2-stream": "1.4.3",
|
||||||
|
"yargs": "17.7.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^14.15.0",
|
||||||
|
"@types/yargs": "17.0.22"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": ">= 4.7.4"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
176
remote/test/puppeteer/packages/browsers/src/CLI.ts
Normal file
176
remote/test/puppeteer/packages/browsers/src/CLI.ts
Normal file
|
|
@ -0,0 +1,176 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2023 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import ProgressBar from 'progress';
|
||||||
|
import yargs from 'yargs';
|
||||||
|
import {hideBin} from 'yargs/helpers';
|
||||||
|
|
||||||
|
import {Browser, BrowserPlatform} from './browsers/types.js';
|
||||||
|
import {fetch} from './fetch.js';
|
||||||
|
import {computeExecutablePath, launch} from './launcher.js';
|
||||||
|
|
||||||
|
type InstallArgs = {
|
||||||
|
browser: {
|
||||||
|
name: Browser;
|
||||||
|
revision: string;
|
||||||
|
};
|
||||||
|
path?: string;
|
||||||
|
platform?: BrowserPlatform;
|
||||||
|
};
|
||||||
|
|
||||||
|
type LaunchArgs = {
|
||||||
|
browser: {
|
||||||
|
name: Browser;
|
||||||
|
revision: string;
|
||||||
|
};
|
||||||
|
path?: string;
|
||||||
|
platform?: BrowserPlatform;
|
||||||
|
detached: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class CLI {
|
||||||
|
#cachePath;
|
||||||
|
|
||||||
|
constructor(cachePath = process.cwd()) {
|
||||||
|
this.#cachePath = cachePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
async run(argv: string[]): Promise<void> {
|
||||||
|
await yargs(hideBin(argv))
|
||||||
|
.command(
|
||||||
|
'install <browser>',
|
||||||
|
'Download and install the specified browser',
|
||||||
|
yargs => {
|
||||||
|
yargs.positional('browser', {
|
||||||
|
description: 'The browser version',
|
||||||
|
type: 'string',
|
||||||
|
coerce: (opt): InstallArgs['browser'] => {
|
||||||
|
return {
|
||||||
|
name: this.#parseBrowser(opt),
|
||||||
|
revision: this.#parseRevision(opt),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async argv => {
|
||||||
|
const args = argv as unknown as InstallArgs;
|
||||||
|
await fetch({
|
||||||
|
browser: args.browser.name,
|
||||||
|
revision: args.browser.revision,
|
||||||
|
platform: args.platform,
|
||||||
|
cacheDir: args.path ?? this.#cachePath,
|
||||||
|
downloadProgressCallback: this.#makeProgressCallback(
|
||||||
|
args.browser.name,
|
||||||
|
args.browser.revision
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.option('path', {
|
||||||
|
type: 'string',
|
||||||
|
desc: 'Path where the browsers will be downloaded to and installed from',
|
||||||
|
default: process.cwd(),
|
||||||
|
})
|
||||||
|
.option('platform', {
|
||||||
|
type: 'string',
|
||||||
|
desc: 'Platform that the binary needs to be compatible with.',
|
||||||
|
choices: Object.values(BrowserPlatform),
|
||||||
|
defaultDescription: 'Auto-detected by default.',
|
||||||
|
})
|
||||||
|
.command(
|
||||||
|
'launch <browser>',
|
||||||
|
'Launch the specified browser',
|
||||||
|
yargs => {
|
||||||
|
yargs.positional('browser', {
|
||||||
|
description: 'The browser version',
|
||||||
|
type: 'string',
|
||||||
|
coerce: (opt): LaunchArgs['browser'] => {
|
||||||
|
return {
|
||||||
|
name: this.#parseBrowser(opt),
|
||||||
|
revision: this.#parseRevision(opt),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async argv => {
|
||||||
|
const args = argv as unknown as LaunchArgs;
|
||||||
|
const executablePath = computeExecutablePath({
|
||||||
|
browser: args.browser.name,
|
||||||
|
revision: args.browser.revision,
|
||||||
|
cacheDir: args.path ?? this.#cachePath,
|
||||||
|
platform: args.platform,
|
||||||
|
});
|
||||||
|
launch({
|
||||||
|
executablePath,
|
||||||
|
detached: args.detached,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.option('path', {
|
||||||
|
type: 'string',
|
||||||
|
desc: 'Path where the browsers will be downloaded to and installed from',
|
||||||
|
default: process.cwd(),
|
||||||
|
})
|
||||||
|
.option('detached', {
|
||||||
|
type: 'boolean',
|
||||||
|
desc: 'Whether to detach the child process.',
|
||||||
|
default: false,
|
||||||
|
})
|
||||||
|
.option('platform', {
|
||||||
|
type: 'string',
|
||||||
|
desc: 'Platform that the binary needs to be compatible with.',
|
||||||
|
choices: Object.values(BrowserPlatform),
|
||||||
|
defaultDescription: 'Auto-detected by default.',
|
||||||
|
})
|
||||||
|
.parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
#parseBrowser(version: string): Browser {
|
||||||
|
return version.split('@').shift() as Browser;
|
||||||
|
}
|
||||||
|
|
||||||
|
#parseRevision(version: string): string {
|
||||||
|
return version.split('@').pop() ?? 'latest';
|
||||||
|
}
|
||||||
|
|
||||||
|
#toMegabytes(bytes: number) {
|
||||||
|
const mb = bytes / 1024 / 1024;
|
||||||
|
return `${Math.round(mb * 10) / 10} Mb`;
|
||||||
|
}
|
||||||
|
|
||||||
|
#makeProgressCallback(browser: Browser, revision: string) {
|
||||||
|
let progressBar: ProgressBar;
|
||||||
|
let lastDownloadedBytes = 0;
|
||||||
|
return (downloadedBytes: number, totalBytes: number) => {
|
||||||
|
if (!progressBar) {
|
||||||
|
progressBar = new ProgressBar(
|
||||||
|
`Downloading ${browser} r${revision} - ${this.#toMegabytes(
|
||||||
|
totalBytes
|
||||||
|
)} [:bar] :percent :etas `,
|
||||||
|
{
|
||||||
|
complete: '=',
|
||||||
|
incomplete: ' ',
|
||||||
|
width: 20,
|
||||||
|
total: totalBytes,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const delta = downloadedBytes - lastDownloadedBytes;
|
||||||
|
lastDownloadedBytes = downloadedBytes;
|
||||||
|
progressBar.tick(delta);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2023 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
import {Browser, BrowserPlatform} from './browsers/types.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The cache used by Puppeteer relies on the following structure:
|
||||||
|
*
|
||||||
|
* - rootDir
|
||||||
|
* -- <browser1> | browserRoot(browser1)
|
||||||
|
* ---- <platform>-<revision> | installationDir()
|
||||||
|
* ------ the browser-platform-revision
|
||||||
|
* ------ specific structure.
|
||||||
|
* -- <browser2> | browserRoot(browser2)
|
||||||
|
* ---- <platform>-<revision> | installationDir()
|
||||||
|
* ------ the browser-platform-revision
|
||||||
|
* ------ specific structure.
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export class CacheStructure {
|
||||||
|
#rootDir: string;
|
||||||
|
|
||||||
|
constructor(rootDir: string) {
|
||||||
|
this.#rootDir = rootDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
browserRoot(browser: Browser): string {
|
||||||
|
return path.join(this.#rootDir, browser);
|
||||||
|
}
|
||||||
|
|
||||||
|
installationDir(
|
||||||
|
browser: Browser,
|
||||||
|
platform: BrowserPlatform,
|
||||||
|
revision: string
|
||||||
|
): string {
|
||||||
|
return path.join(this.browserRoot(browser), `${platform}-${revision}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copyright 2022 Google Inc. All rights reserved.
|
* Copyright 2023 Google Inc. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -14,5 +16,6 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export * as BidiMapper from 'chromium-bidi/lib/cjs/bidiMapper/bidiMapper.js';
|
import {CLI} from './CLI.js';
|
||||||
export * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js';
|
|
||||||
|
new CLI().run(process.argv);
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2023 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as chrome from './chrome.js';
|
||||||
|
import * as firefox from './firefox.js';
|
||||||
|
import {Browser, BrowserPlatform} from './types.js';
|
||||||
|
|
||||||
|
export const downloadUrls = {
|
||||||
|
[Browser.CHROME]: chrome.resolveDownloadUrl,
|
||||||
|
[Browser.FIREFOX]: firefox.resolveDownloadUrl,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const executablePathByBrowser = {
|
||||||
|
[Browser.CHROME]: chrome.relativeExecutablePath,
|
||||||
|
[Browser.FIREFOX]: firefox.relativeExecutablePath,
|
||||||
|
};
|
||||||
|
|
||||||
|
export {Browser, BrowserPlatform};
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2023 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
import {BrowserPlatform} from './types.js';
|
||||||
|
|
||||||
|
function archive(platform: BrowserPlatform, revision: string): string {
|
||||||
|
switch (platform) {
|
||||||
|
case BrowserPlatform.LINUX:
|
||||||
|
return 'chrome-linux';
|
||||||
|
case BrowserPlatform.MAC_ARM:
|
||||||
|
case BrowserPlatform.MAC:
|
||||||
|
return 'chrome-mac';
|
||||||
|
case BrowserPlatform.WIN32:
|
||||||
|
case BrowserPlatform.WIN64:
|
||||||
|
// Windows archive name changed at r591479.
|
||||||
|
return parseInt(revision, 10) > 591479 ? 'chrome-win' : 'chrome-win32';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function folder(platform: BrowserPlatform): string {
|
||||||
|
switch (platform) {
|
||||||
|
case BrowserPlatform.LINUX:
|
||||||
|
return 'Linux_x64';
|
||||||
|
case BrowserPlatform.MAC_ARM:
|
||||||
|
return 'Mac_Arm';
|
||||||
|
case BrowserPlatform.MAC:
|
||||||
|
return 'Mac';
|
||||||
|
case BrowserPlatform.WIN32:
|
||||||
|
return 'Win';
|
||||||
|
case BrowserPlatform.WIN64:
|
||||||
|
return 'Win_x64';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveDownloadUrl(
|
||||||
|
platform: BrowserPlatform,
|
||||||
|
revision: string,
|
||||||
|
baseUrl = 'https://storage.googleapis.com/chromium-browser-snapshots'
|
||||||
|
): string {
|
||||||
|
return `${baseUrl}/${folder(platform)}/${revision}/${archive(
|
||||||
|
platform,
|
||||||
|
revision
|
||||||
|
)}.zip`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function relativeExecutablePath(
|
||||||
|
platform: BrowserPlatform,
|
||||||
|
_revision: string
|
||||||
|
): string {
|
||||||
|
switch (platform) {
|
||||||
|
case BrowserPlatform.MAC:
|
||||||
|
case BrowserPlatform.MAC_ARM:
|
||||||
|
return path.join(
|
||||||
|
'chrome-mac',
|
||||||
|
'Chromium.app',
|
||||||
|
'Contents',
|
||||||
|
'MacOS',
|
||||||
|
'Chromium'
|
||||||
|
);
|
||||||
|
case BrowserPlatform.LINUX:
|
||||||
|
return path.join('chrome-linux', 'chrome');
|
||||||
|
case BrowserPlatform.WIN32:
|
||||||
|
case BrowserPlatform.WIN64:
|
||||||
|
return path.join('chrome-win', 'chrome.exe');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2023 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
import {BrowserPlatform} from './types.js';
|
||||||
|
|
||||||
|
function archive(platform: BrowserPlatform, revision: string): string {
|
||||||
|
switch (platform) {
|
||||||
|
case BrowserPlatform.LINUX:
|
||||||
|
return `firefox-${revision}.en-US.${platform}-x86_64.tar.bz2`;
|
||||||
|
case BrowserPlatform.MAC_ARM:
|
||||||
|
case BrowserPlatform.MAC:
|
||||||
|
return `firefox-${revision}.en-US.mac.dmg`;
|
||||||
|
case BrowserPlatform.WIN32:
|
||||||
|
case BrowserPlatform.WIN64:
|
||||||
|
return `firefox-${revision}.en-US.${platform}.zip`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveDownloadUrl(
|
||||||
|
platform: BrowserPlatform,
|
||||||
|
revision: string,
|
||||||
|
baseUrl = 'https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central'
|
||||||
|
): string {
|
||||||
|
return `${baseUrl}/${archive(platform, revision)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function relativeExecutablePath(
|
||||||
|
platform: BrowserPlatform,
|
||||||
|
_revision: string
|
||||||
|
): string {
|
||||||
|
switch (platform) {
|
||||||
|
case BrowserPlatform.MAC_ARM:
|
||||||
|
case BrowserPlatform.MAC:
|
||||||
|
return path.join('Firefox Nightly.app', 'Contents', 'MacOS', 'firefox');
|
||||||
|
case BrowserPlatform.LINUX:
|
||||||
|
return path.join('firefox', 'firefox');
|
||||||
|
case BrowserPlatform.WIN32:
|
||||||
|
case BrowserPlatform.WIN64:
|
||||||
|
return path.join('firefox', 'firefox.exe');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2023 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as chrome from './chrome.js';
|
||||||
|
import * as firefox from './firefox.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supported browsers.
|
||||||
|
*/
|
||||||
|
export enum Browser {
|
||||||
|
CHROME = 'chrome',
|
||||||
|
FIREFOX = 'firefox',
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Platform names used to identify a OS platfrom x architecture combination in the way
|
||||||
|
* that is relevant for the browser download.
|
||||||
|
*/
|
||||||
|
export enum BrowserPlatform {
|
||||||
|
LINUX = 'linux',
|
||||||
|
MAC = 'mac',
|
||||||
|
MAC_ARM = 'mac_arm',
|
||||||
|
WIN32 = 'win32',
|
||||||
|
WIN64 = 'win64',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const downloadUrls = {
|
||||||
|
[Browser.CHROME]: chrome.resolveDownloadUrl,
|
||||||
|
[Browser.FIREFOX]: firefox.resolveDownloadUrl,
|
||||||
|
};
|
||||||
19
remote/test/puppeteer/packages/browsers/src/debug.ts
Normal file
19
remote/test/puppeteer/packages/browsers/src/debug.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2023 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import debug from 'debug';
|
||||||
|
|
||||||
|
export {debug};
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2023 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import os from 'os';
|
||||||
|
|
||||||
|
import {BrowserPlatform} from './browsers/browsers.js';
|
||||||
|
|
||||||
|
export function detectBrowserPlatform(): BrowserPlatform | undefined {
|
||||||
|
const platform = os.platform();
|
||||||
|
switch (platform) {
|
||||||
|
case 'darwin':
|
||||||
|
return os.arch() === 'arm64'
|
||||||
|
? BrowserPlatform.MAC_ARM
|
||||||
|
: BrowserPlatform.MAC;
|
||||||
|
case 'linux':
|
||||||
|
return BrowserPlatform.LINUX;
|
||||||
|
case 'win32':
|
||||||
|
return os.arch() === 'x64' ||
|
||||||
|
// Windows 11 for ARM supports x64 emulation
|
||||||
|
(os.arch() === 'arm64' && isWindows11(os.release()))
|
||||||
|
? BrowserPlatform.WIN64
|
||||||
|
: BrowserPlatform.WIN32;
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Windows 11 is identified by the version 10.0.22000 or greater
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
function isWindows11(version: string): boolean {
|
||||||
|
const parts = version.split('.');
|
||||||
|
if (parts.length > 2) {
|
||||||
|
const major = parseInt(parts[0] as string, 10);
|
||||||
|
const minor = parseInt(parts[1] as string, 10);
|
||||||
|
const patch = parseInt(parts[2] as string, 10);
|
||||||
|
return (
|
||||||
|
major > 10 ||
|
||||||
|
(major === 10 && minor > 0) ||
|
||||||
|
(major === 10 && minor === 0 && patch >= 22000)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
140
remote/test/puppeteer/packages/browsers/src/fetch.ts
Normal file
140
remote/test/puppeteer/packages/browsers/src/fetch.ts
Normal file
|
|
@ -0,0 +1,140 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2017 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import assert from 'assert';
|
||||||
|
import {existsSync} from 'fs';
|
||||||
|
import {mkdir, unlink} from 'fs/promises';
|
||||||
|
import os from 'os';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
import {Browser, BrowserPlatform, downloadUrls} from './browsers/browsers.js';
|
||||||
|
import {CacheStructure} from './CacheStructure.js';
|
||||||
|
import {debug} from './debug.js';
|
||||||
|
import {detectBrowserPlatform} from './detectPlatform.js';
|
||||||
|
import {unpackArchive} from './fileUtil.js';
|
||||||
|
import {downloadFile, headHttpRequest} from './httpUtil.js';
|
||||||
|
|
||||||
|
const debugFetch = debug('puppeteer:browsers:fetcher');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface Options {
|
||||||
|
/**
|
||||||
|
* Determines the path to download browsers to.
|
||||||
|
*/
|
||||||
|
cacheDir: string;
|
||||||
|
/**
|
||||||
|
* Determines which platform the browser will be suited for.
|
||||||
|
*
|
||||||
|
* @defaultValue Auto-detected.
|
||||||
|
*/
|
||||||
|
platform?: BrowserPlatform;
|
||||||
|
/**
|
||||||
|
* Determines which browser to fetch.
|
||||||
|
*/
|
||||||
|
browser: Browser;
|
||||||
|
/**
|
||||||
|
* Determines which revision to dowloand. Revision should uniquely identify
|
||||||
|
* binaries and they are used for caching.
|
||||||
|
*/
|
||||||
|
revision: string;
|
||||||
|
/**
|
||||||
|
* Provides information about the progress of the download.
|
||||||
|
*/
|
||||||
|
downloadProgressCallback?: (
|
||||||
|
downloadedBytes: number,
|
||||||
|
totalBytes: number
|
||||||
|
) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type InstalledBrowser = {
|
||||||
|
path: string;
|
||||||
|
browser: Browser;
|
||||||
|
revision: string;
|
||||||
|
platform: BrowserPlatform;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function fetch(options: Options): Promise<InstalledBrowser> {
|
||||||
|
options.platform ??= detectBrowserPlatform();
|
||||||
|
if (!options.platform) {
|
||||||
|
throw new Error(
|
||||||
|
`Cannot download a binary for the provided platform: ${os.platform()} (${os.arch()})`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const url = getDownloadUrl(
|
||||||
|
options.browser,
|
||||||
|
options.platform,
|
||||||
|
options.revision
|
||||||
|
);
|
||||||
|
const fileName = url.toString().split('/').pop();
|
||||||
|
assert(fileName, `A malformed download URL was found: ${url}.`);
|
||||||
|
const structure = new CacheStructure(options.cacheDir);
|
||||||
|
const browserRoot = structure.browserRoot(options.browser);
|
||||||
|
const archivePath = path.join(browserRoot, fileName);
|
||||||
|
if (!existsSync(browserRoot)) {
|
||||||
|
await mkdir(browserRoot, {recursive: true});
|
||||||
|
}
|
||||||
|
const outputPath = structure.installationDir(
|
||||||
|
options.browser,
|
||||||
|
options.platform,
|
||||||
|
options.revision
|
||||||
|
);
|
||||||
|
if (existsSync(outputPath)) {
|
||||||
|
return {
|
||||||
|
path: outputPath,
|
||||||
|
browser: options.browser,
|
||||||
|
platform: options.platform,
|
||||||
|
revision: options.revision,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
debugFetch(`Downloading binary from ${url}`);
|
||||||
|
await downloadFile(url, archivePath, options.downloadProgressCallback);
|
||||||
|
debugFetch(`Installing ${archivePath} to ${outputPath}`);
|
||||||
|
await unpackArchive(archivePath, outputPath);
|
||||||
|
} finally {
|
||||||
|
if (existsSync(archivePath)) {
|
||||||
|
await unlink(archivePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
path: outputPath,
|
||||||
|
browser: options.browser,
|
||||||
|
platform: options.platform,
|
||||||
|
revision: options.revision,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function canFetch(options: Options): Promise<boolean> {
|
||||||
|
options.platform ??= detectBrowserPlatform();
|
||||||
|
if (!options.platform) {
|
||||||
|
throw new Error(
|
||||||
|
`Cannot download a binary for the provided platform: ${os.platform()} (${os.arch()})`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return await headHttpRequest(
|
||||||
|
getDownloadUrl(options.browser, options.platform, options.revision)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDownloadUrl(
|
||||||
|
browser: Browser,
|
||||||
|
platform: BrowserPlatform,
|
||||||
|
revision: string
|
||||||
|
): URL {
|
||||||
|
return new URL(downloadUrls[browser](platform, revision));
|
||||||
|
}
|
||||||
89
remote/test/puppeteer/packages/browsers/src/fileUtil.ts
Normal file
89
remote/test/puppeteer/packages/browsers/src/fileUtil.ts
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2023 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {exec as execChildProcess} from 'child_process';
|
||||||
|
import {createReadStream} from 'fs';
|
||||||
|
import {mkdir, readdir} from 'fs/promises';
|
||||||
|
import * as path from 'path';
|
||||||
|
import {promisify} from 'util';
|
||||||
|
|
||||||
|
import extractZip from 'extract-zip';
|
||||||
|
import tar from 'tar-fs';
|
||||||
|
import bzip from 'unbzip2-stream';
|
||||||
|
|
||||||
|
const exec = promisify(execChildProcess);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export async function unpackArchive(
|
||||||
|
archivePath: string,
|
||||||
|
folderPath: string
|
||||||
|
): Promise<void> {
|
||||||
|
if (archivePath.endsWith('.zip')) {
|
||||||
|
await extractZip(archivePath, {dir: folderPath});
|
||||||
|
} else if (archivePath.endsWith('.tar.bz2')) {
|
||||||
|
await extractTar(archivePath, folderPath);
|
||||||
|
} else if (archivePath.endsWith('.dmg')) {
|
||||||
|
await mkdir(folderPath);
|
||||||
|
await installDMG(archivePath, folderPath);
|
||||||
|
} else {
|
||||||
|
throw new Error(`Unsupported archive format: ${archivePath}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
function extractTar(tarPath: string, folderPath: string): Promise<void> {
|
||||||
|
return new Promise((fulfill, reject) => {
|
||||||
|
const tarStream = tar.extract(folderPath);
|
||||||
|
tarStream.on('error', reject);
|
||||||
|
tarStream.on('finish', fulfill);
|
||||||
|
const readStream = createReadStream(tarPath);
|
||||||
|
readStream.pipe(bzip()).pipe(tarStream);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
async function installDMG(dmgPath: string, folderPath: string): Promise<void> {
|
||||||
|
const {stdout} = await exec(
|
||||||
|
`hdiutil attach -nobrowse -noautoopen "${dmgPath}"`
|
||||||
|
);
|
||||||
|
|
||||||
|
const volumes = stdout.match(/\/Volumes\/(.*)/m);
|
||||||
|
if (!volumes) {
|
||||||
|
throw new Error(`Could not find volume path in ${stdout}`);
|
||||||
|
}
|
||||||
|
const mountPath = volumes[0]!;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const fileNames = await readdir(mountPath);
|
||||||
|
const appName = fileNames.find(item => {
|
||||||
|
return typeof item === 'string' && item.endsWith('.app');
|
||||||
|
});
|
||||||
|
if (!appName) {
|
||||||
|
throw new Error(`Cannot find app in ${mountPath}`);
|
||||||
|
}
|
||||||
|
const mountedPath = path.join(mountPath!, appName);
|
||||||
|
|
||||||
|
await exec(`cp -R "${mountedPath}" "${folderPath}"`);
|
||||||
|
} finally {
|
||||||
|
await exec(`hdiutil detach "${mountPath}" -quiet`);
|
||||||
|
}
|
||||||
|
}
|
||||||
139
remote/test/puppeteer/packages/browsers/src/httpUtil.ts
Normal file
139
remote/test/puppeteer/packages/browsers/src/httpUtil.ts
Normal file
|
|
@ -0,0 +1,139 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2023 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {createWriteStream} from 'fs';
|
||||||
|
import * as http from 'http';
|
||||||
|
import * as https from 'https';
|
||||||
|
import {URL} from 'url';
|
||||||
|
|
||||||
|
import createHttpsProxyAgent from 'https-proxy-agent';
|
||||||
|
import {getProxyForUrl} from 'proxy-from-env';
|
||||||
|
|
||||||
|
export function headHttpRequest(url: URL): Promise<boolean> {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const request = httpRequest(
|
||||||
|
url,
|
||||||
|
'HEAD',
|
||||||
|
response => {
|
||||||
|
resolve(response.statusCode === 200);
|
||||||
|
},
|
||||||
|
false
|
||||||
|
);
|
||||||
|
request.on('error', () => {
|
||||||
|
resolve(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function httpRequest(
|
||||||
|
url: URL,
|
||||||
|
method: string,
|
||||||
|
response: (x: http.IncomingMessage) => void,
|
||||||
|
keepAlive = true
|
||||||
|
): http.ClientRequest {
|
||||||
|
const options: http.RequestOptions = {
|
||||||
|
protocol: url.protocol,
|
||||||
|
hostname: url.hostname,
|
||||||
|
port: url.port,
|
||||||
|
path: url.pathname,
|
||||||
|
method,
|
||||||
|
headers: keepAlive ? {Connection: 'keep-alive'} : undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
const proxyURL = getProxyForUrl(url.toString());
|
||||||
|
if (proxyURL) {
|
||||||
|
const proxy = new URL(proxyURL);
|
||||||
|
if (proxy.protocol === 'http:') {
|
||||||
|
options.path = url.href;
|
||||||
|
options.hostname = proxy.hostname;
|
||||||
|
options.protocol = proxy.protocol;
|
||||||
|
options.port = proxy.port;
|
||||||
|
} else {
|
||||||
|
options.agent = createHttpsProxyAgent({
|
||||||
|
host: proxy.host,
|
||||||
|
path: proxy.pathname,
|
||||||
|
port: proxy.port,
|
||||||
|
secureProxy: proxy.protocol === 'https:',
|
||||||
|
headers: options.headers,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const requestCallback = (res: http.IncomingMessage): void => {
|
||||||
|
if (
|
||||||
|
res.statusCode &&
|
||||||
|
res.statusCode >= 300 &&
|
||||||
|
res.statusCode < 400 &&
|
||||||
|
res.headers.location
|
||||||
|
) {
|
||||||
|
httpRequest(new URL(res.headers.location), method, response);
|
||||||
|
} else {
|
||||||
|
response(res);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const request =
|
||||||
|
options.protocol === 'https:'
|
||||||
|
? https.request(options, requestCallback)
|
||||||
|
: http.request(options, requestCallback);
|
||||||
|
request.end();
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export function downloadFile(
|
||||||
|
url: URL,
|
||||||
|
destinationPath: string,
|
||||||
|
progressCallback?: (downloadedBytes: number, totalBytes: number) => void
|
||||||
|
): Promise<void> {
|
||||||
|
return new Promise<void>((resolve, reject) => {
|
||||||
|
let downloadedBytes = 0;
|
||||||
|
let totalBytes = 0;
|
||||||
|
|
||||||
|
function onData(chunk: string): void {
|
||||||
|
downloadedBytes += chunk.length;
|
||||||
|
progressCallback!(downloadedBytes, totalBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
const request = httpRequest(url, 'GET', response => {
|
||||||
|
if (response.statusCode !== 200) {
|
||||||
|
const error = new Error(
|
||||||
|
`Download failed: server returned code ${response.statusCode}. URL: ${url}`
|
||||||
|
);
|
||||||
|
// consume response data to free up memory
|
||||||
|
response.resume();
|
||||||
|
reject(error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const file = createWriteStream(destinationPath);
|
||||||
|
file.on('finish', () => {
|
||||||
|
return resolve();
|
||||||
|
});
|
||||||
|
file.on('error', error => {
|
||||||
|
return reject(error);
|
||||||
|
});
|
||||||
|
response.pipe(file);
|
||||||
|
totalBytes = parseInt(response.headers['content-length']!, 10);
|
||||||
|
if (progressCallback) {
|
||||||
|
response.on('data', onData);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
request.on('error', error => {
|
||||||
|
return reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
295
remote/test/puppeteer/packages/browsers/src/launcher.ts
Normal file
295
remote/test/puppeteer/packages/browsers/src/launcher.ts
Normal file
|
|
@ -0,0 +1,295 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2023 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import childProcess from 'child_process';
|
||||||
|
import os from 'os';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Browser,
|
||||||
|
BrowserPlatform,
|
||||||
|
executablePathByBrowser,
|
||||||
|
} from './browsers/browsers.js';
|
||||||
|
import {CacheStructure} from './CacheStructure.js';
|
||||||
|
import {debug} from './debug.js';
|
||||||
|
import {detectBrowserPlatform} from './detectPlatform.js';
|
||||||
|
|
||||||
|
const debugLaunch = debug('puppeteer:browsers:launcher');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface Options {
|
||||||
|
/**
|
||||||
|
* Root path to the storage directory.
|
||||||
|
*/
|
||||||
|
cacheDir: string;
|
||||||
|
/**
|
||||||
|
* Determines which platform the browser will be suited for.
|
||||||
|
*
|
||||||
|
* @defaultValue Auto-detected.
|
||||||
|
*/
|
||||||
|
platform?: BrowserPlatform;
|
||||||
|
/**
|
||||||
|
* Determines which browser to fetch.
|
||||||
|
*/
|
||||||
|
browser: Browser;
|
||||||
|
/**
|
||||||
|
* Determines which revision to dowloand. Revision should uniquely identify
|
||||||
|
* binaries and they are used for caching.
|
||||||
|
*/
|
||||||
|
revision: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function computeExecutablePath(options: Options): string {
|
||||||
|
options.platform ??= detectBrowserPlatform();
|
||||||
|
if (!options.platform) {
|
||||||
|
throw new Error(
|
||||||
|
`Cannot download a binary for the provided platform: ${os.platform()} (${os.arch()})`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const installationDir = new CacheStructure(options.cacheDir).installationDir(
|
||||||
|
options.browser,
|
||||||
|
options.platform,
|
||||||
|
options.revision
|
||||||
|
);
|
||||||
|
return path.join(
|
||||||
|
installationDir,
|
||||||
|
executablePathByBrowser[options.browser](options.platform, options.revision)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
type LaunchOptions = {
|
||||||
|
executablePath: string;
|
||||||
|
pipe?: boolean;
|
||||||
|
dumpio?: boolean;
|
||||||
|
args?: string[];
|
||||||
|
env?: Record<string, string>;
|
||||||
|
handleSIGINT?: boolean;
|
||||||
|
handleSIGTERM?: boolean;
|
||||||
|
handleSIGHUP?: boolean;
|
||||||
|
detached?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function launch(opts: LaunchOptions): Process {
|
||||||
|
return new Process(opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Process {
|
||||||
|
#executablePath;
|
||||||
|
#args: string[];
|
||||||
|
#browserProcess: childProcess.ChildProcess;
|
||||||
|
#exited = false;
|
||||||
|
#browserProcessExiting: Promise<void>;
|
||||||
|
|
||||||
|
constructor(opts: LaunchOptions) {
|
||||||
|
this.#executablePath = opts.executablePath;
|
||||||
|
this.#args = opts.args ?? [];
|
||||||
|
|
||||||
|
opts.pipe ??= false;
|
||||||
|
opts.dumpio ??= false;
|
||||||
|
opts.handleSIGINT ??= true;
|
||||||
|
opts.handleSIGTERM ??= true;
|
||||||
|
opts.handleSIGHUP ??= true;
|
||||||
|
opts.detached ??= true;
|
||||||
|
|
||||||
|
const stdio = this.#configureStdio({
|
||||||
|
pipe: opts.pipe,
|
||||||
|
dumpio: opts.dumpio,
|
||||||
|
});
|
||||||
|
|
||||||
|
debugLaunch(`Launching ${this.#executablePath} ${this.#args.join(' ')}`);
|
||||||
|
|
||||||
|
this.#browserProcess = childProcess.spawn(
|
||||||
|
this.#executablePath,
|
||||||
|
this.#args,
|
||||||
|
{
|
||||||
|
// On non-windows platforms, `detached: true` makes child process a
|
||||||
|
// leader of a new process group, making it possible to kill child
|
||||||
|
// process tree with `.kill(-pid)` command. @see
|
||||||
|
// https://nodejs.org/api/child_process.html#child_process_options_detached
|
||||||
|
detached: opts.detached,
|
||||||
|
env: opts.env,
|
||||||
|
stdio,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (opts.dumpio) {
|
||||||
|
this.#browserProcess.stderr?.pipe(process.stderr);
|
||||||
|
this.#browserProcess.stdout?.pipe(process.stdout);
|
||||||
|
}
|
||||||
|
process.on('exit', this.#onDriverProcessExit);
|
||||||
|
if (opts.handleSIGINT) {
|
||||||
|
process.on('SIGINT', this.#onDriverProcessSignal);
|
||||||
|
}
|
||||||
|
if (opts.handleSIGTERM) {
|
||||||
|
process.on('SIGTERM', this.#onDriverProcessSignal);
|
||||||
|
}
|
||||||
|
if (opts.handleSIGHUP) {
|
||||||
|
process.on('SIGHUP', this.#onDriverProcessSignal);
|
||||||
|
}
|
||||||
|
this.#browserProcessExiting = new Promise(resolve => {
|
||||||
|
this.#browserProcess.once('exit', () => {
|
||||||
|
this.#exited = true;
|
||||||
|
this.#clearListeners();
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#configureStdio(opts: {
|
||||||
|
pipe: boolean;
|
||||||
|
dumpio: boolean;
|
||||||
|
}): Array<'ignore' | 'pipe'> {
|
||||||
|
if (opts.pipe) {
|
||||||
|
if (opts.dumpio) {
|
||||||
|
return ['ignore', 'pipe', 'pipe', 'pipe', 'pipe'];
|
||||||
|
} else {
|
||||||
|
return ['ignore', 'ignore', 'ignore', 'pipe', 'pipe'];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (opts.dumpio) {
|
||||||
|
return ['pipe', 'pipe', 'pipe'];
|
||||||
|
} else {
|
||||||
|
return ['pipe', 'ignore', 'pipe'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#clearListeners(): void {
|
||||||
|
process.off('exit', this.#onDriverProcessExit);
|
||||||
|
process.off('SIGINT', this.#onDriverProcessSignal);
|
||||||
|
process.off('SIGTERM', this.#onDriverProcessSignal);
|
||||||
|
process.off('SIGHUP', this.#onDriverProcessSignal);
|
||||||
|
}
|
||||||
|
|
||||||
|
#onDriverProcessExit = (_code: number) => {
|
||||||
|
this.kill();
|
||||||
|
};
|
||||||
|
|
||||||
|
#onDriverProcessSignal = (signal: string): void => {
|
||||||
|
switch (signal) {
|
||||||
|
case 'SIGINT':
|
||||||
|
this.kill();
|
||||||
|
process.exit(130);
|
||||||
|
case 'SIGTERM':
|
||||||
|
case 'SIGUP':
|
||||||
|
this.kill();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
close(): Promise<void> {
|
||||||
|
if (this.#exited) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
this.kill();
|
||||||
|
return this.#browserProcessExiting;
|
||||||
|
}
|
||||||
|
|
||||||
|
kill(): void {
|
||||||
|
// If the process failed to launch (for example if the browser executable path
|
||||||
|
// is invalid), then the process does not get a pid assigned. A call to
|
||||||
|
// `proc.kill` would error, as the `pid` to-be-killed can not be found.
|
||||||
|
if (
|
||||||
|
this.#browserProcess &&
|
||||||
|
this.#browserProcess.pid &&
|
||||||
|
pidExists(this.#browserProcess.pid)
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
childProcess.exec(
|
||||||
|
`taskkill /pid ${this.#browserProcess.pid} /T /F`,
|
||||||
|
error => {
|
||||||
|
if (error) {
|
||||||
|
// taskkill can fail to kill the process e.g. due to missing permissions.
|
||||||
|
// Let's kill the process via Node API. This delays killing of all child
|
||||||
|
// processes of `this.proc` until the main Node.js process dies.
|
||||||
|
this.#browserProcess.kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// on linux the process group can be killed with the group id prefixed with
|
||||||
|
// a minus sign. The process group id is the group leader's pid.
|
||||||
|
const processGroupId = -this.#browserProcess.pid;
|
||||||
|
|
||||||
|
try {
|
||||||
|
process.kill(processGroupId, 'SIGKILL');
|
||||||
|
} catch (error) {
|
||||||
|
// Killing the process group can fail due e.g. to missing permissions.
|
||||||
|
// Let's kill the process via Node API. This delays killing of all child
|
||||||
|
// processes of `this.proc` until the main Node.js process dies.
|
||||||
|
this.#browserProcess.kill('SIGKILL');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(
|
||||||
|
`${PROCESS_ERROR_EXPLANATION}\nError cause: ${
|
||||||
|
isErrorLike(error) ? error.stack : error
|
||||||
|
}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.#clearListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const PROCESS_ERROR_EXPLANATION = `Puppeteer was unable to kill the process which ran the browser binary.
|
||||||
|
This means that, on future Puppeteer launches, Puppeteer might not be able to launch the browser.
|
||||||
|
Please check your open processes and ensure that the browser processes that Puppeteer launched have been killed.
|
||||||
|
If you think this is a bug, please report it on the Puppeteer issue tracker.`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
function pidExists(pid: number): boolean {
|
||||||
|
try {
|
||||||
|
return process.kill(pid, 0);
|
||||||
|
} catch (error) {
|
||||||
|
if (isErrnoException(error)) {
|
||||||
|
if (error.code && error.code === 'ESRCH') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export interface ErrorLike extends Error {
|
||||||
|
name: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export function isErrorLike(obj: unknown): obj is ErrorLike {
|
||||||
|
return (
|
||||||
|
typeof obj === 'object' && obj !== null && 'name' in obj && 'message' in obj
|
||||||
|
);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export function isErrnoException(obj: unknown): obj is NodeJS.ErrnoException {
|
||||||
|
return (
|
||||||
|
isErrorLike(obj) &&
|
||||||
|
('errno' in obj || 'code' in obj || 'path' in obj || 'syscall' in obj)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"extends": "../../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "CommonJS",
|
||||||
|
"outDir": "../lib/cjs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"extends": "../../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../lib/esm"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2023 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import assert from 'assert';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
import {BrowserPlatform} from '../../lib/cjs/browsers/browsers.js';
|
||||||
|
import {
|
||||||
|
resolveDownloadUrl,
|
||||||
|
relativeExecutablePath,
|
||||||
|
} from '../../lib/cjs/browsers/chrome.js';
|
||||||
|
|
||||||
|
describe('Chrome', () => {
|
||||||
|
it('should resolve download URLs', () => {
|
||||||
|
assert.strictEqual(
|
||||||
|
resolveDownloadUrl(BrowserPlatform.LINUX, '1083080'),
|
||||||
|
'https://storage.googleapis.com/chromium-browser-snapshots/Linux_x64/1083080/chrome-linux.zip'
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
resolveDownloadUrl(BrowserPlatform.MAC, '1083080'),
|
||||||
|
'https://storage.googleapis.com/chromium-browser-snapshots/Mac/1083080/chrome-mac.zip'
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
resolveDownloadUrl(BrowserPlatform.MAC_ARM, '1083080'),
|
||||||
|
'https://storage.googleapis.com/chromium-browser-snapshots/Mac_Arm/1083080/chrome-mac.zip'
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
resolveDownloadUrl(BrowserPlatform.WIN32, '1083080'),
|
||||||
|
'https://storage.googleapis.com/chromium-browser-snapshots/Win/1083080/chrome-win.zip'
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
resolveDownloadUrl(BrowserPlatform.WIN64, '1083080'),
|
||||||
|
'https://storage.googleapis.com/chromium-browser-snapshots/Win_x64/1083080/chrome-win.zip'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should resolve executable paths', () => {
|
||||||
|
assert.strictEqual(
|
||||||
|
relativeExecutablePath(BrowserPlatform.LINUX, '12372323'),
|
||||||
|
path.join('chrome-linux', 'chrome')
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
relativeExecutablePath(BrowserPlatform.MAC, '12372323'),
|
||||||
|
path.join('chrome-mac', 'Chromium.app', 'Contents', 'MacOS', 'Chromium')
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
relativeExecutablePath(BrowserPlatform.MAC_ARM, '12372323'),
|
||||||
|
path.join('chrome-mac', 'Chromium.app', 'Contents', 'MacOS', 'Chromium')
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
relativeExecutablePath(BrowserPlatform.WIN32, '12372323'),
|
||||||
|
path.join('chrome-win', 'chrome.exe')
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
relativeExecutablePath(BrowserPlatform.WIN64, '12372323'),
|
||||||
|
path.join('chrome-win', 'chrome.exe')
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
75
remote/test/puppeteer/packages/browsers/test/src/cli.spec.ts
Normal file
75
remote/test/puppeteer/packages/browsers/test/src/cli.spec.ts
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2023 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import assert from 'assert';
|
||||||
|
import fs from 'fs';
|
||||||
|
import os from 'os';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
import {CLI} from '../../lib/cjs/CLI.js';
|
||||||
|
|
||||||
|
describe('CLI', function () {
|
||||||
|
this.timeout(60000);
|
||||||
|
|
||||||
|
let tmpDir = '/tmp/puppeteer-browsers-test';
|
||||||
|
const testChromeRevision = '1083080';
|
||||||
|
const testFirefoxRevision = '111.0a1';
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'puppeteer-browsers-test'));
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
fs.rmSync(tmpDir, {recursive: true});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should download Chromium binaries', async () => {
|
||||||
|
await new CLI(tmpDir).run([
|
||||||
|
'npx',
|
||||||
|
'@puppeteer/browsers',
|
||||||
|
'install',
|
||||||
|
`chrome@${testChromeRevision}`,
|
||||||
|
`--path=${tmpDir}`,
|
||||||
|
'--platform=linux',
|
||||||
|
]);
|
||||||
|
assert.ok(
|
||||||
|
fs.existsSync(
|
||||||
|
path.join(
|
||||||
|
tmpDir,
|
||||||
|
'chrome',
|
||||||
|
`linux-${testChromeRevision}`,
|
||||||
|
'chrome-linux'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should download Firefox binaries', async () => {
|
||||||
|
await new CLI(tmpDir).run([
|
||||||
|
'npx',
|
||||||
|
'@puppeteer/browsers',
|
||||||
|
'install',
|
||||||
|
`firefox@${testFirefoxRevision}`,
|
||||||
|
`--path=${tmpDir}`,
|
||||||
|
'--platform=linux',
|
||||||
|
]);
|
||||||
|
assert.ok(
|
||||||
|
fs.existsSync(
|
||||||
|
path.join(tmpDir, 'firefox', `linux-${testFirefoxRevision}`, 'firefox')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
228
remote/test/puppeteer/packages/browsers/test/src/fetch.spec.ts
Normal file
228
remote/test/puppeteer/packages/browsers/test/src/fetch.spec.ts
Normal file
|
|
@ -0,0 +1,228 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2023 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import assert from 'assert';
|
||||||
|
import fs from 'fs';
|
||||||
|
import http from 'http';
|
||||||
|
import https from 'https';
|
||||||
|
import os from 'os';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
import {Browser, BrowserPlatform} from '../../lib/cjs/browsers/browsers.js';
|
||||||
|
import {fetch, canFetch} from '../../lib/cjs/fetch.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests in this spec use real download URLs and unpack live browser archives
|
||||||
|
* so it requires the network access.
|
||||||
|
*/
|
||||||
|
describe('fetch', () => {
|
||||||
|
let tmpDir = '/tmp/puppeteer-browsers-test';
|
||||||
|
const testChromeRevision = '1083080';
|
||||||
|
const testFirefoxRevision = '111.0a1';
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'puppeteer-browsers-test'));
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
fs.rmSync(tmpDir, {recursive: true});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check if a revision can be downloaded', async () => {
|
||||||
|
assert.ok(
|
||||||
|
await canFetch({
|
||||||
|
cacheDir: tmpDir,
|
||||||
|
browser: Browser.CHROME,
|
||||||
|
platform: BrowserPlatform.LINUX,
|
||||||
|
revision: testChromeRevision,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should report if a revision is not downloadable', async () => {
|
||||||
|
assert.strictEqual(
|
||||||
|
await canFetch({
|
||||||
|
cacheDir: tmpDir,
|
||||||
|
browser: Browser.CHROME,
|
||||||
|
platform: BrowserPlatform.LINUX,
|
||||||
|
revision: 'unknown',
|
||||||
|
}),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should download a revision that is a zip archive', async function () {
|
||||||
|
this.timeout(60000);
|
||||||
|
const expectedOutputPath = path.join(
|
||||||
|
tmpDir,
|
||||||
|
'chrome',
|
||||||
|
`${BrowserPlatform.LINUX}-${testChromeRevision}`
|
||||||
|
);
|
||||||
|
assert.strictEqual(fs.existsSync(expectedOutputPath), false);
|
||||||
|
let browser = await fetch({
|
||||||
|
cacheDir: tmpDir,
|
||||||
|
browser: Browser.CHROME,
|
||||||
|
platform: BrowserPlatform.LINUX,
|
||||||
|
revision: testChromeRevision,
|
||||||
|
});
|
||||||
|
assert.strictEqual(browser.path, expectedOutputPath);
|
||||||
|
assert.ok(fs.existsSync(expectedOutputPath));
|
||||||
|
// Second iteration should be no-op.
|
||||||
|
browser = await fetch({
|
||||||
|
cacheDir: tmpDir,
|
||||||
|
browser: Browser.CHROME,
|
||||||
|
platform: BrowserPlatform.LINUX,
|
||||||
|
revision: testChromeRevision,
|
||||||
|
});
|
||||||
|
assert.strictEqual(browser.path, expectedOutputPath);
|
||||||
|
assert.ok(fs.existsSync(expectedOutputPath));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should download a revision that is a bzip2 archive', async function () {
|
||||||
|
this.timeout(60000);
|
||||||
|
const expectedOutputPath = path.join(
|
||||||
|
tmpDir,
|
||||||
|
'firefox',
|
||||||
|
`${BrowserPlatform.LINUX}-${testFirefoxRevision}`
|
||||||
|
);
|
||||||
|
assert.strictEqual(fs.existsSync(expectedOutputPath), false);
|
||||||
|
const browser = await fetch({
|
||||||
|
cacheDir: tmpDir,
|
||||||
|
browser: Browser.FIREFOX,
|
||||||
|
platform: BrowserPlatform.LINUX,
|
||||||
|
revision: testFirefoxRevision,
|
||||||
|
});
|
||||||
|
assert.strictEqual(browser.path, expectedOutputPath);
|
||||||
|
assert.ok(fs.existsSync(expectedOutputPath));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fetch relies on the `hdiutil` utility on MacOS.
|
||||||
|
// The utility is not available on other platforms.
|
||||||
|
(os.platform() === 'darwin' ? it : it.skip)(
|
||||||
|
'should download a revision that is a dmg archive',
|
||||||
|
async function () {
|
||||||
|
this.timeout(120000);
|
||||||
|
const expectedOutputPath = path.join(
|
||||||
|
tmpDir,
|
||||||
|
'firefox',
|
||||||
|
`${BrowserPlatform.MAC}-${testFirefoxRevision}`
|
||||||
|
);
|
||||||
|
assert.strictEqual(fs.existsSync(expectedOutputPath), false);
|
||||||
|
const browser = await fetch({
|
||||||
|
cacheDir: tmpDir,
|
||||||
|
browser: Browser.FIREFOX,
|
||||||
|
platform: BrowserPlatform.MAC,
|
||||||
|
revision: testFirefoxRevision,
|
||||||
|
});
|
||||||
|
assert.strictEqual(browser.path, expectedOutputPath);
|
||||||
|
assert.ok(fs.existsSync(expectedOutputPath));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
describe('with proxy', () => {
|
||||||
|
const proxyUrl = new URL(`http://localhost:54321`);
|
||||||
|
let proxyServer: http.Server;
|
||||||
|
let proxiedRequestUrls: string[] = [];
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
proxiedRequestUrls = [];
|
||||||
|
proxyServer = http
|
||||||
|
.createServer(
|
||||||
|
(
|
||||||
|
originalRequest: http.IncomingMessage,
|
||||||
|
originalResponse: http.ServerResponse
|
||||||
|
) => {
|
||||||
|
const url = originalRequest.url as string;
|
||||||
|
const proxyRequest = (
|
||||||
|
url.startsWith('http:') ? http : https
|
||||||
|
).request(
|
||||||
|
url,
|
||||||
|
{
|
||||||
|
method: originalRequest.method,
|
||||||
|
rejectUnauthorized: false,
|
||||||
|
},
|
||||||
|
proxyResponse => {
|
||||||
|
originalResponse.writeHead(
|
||||||
|
proxyResponse.statusCode as number,
|
||||||
|
proxyResponse.headers
|
||||||
|
);
|
||||||
|
proxyResponse.pipe(originalResponse, {end: true});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
originalRequest.pipe(proxyRequest, {end: true});
|
||||||
|
proxiedRequestUrls.push(url);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.listen({
|
||||||
|
port: proxyUrl.port,
|
||||||
|
hostname: proxyUrl.hostname,
|
||||||
|
});
|
||||||
|
|
||||||
|
process.env['HTTPS_PROXY'] = proxyUrl.toString();
|
||||||
|
process.env['HTTP_PROXY'] = proxyUrl.toString();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
proxyServer.close(error => {
|
||||||
|
if (error) {
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
resolve(undefined);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
delete process.env['HTTP_PROXY'];
|
||||||
|
delete process.env['HTTPS_PROXY'];
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can send canFetch requests via a proxy', async () => {
|
||||||
|
assert.strictEqual(
|
||||||
|
await canFetch({
|
||||||
|
cacheDir: tmpDir,
|
||||||
|
browser: Browser.CHROME,
|
||||||
|
platform: BrowserPlatform.LINUX,
|
||||||
|
revision: testChromeRevision,
|
||||||
|
}),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
assert.deepStrictEqual(proxiedRequestUrls, [
|
||||||
|
'https://storage.googleapis.com/chromium-browser-snapshots/Linux_x64/1083080/chrome-linux.zip',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can fetch via a proxy', async function () {
|
||||||
|
this.timeout(60000);
|
||||||
|
const expectedOutputPath = path.join(
|
||||||
|
tmpDir,
|
||||||
|
'chrome',
|
||||||
|
`${BrowserPlatform.LINUX}-${testChromeRevision}`
|
||||||
|
);
|
||||||
|
assert.strictEqual(fs.existsSync(expectedOutputPath), false);
|
||||||
|
const browser = await fetch({
|
||||||
|
cacheDir: tmpDir,
|
||||||
|
browser: Browser.CHROME,
|
||||||
|
platform: BrowserPlatform.LINUX,
|
||||||
|
revision: testChromeRevision,
|
||||||
|
});
|
||||||
|
assert.strictEqual(browser.path, expectedOutputPath);
|
||||||
|
assert.ok(fs.existsSync(expectedOutputPath));
|
||||||
|
assert.deepStrictEqual(proxiedRequestUrls, [
|
||||||
|
'https://storage.googleapis.com/chromium-browser-snapshots/Linux_x64/1083080/chrome-linux.zip',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2023 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import assert from 'assert';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
import {BrowserPlatform} from '../../lib/cjs/browsers/browsers.js';
|
||||||
|
import {
|
||||||
|
relativeExecutablePath,
|
||||||
|
resolveDownloadUrl,
|
||||||
|
} from '../../lib/cjs/browsers/firefox.js';
|
||||||
|
|
||||||
|
describe('Firefox', () => {
|
||||||
|
it('should resolve download URLs', () => {
|
||||||
|
assert.strictEqual(
|
||||||
|
resolveDownloadUrl(BrowserPlatform.LINUX, '111.0a1'),
|
||||||
|
'https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central/firefox-111.0a1.en-US.linux-x86_64.tar.bz2'
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
resolveDownloadUrl(BrowserPlatform.MAC, '111.0a1'),
|
||||||
|
'https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central/firefox-111.0a1.en-US.mac.dmg'
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
resolveDownloadUrl(BrowserPlatform.MAC_ARM, '111.0a1'),
|
||||||
|
'https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central/firefox-111.0a1.en-US.mac.dmg'
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
resolveDownloadUrl(BrowserPlatform.WIN32, '111.0a1'),
|
||||||
|
'https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central/firefox-111.0a1.en-US.win32.zip'
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
resolveDownloadUrl(BrowserPlatform.WIN64, '111.0a1'),
|
||||||
|
'https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central/firefox-111.0a1.en-US.win64.zip'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should resolve executable paths', () => {
|
||||||
|
assert.strictEqual(
|
||||||
|
relativeExecutablePath(BrowserPlatform.LINUX, '111.0a1'),
|
||||||
|
path.join('firefox', 'firefox')
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
relativeExecutablePath(BrowserPlatform.MAC, '111.0a1'),
|
||||||
|
path.join('Firefox Nightly.app', 'Contents', 'MacOS', 'firefox')
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
relativeExecutablePath(BrowserPlatform.MAC_ARM, '111.0a1'),
|
||||||
|
path.join('Firefox Nightly.app', 'Contents', 'MacOS', 'firefox')
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
relativeExecutablePath(BrowserPlatform.WIN32, '111.0a1'),
|
||||||
|
path.join('firefox', 'firefox.exe')
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
relativeExecutablePath(BrowserPlatform.WIN64, '111.0a1'),
|
||||||
|
path.join('firefox', 'firefox.exe')
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,124 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2023 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import assert from 'assert';
|
||||||
|
import fs from 'fs';
|
||||||
|
import os from 'os';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
import {Browser, BrowserPlatform} from '../../lib/cjs/browsers/browsers.js';
|
||||||
|
import {fetch} from '../../lib/cjs/fetch.js';
|
||||||
|
import {computeExecutablePath, launch} from '../../lib/cjs/launcher.js';
|
||||||
|
|
||||||
|
describe('launcher', () => {
|
||||||
|
it('should compute executable path for Chrome', () => {
|
||||||
|
assert.strictEqual(
|
||||||
|
computeExecutablePath({
|
||||||
|
browser: Browser.CHROME,
|
||||||
|
platform: BrowserPlatform.LINUX,
|
||||||
|
revision: '123',
|
||||||
|
cacheDir: 'cache',
|
||||||
|
}),
|
||||||
|
path.join('cache', 'chrome', 'linux-123', 'chrome-linux', 'chrome')
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should compute executable path for Firefox', () => {
|
||||||
|
assert.strictEqual(
|
||||||
|
computeExecutablePath({
|
||||||
|
browser: Browser.FIREFOX,
|
||||||
|
platform: BrowserPlatform.LINUX,
|
||||||
|
revision: '123',
|
||||||
|
cacheDir: 'cache',
|
||||||
|
}),
|
||||||
|
path.join('cache', 'firefox', 'linux-123', 'firefox', 'firefox')
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Chrome', function () {
|
||||||
|
this.timeout(60000);
|
||||||
|
|
||||||
|
let tmpDir = '/tmp/puppeteer-browsers-test';
|
||||||
|
const testChromeRevision = '1083080';
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
tmpDir = fs.mkdtempSync(
|
||||||
|
path.join(os.tmpdir(), 'puppeteer-browsers-test')
|
||||||
|
);
|
||||||
|
await fetch({
|
||||||
|
cacheDir: tmpDir,
|
||||||
|
browser: Browser.CHROME,
|
||||||
|
revision: testChromeRevision,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
fs.rmSync(tmpDir, {recursive: true});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should launch a Chrome browser', async () => {
|
||||||
|
const executablePath = computeExecutablePath({
|
||||||
|
cacheDir: tmpDir,
|
||||||
|
browser: Browser.CHROME,
|
||||||
|
revision: testChromeRevision,
|
||||||
|
});
|
||||||
|
const process = launch({
|
||||||
|
executablePath,
|
||||||
|
args: [
|
||||||
|
'--use-mock-keychain',
|
||||||
|
'--disable-features=DialMediaRouteProvider',
|
||||||
|
`--user-data-dir=${path.join(tmpDir, 'profile')}`,
|
||||||
|
],
|
||||||
|
});
|
||||||
|
await process.close();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Firefox', function () {
|
||||||
|
this.timeout(60000);
|
||||||
|
|
||||||
|
let tmpDir = '/tmp/puppeteer-browsers-test';
|
||||||
|
const testFirefoxRevision = '111.0a1';
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
tmpDir = fs.mkdtempSync(
|
||||||
|
path.join(os.tmpdir(), 'puppeteer-browsers-test')
|
||||||
|
);
|
||||||
|
await fetch({
|
||||||
|
cacheDir: tmpDir,
|
||||||
|
browser: Browser.FIREFOX,
|
||||||
|
revision: testFirefoxRevision,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
fs.rmSync(tmpDir, {recursive: true});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should launch a Firefox browser', async () => {
|
||||||
|
const executablePath = computeExecutablePath({
|
||||||
|
cacheDir: tmpDir,
|
||||||
|
browser: Browser.FIREFOX,
|
||||||
|
revision: testFirefoxRevision,
|
||||||
|
});
|
||||||
|
const process = launch({
|
||||||
|
executablePath,
|
||||||
|
args: [`--user-data-dir=${path.join(tmpDir, 'profile')}`],
|
||||||
|
});
|
||||||
|
await process.close();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"extends": "../../../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "CommonJS",
|
||||||
|
"outDir": "../build"
|
||||||
|
},
|
||||||
|
"references": [{"path": "../../tsconfig.json"}]
|
||||||
|
}
|
||||||
8
remote/test/puppeteer/packages/browsers/tsconfig.json
Normal file
8
remote/test/puppeteer/packages/browsers/tsconfig.json
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"files": [],
|
||||||
|
"references": [
|
||||||
|
{"path": "src/tsconfig.esm.json"},
|
||||||
|
{"path": "src/tsconfig.cjs.json"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -3,18 +3,16 @@
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "Puppeteer Angular schematics",
|
"description": "Puppeteer Angular schematics",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "npm run build --watch",
|
"build:tsc": "wireit",
|
||||||
"dev:test": "npm run test --watch",
|
|
||||||
"copy": "wireit",
|
|
||||||
"build": "wireit",
|
"build": "wireit",
|
||||||
"clean": "tsc --build --clean && rimraf lib",
|
"clean": "tsc -b --clean && rimraf lib && rimraf test/build",
|
||||||
"clean:test": "rimraf test/build",
|
"dev:test": "npm run test --watch",
|
||||||
|
"dev": "npm run build --watch",
|
||||||
"test": "wireit"
|
"test": "wireit"
|
||||||
},
|
},
|
||||||
"wireit": {
|
"wireit": {
|
||||||
"copy": {
|
"build": {
|
||||||
"clean": "if-file-deleted",
|
"command": "node tools/copySchemaFiles.js",
|
||||||
"command": "node copySchemaFiles.js",
|
|
||||||
"files": [
|
"files": [
|
||||||
"src/**/files/**",
|
"src/**/files/**",
|
||||||
"src/**/*.json"
|
"src/**/*.json"
|
||||||
|
|
@ -24,29 +22,25 @@
|
||||||
"lib/**/*.json"
|
"lib/**/*.json"
|
||||||
],
|
],
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"clean"
|
"build:tsc"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"build": {
|
"build:tsc": {
|
||||||
"command": "tsc -b",
|
"command": "tsc -b",
|
||||||
|
"clean": "if-file-deleted",
|
||||||
"files": [
|
"files": [
|
||||||
"src/**/*.ts",
|
"**/tsconfig.*.json",
|
||||||
"!src/**/files",
|
"**/tsconfig.json",
|
||||||
"!src/**/*.json"
|
"src/**/*.ts"
|
||||||
],
|
],
|
||||||
"output": [
|
"output": [
|
||||||
"lib/**",
|
"lib/**/*.{ts,js}",
|
||||||
"!lib/**/files",
|
"lib/**/*.{ts,js}.map"
|
||||||
"!lib/**/*.json"
|
|
||||||
],
|
|
||||||
"dependencies": [
|
|
||||||
"copy"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"test": {
|
"test": {
|
||||||
"command": "mocha",
|
"command": "mocha",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"clean:test",
|
|
||||||
"build"
|
"build"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -66,9 +60,9 @@
|
||||||
"node": ">=14.1.0"
|
"node": ">=14.1.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular-devkit/architect": "^0.1501.2",
|
"@angular-devkit/architect": "^0.1501.6",
|
||||||
"@angular-devkit/core": "^15.1.2",
|
"@angular-devkit/core": "^15.1.6",
|
||||||
"@angular-devkit/schematics": "^15.1.2"
|
"@angular-devkit/schematics": "^15.1.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^14.15.0",
|
"@types/node": "^14.15.0",
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import {spawn} from 'child_process';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
createBuilder,
|
createBuilder,
|
||||||
BuilderContext,
|
BuilderContext,
|
||||||
|
|
@ -6,7 +8,6 @@ import {
|
||||||
BuilderRun,
|
BuilderRun,
|
||||||
} from '@angular-devkit/architect';
|
} from '@angular-devkit/architect';
|
||||||
import {JsonObject} from '@angular-devkit/core';
|
import {JsonObject} from '@angular-devkit/core';
|
||||||
import {spawn} from 'child_process';
|
|
||||||
|
|
||||||
import {PuppeteerBuilderOptions} from './types.js';
|
import {PuppeteerBuilderOptions} from './types.js';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,14 +16,15 @@
|
||||||
|
|
||||||
import {chain, Rule, SchematicContext, Tree} from '@angular-devkit/schematics';
|
import {chain, Rule, SchematicContext, Tree} from '@angular-devkit/schematics';
|
||||||
import {NodePackageInstallTask} from '@angular-devkit/schematics/tasks';
|
import {NodePackageInstallTask} from '@angular-devkit/schematics/tasks';
|
||||||
|
|
||||||
import {concatMap, map, scan} from 'rxjs/operators';
|
|
||||||
import {of} from 'rxjs';
|
import {of} from 'rxjs';
|
||||||
|
import {concatMap, map, scan} from 'rxjs/operators';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
addBaseFiles,
|
addBaseFiles,
|
||||||
addFrameworkFiles,
|
addFrameworkFiles,
|
||||||
getNgCommandName,
|
getNgCommandName,
|
||||||
} from '../utils/files.js';
|
} from '../utils/files.js';
|
||||||
|
import {getAngularConfig} from '../utils/json.js';
|
||||||
import {
|
import {
|
||||||
addPackageJsonDependencies,
|
addPackageJsonDependencies,
|
||||||
addPackageJsonScripts,
|
addPackageJsonScripts,
|
||||||
|
|
@ -33,9 +34,7 @@ import {
|
||||||
type NodePackage,
|
type NodePackage,
|
||||||
updateAngularJsonScripts,
|
updateAngularJsonScripts,
|
||||||
} from '../utils/packages.js';
|
} from '../utils/packages.js';
|
||||||
|
|
||||||
import {type SchematicsOptions} from '../utils/types.js';
|
import {type SchematicsOptions} from '../utils/types.js';
|
||||||
import {getAngularConfig} from '../utils/json.js';
|
|
||||||
|
|
||||||
// You don't have to export the function as default. You can also have more than one rule
|
// You don't have to export the function as default. You can also have more than one rule
|
||||||
// factory per file.
|
// factory per file.
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {relative, resolve} from 'path';
|
||||||
|
|
||||||
import {getSystemPath, normalize, strings} from '@angular-devkit/core';
|
import {getSystemPath, normalize, strings} from '@angular-devkit/core';
|
||||||
import {
|
import {
|
||||||
SchematicContext,
|
SchematicContext,
|
||||||
|
|
@ -26,7 +28,7 @@ import {
|
||||||
move,
|
move,
|
||||||
url,
|
url,
|
||||||
} from '@angular-devkit/schematics';
|
} from '@angular-devkit/schematics';
|
||||||
import {relative, resolve} from 'path';
|
|
||||||
import {SchematicsOptions, TestingFramework} from './types.js';
|
import {SchematicsOptions, TestingFramework} from './types.js';
|
||||||
|
|
||||||
export interface FilesOptions {
|
export interface FilesOptions {
|
||||||
|
|
|
||||||
|
|
@ -14,15 +14,17 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Tree} from '@angular-devkit/schematics';
|
|
||||||
import {get} from 'https';
|
import {get} from 'https';
|
||||||
import {SchematicsOptions, TestingFramework} from './types.js';
|
|
||||||
|
import {Tree} from '@angular-devkit/schematics';
|
||||||
|
|
||||||
|
import {getNgCommandName, getScriptFromOptions} from './files.js';
|
||||||
import {
|
import {
|
||||||
getAngularConfig,
|
getAngularConfig,
|
||||||
getJsonFileAsObject,
|
getJsonFileAsObject,
|
||||||
getObjectAsJson,
|
getObjectAsJson,
|
||||||
} from './json.js';
|
} from './json.js';
|
||||||
import {getNgCommandName, getScriptFromOptions} from './files.js';
|
import {SchematicsOptions, TestingFramework} from './types.js';
|
||||||
export interface NodePackage {
|
export interface NodePackage {
|
||||||
name: string;
|
name: string;
|
||||||
version: string;
|
version: string;
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,13 @@
|
||||||
import expect from 'expect';
|
|
||||||
import sinon from 'sinon';
|
|
||||||
import https from 'https';
|
import https from 'https';
|
||||||
import {join} from 'path';
|
import {join} from 'path';
|
||||||
|
|
||||||
|
import {JsonObject} from '@angular-devkit/core';
|
||||||
import {
|
import {
|
||||||
SchematicTestRunner,
|
SchematicTestRunner,
|
||||||
UnitTestTree,
|
UnitTestTree,
|
||||||
} from '@angular-devkit/schematics/testing/schematic-test-runner';
|
} from '@angular-devkit/schematics/testing/schematic-test-runner';
|
||||||
import {JsonObject} from '@angular-devkit/core';
|
import expect from 'expect';
|
||||||
|
import sinon from 'sinon';
|
||||||
|
|
||||||
const WORKSPACE_OPTIONS = {
|
const WORKSPACE_OPTIONS = {
|
||||||
name: 'workspace',
|
name: 'workspace',
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const fs = require('fs/promises');
|
const fs = require('fs/promises');
|
||||||
|
const {join} = require('path');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {String} directory
|
* @param {String} directory
|
||||||
|
|
@ -42,8 +44,8 @@ async function findSchemaFiles(directory, files = []) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function copySchemaFiles() {
|
async function copySchemaFiles() {
|
||||||
const srcDir = './src';
|
const srcDir = join(__dirname, '..', 'src');
|
||||||
const outputDir = './lib';
|
const outputDir = join(__dirname, '..', 'lib');
|
||||||
const files = await findSchemaFiles(srcDir);
|
const files = await findSchemaFiles(srcDir);
|
||||||
|
|
||||||
const moves = files.map(file => {
|
const moves = files.map(file => {
|
||||||
|
|
@ -2,6 +2,59 @@
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||||
|
|
||||||
|
## [19.7.2](https://github.com/puppeteer/puppeteer/compare/puppeteer-core-v19.7.1...puppeteer-core-v19.7.2) (2023-02-20)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* bump chromium-bidi to a version that does not declare mitt as a peer dependency ([#9701](https://github.com/puppeteer/puppeteer/issues/9701)) ([82916c1](https://github.com/puppeteer/puppeteer/commit/82916c102b2c399093ba9019e272207b5ce81849))
|
||||||
|
|
||||||
|
## [19.7.1](https://github.com/puppeteer/puppeteer/compare/puppeteer-core-v19.7.0...puppeteer-core-v19.7.1) (2023-02-15)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* fix circularity on JSHandle interface ([#9661](https://github.com/puppeteer/puppeteer/issues/9661)) ([eb13863](https://github.com/puppeteer/puppeteer/commit/eb138635d661d3cdaf2940959fece5aca482178a))
|
||||||
|
* make chromium-bidi an opt peer dep ([#9667](https://github.com/puppeteer/puppeteer/issues/9667)) ([c6054ac](https://github.com/puppeteer/puppeteer/commit/c6054ac1a56c08ee7bf01321878699b7b4ab4e0b))
|
||||||
|
|
||||||
|
## [19.7.0](https://github.com/puppeteer/puppeteer/compare/puppeteer-core-v19.6.3...puppeteer-core-v19.7.0) (2023-02-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add touchstart, touchmove and touchend methods ([#9622](https://github.com/puppeteer/puppeteer/issues/9622)) ([c8bb11a](https://github.com/puppeteer/puppeteer/commit/c8bb11adfcf1537032730a91baa3c36a6e324926))
|
||||||
|
* **chromium:** roll to Chromium 111.0.5556.0 (r1095492) ([#9656](https://github.com/puppeteer/puppeteer/issues/9656)) ([df59d01](https://github.com/puppeteer/puppeteer/commit/df59d010c20644da06eb4c4e28a11c4eea164aba))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* `page.goto` error throwing on 40x/50x responses with an empty body ([#9523](https://github.com/puppeteer/puppeteer/issues/9523)) ([#9577](https://github.com/puppeteer/puppeteer/issues/9577)) ([ddb0cc1](https://github.com/puppeteer/puppeteer/commit/ddb0cc174d2a14c0948dcdaf9bae78620937c667))
|
||||||
|
|
||||||
|
## [19.6.3](https://github.com/puppeteer/puppeteer/compare/puppeteer-core-v19.6.2...puppeteer-core-v19.6.3) (2023-02-01)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* ignore not found contexts for console messages ([#9595](https://github.com/puppeteer/puppeteer/issues/9595)) ([390685b](https://github.com/puppeteer/puppeteer/commit/390685bbe52c22b686fc0e3119b4ac7b1073c581))
|
||||||
|
* restore WaitTask terminate condition ([#9612](https://github.com/puppeteer/puppeteer/issues/9612)) ([e16cbc6](https://github.com/puppeteer/puppeteer/commit/e16cbc6626cffd40d0caa30801620e7293455006))
|
||||||
|
|
||||||
|
## [19.6.2](https://github.com/puppeteer/puppeteer/compare/puppeteer-core-v19.6.1...puppeteer-core-v19.6.2) (2023-01-27)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* atomically get Puppeteer utilities ([#9597](https://github.com/puppeteer/puppeteer/issues/9597)) ([050a7b0](https://github.com/puppeteer/puppeteer/commit/050a7b062415ebaf10bcb71c405143eacc4e5d4b))
|
||||||
|
|
||||||
|
## [19.6.1](https://github.com/puppeteer/puppeteer/compare/puppeteer-core-v19.6.0...puppeteer-core-v19.6.1) (2023-01-26)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* don't clean up previous browser versions ([#9568](https://github.com/puppeteer/puppeteer/issues/9568)) ([344bc2a](https://github.com/puppeteer/puppeteer/commit/344bc2af62e4068fe2cb8162d4b6c8242aac843b)), closes [#9533](https://github.com/puppeteer/puppeteer/issues/9533)
|
||||||
|
* mimic rejection for PuppeteerUtil on early call ([#9589](https://github.com/puppeteer/puppeteer/issues/9589)) ([1980de9](https://github.com/puppeteer/puppeteer/commit/1980de91a161523c7098a79919b20e6d8d2e5d81))
|
||||||
|
* **revert:** use LazyArg for puppeteer utilities ([#9590](https://github.com/puppeteer/puppeteer/issues/9590)) ([6edd996](https://github.com/puppeteer/puppeteer/commit/6edd99676827de2c83f7a858e4f903b1c34e7d35))
|
||||||
|
* use LazyArg for puppeteer utilities ([#9575](https://github.com/puppeteer/puppeteer/issues/9575)) ([496658f](https://github.com/puppeteer/puppeteer/commit/496658f02945b53096483f36cb3d64556cff045e))
|
||||||
|
|
||||||
## [19.6.0](https://github.com/puppeteer/puppeteer/compare/puppeteer-core-v19.5.2...puppeteer-core-v19.6.0) (2023-01-23)
|
## [19.6.0](https://github.com/puppeteer/puppeteer/compare/puppeteer-core-v19.5.2...puppeteer-core-v19.6.0) (2023-01-23)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
|
||||||
|
"mainEntryPointFilePath": "<projectFolder>/lib/esm/puppeteer/puppeteer-core.d.ts",
|
||||||
|
|
||||||
|
"extends": "./api-extractor.json",
|
||||||
|
|
||||||
|
"dtsRollup": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
|
||||||
|
"docModel": {
|
||||||
|
"enabled": true,
|
||||||
|
"apiJsonFilePath": "<projectFolder>/../../docs/<unscopedPackageName>.api.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,8 +8,7 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
"docModel": {
|
"docModel": {
|
||||||
"enabled": true,
|
"enabled": false
|
||||||
"apiJsonFilePath": "<projectFolder>/../../docs/<unscopedPackageName>.api.json"
|
|
||||||
},
|
},
|
||||||
|
|
||||||
"dtsRollup": {
|
"dtsRollup": {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "puppeteer-core",
|
"name": "puppeteer-core",
|
||||||
"version": "19.6.0",
|
"version": "19.7.2",
|
||||||
"description": "A high-level API to control headless Chrome over the DevTools Protocol",
|
"description": "A high-level API to control headless Chrome over the DevTools Protocol",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"puppeteer",
|
"puppeteer",
|
||||||
|
|
@ -34,17 +34,15 @@
|
||||||
"node": ">=14.1.0"
|
"node": ">=14.1.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build:third_party": "wireit",
|
"build:docs": "wireit",
|
||||||
"build:tsc": "wireit",
|
"build:tsc": "wireit",
|
||||||
"build:types": "wireit",
|
"build:types": "wireit",
|
||||||
"build": "wireit",
|
"build": "wireit",
|
||||||
"check": "tsx tools/ensure-correct-devtools-protocol-package",
|
"check": "tsx tools/ensure-correct-devtools-protocol-package",
|
||||||
"format:types": "wireit",
|
"clean": "tsc -b --clean && rimraf lib src/generated",
|
||||||
"generate:package-json": "wireit",
|
"generate:package-json": "wireit",
|
||||||
"generate:sources": "wireit",
|
"generate:sources": "wireit",
|
||||||
"prepack": "wireit",
|
"prepack": "wireit"
|
||||||
"clean": "tsc -b --clean && rimraf lib src/generated",
|
|
||||||
"clean:third_party": "wireit"
|
|
||||||
},
|
},
|
||||||
"wireit": {
|
"wireit": {
|
||||||
"prepack": {
|
"prepack": {
|
||||||
|
|
@ -58,93 +56,71 @@
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"build:third_party",
|
"build:tsc",
|
||||||
"format:types",
|
"build:types"
|
||||||
"generate:package-json"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"generate:sources": {
|
"generate:sources": {
|
||||||
"command": "tsx tools/generate_sources.ts",
|
"command": "tsx tools/generate_sources.ts",
|
||||||
|
"clean": "if-file-deleted",
|
||||||
"files": [
|
"files": [
|
||||||
"tools/generate_sources.ts",
|
"../../versions.js",
|
||||||
"src/templates/**"
|
"src/{injected,templates}/**",
|
||||||
|
"tools/generate_sources.ts"
|
||||||
],
|
],
|
||||||
"output": [
|
"output": [
|
||||||
"src/generated/**"
|
"src/generated/*.ts"
|
||||||
]
|
|
||||||
},
|
|
||||||
"clean:third_party": {
|
|
||||||
"command": "rimraf lib/esm/third_party lib/cjs/third_party"
|
|
||||||
},
|
|
||||||
"build:third_party": {
|
|
||||||
"command": "rollup --config rollup.third_party.config.js",
|
|
||||||
"dependencies": [
|
|
||||||
"build:tsc"
|
|
||||||
],
|
|
||||||
"clean": false,
|
|
||||||
"files": [
|
|
||||||
"lib/esm/third_party/**",
|
|
||||||
"lib/cjs/third_party/**"
|
|
||||||
],
|
|
||||||
"output": [
|
|
||||||
"lib/esm/third_party/**",
|
|
||||||
"lib/cjs/third_party/**"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"generate:package-json": {
|
"generate:package-json": {
|
||||||
"command": "tsx ../../tools/generate_module_package_json.ts lib/esm/package.json",
|
"command": "tsx ../../tools/generate_module_package_json.ts lib/esm/package.json",
|
||||||
"clean": "if-file-deleted",
|
"files": [
|
||||||
"dependencies": [
|
"../../tools/generate_module_package_json.ts"
|
||||||
"build:tsc"
|
|
||||||
],
|
],
|
||||||
"output": [
|
"output": [
|
||||||
"lib/esm/package.json"
|
"lib/esm/package.json"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"build:types": {
|
"build:docs": {
|
||||||
"command": "api-extractor run --local",
|
"command": "api-extractor run --local --config \"./api-extractor.docs.json\"",
|
||||||
|
"files": [
|
||||||
|
"api-extractor.docs.json",
|
||||||
|
"lib/esm/puppeteer/puppeteer-core.d.ts",
|
||||||
|
"tsconfig.json"
|
||||||
|
],
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"build:tsc"
|
"build:tsc"
|
||||||
],
|
|
||||||
"files": [
|
|
||||||
"tsconfig.json",
|
|
||||||
"api-extractor.json",
|
|
||||||
"lib/esm/puppeteer/types.d.ts"
|
|
||||||
],
|
|
||||||
"output": [
|
|
||||||
"lib/types.d.ts"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"format:types": {
|
|
||||||
"command": "eslint --cache-location .eslintcache --cache --ext=ts --no-ignore --no-eslintrc -c=../../.eslintrc.types.cjs --fix lib/types.d.ts",
|
|
||||||
"dependencies": [
|
|
||||||
"build:types"
|
|
||||||
],
|
|
||||||
"clean": false,
|
|
||||||
"files": [
|
|
||||||
"lib/types.d.ts",
|
|
||||||
"../../.eslintrc.types.cjs"
|
|
||||||
],
|
|
||||||
"output": [
|
|
||||||
"lib/types.d.ts"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"build:tsc": {
|
"build:tsc": {
|
||||||
"command": "tsc -b",
|
"command": "tsc -b && rollup --config rollup.third_party.config.mjs",
|
||||||
"clean": "if-file-deleted",
|
"clean": "if-file-deleted",
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
"clean:third_party",
|
"generate:package-json",
|
||||||
"generate:sources"
|
"generate:sources"
|
||||||
],
|
],
|
||||||
"files": [
|
"files": [
|
||||||
"src/**",
|
"{compat,src,third_party}/**",
|
||||||
"compat/**",
|
"rollup.third_party.config.mjs"
|
||||||
"third_party/**",
|
|
||||||
"**/tsconfig.*.json"
|
|
||||||
],
|
],
|
||||||
"output": [
|
"output": [
|
||||||
"lib/esm/**",
|
"lib/{cjs,esm}/**",
|
||||||
"lib/cjs/**"
|
"!lib/esm/package.json"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"build:types": {
|
||||||
|
"command": "api-extractor run --local && eslint --cache-location .eslintcache --cache --ext=ts --no-ignore --no-eslintrc -c=../../.eslintrc.types.cjs --fix lib/types.d.ts",
|
||||||
|
"files": [
|
||||||
|
"../../.eslintrc.types.cjs",
|
||||||
|
"api-extractor.json",
|
||||||
|
"lib/esm/puppeteer/types.d.ts",
|
||||||
|
"tsconfig.json"
|
||||||
|
],
|
||||||
|
"output": [
|
||||||
|
"lib/types.d.ts"
|
||||||
|
],
|
||||||
|
"dependencies": [
|
||||||
|
"build:tsc"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -155,9 +131,10 @@
|
||||||
"author": "The Chromium Authors",
|
"author": "The Chromium Authors",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"chromium-bidi": "0.4.4",
|
||||||
"cross-fetch": "3.1.5",
|
"cross-fetch": "3.1.5",
|
||||||
"debug": "4.3.4",
|
"debug": "4.3.4",
|
||||||
"devtools-protocol": "0.0.1082910",
|
"devtools-protocol": "0.0.1094867",
|
||||||
"extract-zip": "2.0.1",
|
"extract-zip": "2.0.1",
|
||||||
"https-proxy-agent": "5.0.1",
|
"https-proxy-agent": "5.0.1",
|
||||||
"proxy-from-env": "1.1.0",
|
"proxy-from-env": "1.1.0",
|
||||||
|
|
@ -165,5 +142,13 @@
|
||||||
"tar-fs": "2.1.1",
|
"tar-fs": "2.1.1",
|
||||||
"unbzip2-stream": "1.4.3",
|
"unbzip2-stream": "1.4.3",
|
||||||
"ws": "8.11.0"
|
"ws": "8.11.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": ">= 4.7.4"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"typescript": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,29 +13,23 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import glob from 'glob';
|
|
||||||
import dts from 'rollup-plugin-dts';
|
|
||||||
import {nodeResolve} from '@rollup/plugin-node-resolve';
|
|
||||||
import commonjs from '@rollup/plugin-commonjs';
|
import commonjs from '@rollup/plugin-commonjs';
|
||||||
|
import {nodeResolve} from '@rollup/plugin-node-resolve';
|
||||||
|
import glob from 'glob';
|
||||||
|
|
||||||
export default ['cjs', 'esm'].flatMap(outputType => {
|
export default ['cjs', 'esm'].flatMap(outputType => {
|
||||||
const configs = [];
|
const configs = [];
|
||||||
// Note we don't use path.join here. We cannot since `glob` does not support
|
// Note we don't use path.join here. We cannot since `glob` does not support
|
||||||
// the backslash path separator.
|
// the backslash path separator.
|
||||||
const thirdPartyPath = `lib/${outputType}/third_party`;
|
for (const file of glob.sync(`lib/${outputType}/third_party/**/*.js`)) {
|
||||||
for (const jsFile of glob.sync(`${thirdPartyPath}/**/*.js`)) {
|
|
||||||
configs.push({
|
configs.push({
|
||||||
input: jsFile,
|
input: file,
|
||||||
output: {file: jsFile, format: outputType},
|
output: {
|
||||||
|
file,
|
||||||
|
format: outputType,
|
||||||
|
},
|
||||||
plugins: [commonjs(), nodeResolve()],
|
plugins: [commonjs(), nodeResolve()],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
for (const typesFile of glob.sync(`${thirdPartyPath}/**/*.d.ts`)) {
|
|
||||||
configs.push({
|
|
||||||
input: typesFile,
|
|
||||||
output: {file: typesFile, format: outputType},
|
|
||||||
plugins: [dts({respectExternal: true})],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return configs;
|
return configs;
|
||||||
});
|
});
|
||||||
|
|
@ -17,11 +17,14 @@
|
||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
|
|
||||||
import {ChildProcess} from 'child_process';
|
import {ChildProcess} from 'child_process';
|
||||||
|
|
||||||
import {Protocol} from 'devtools-protocol';
|
import {Protocol} from 'devtools-protocol';
|
||||||
|
|
||||||
import {EventEmitter} from '../common/EventEmitter.js';
|
import {EventEmitter} from '../common/EventEmitter.js';
|
||||||
import type {Page} from './Page.js'; // TODO: move to ./api
|
|
||||||
import type {Target} from '../common/Target.js'; // TODO: move to ./api
|
import type {Target} from '../common/Target.js'; // TODO: move to ./api
|
||||||
|
|
||||||
import type {BrowserContext} from './BrowserContext.js';
|
import type {BrowserContext} from './BrowserContext.js';
|
||||||
|
import type {Page} from './Page.js'; // TODO: move to ./api
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BrowserContext options.
|
* BrowserContext options.
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {EventEmitter} from '../common/EventEmitter.js';
|
import {EventEmitter} from '../common/EventEmitter.js';
|
||||||
import {Page} from './Page.js';
|
|
||||||
import {Target} from '../common/Target.js';
|
import {Target} from '../common/Target.js';
|
||||||
|
|
||||||
import type {Permission, Browser} from './Browser.js';
|
import type {Permission, Browser} from './Browser.js';
|
||||||
|
import {Page} from './Page.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BrowserContexts provide a way to operate multiple independent browser
|
* BrowserContexts provide a way to operate multiple independent browser
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,728 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2023 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Protocol} from 'devtools-protocol';
|
||||||
|
|
||||||
|
import {CDPSession} from '../common/Connection.js';
|
||||||
|
import {ExecutionContext} from '../common/ExecutionContext.js';
|
||||||
|
import {Frame} from '../common/Frame.js';
|
||||||
|
import {MouseButton} from '../common/Input.js';
|
||||||
|
import {WaitForSelectorOptions} from '../common/IsolatedWorld.js';
|
||||||
|
import {
|
||||||
|
ElementFor,
|
||||||
|
EvaluateFuncWith,
|
||||||
|
HandleFor,
|
||||||
|
NodeFor,
|
||||||
|
} from '../common/types.js';
|
||||||
|
import {KeyInput} from '../common/USKeyboardLayout.js';
|
||||||
|
|
||||||
|
import {JSHandle} from './JSHandle.js';
|
||||||
|
import {ScreenshotOptions} from './Page.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface BoxModel {
|
||||||
|
content: Point[];
|
||||||
|
padding: Point[];
|
||||||
|
border: Point[];
|
||||||
|
margin: Point[];
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface BoundingBox extends Point {
|
||||||
|
/**
|
||||||
|
* the width of the element in pixels.
|
||||||
|
*/
|
||||||
|
width: number;
|
||||||
|
/**
|
||||||
|
* the height of the element in pixels.
|
||||||
|
*/
|
||||||
|
height: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface Offset {
|
||||||
|
/**
|
||||||
|
* x-offset for the clickable point relative to the top-left corner of the border box.
|
||||||
|
*/
|
||||||
|
x: number;
|
||||||
|
/**
|
||||||
|
* y-offset for the clickable point relative to the top-left corner of the border box.
|
||||||
|
*/
|
||||||
|
y: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface ClickOptions {
|
||||||
|
/**
|
||||||
|
* Time to wait between `mousedown` and `mouseup` in milliseconds.
|
||||||
|
*
|
||||||
|
* @defaultValue 0
|
||||||
|
*/
|
||||||
|
delay?: number;
|
||||||
|
/**
|
||||||
|
* @defaultValue 'left'
|
||||||
|
*/
|
||||||
|
button?: MouseButton;
|
||||||
|
/**
|
||||||
|
* @defaultValue 1
|
||||||
|
*/
|
||||||
|
clickCount?: number;
|
||||||
|
/**
|
||||||
|
* Offset for the clickable point relative to the top-left corner of the border box.
|
||||||
|
*/
|
||||||
|
offset?: Offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface PressOptions {
|
||||||
|
/**
|
||||||
|
* Time to wait between `keydown` and `keyup` in milliseconds. Defaults to 0.
|
||||||
|
*/
|
||||||
|
delay?: number;
|
||||||
|
/**
|
||||||
|
* If specified, generates an input event with this text.
|
||||||
|
*/
|
||||||
|
text?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface Point {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ElementHandle represents an in-page DOM element.
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
* ElementHandles can be created with the {@link Page.$} method.
|
||||||
|
*
|
||||||
|
* ```ts
|
||||||
|
* import puppeteer from 'puppeteer';
|
||||||
|
*
|
||||||
|
* (async () => {
|
||||||
|
* const browser = await puppeteer.launch();
|
||||||
|
* const page = await browser.newPage();
|
||||||
|
* await page.goto('https://example.com');
|
||||||
|
* const hrefElement = await page.$('a');
|
||||||
|
* await hrefElement.click();
|
||||||
|
* // ...
|
||||||
|
* })();
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* ElementHandle prevents the DOM element from being garbage-collected unless the
|
||||||
|
* handle is {@link JSHandle.dispose | disposed}. ElementHandles are auto-disposed
|
||||||
|
* when their origin frame gets navigated.
|
||||||
|
*
|
||||||
|
* ElementHandle instances can be used as arguments in {@link Page.$eval} and
|
||||||
|
* {@link Page.evaluate} methods.
|
||||||
|
*
|
||||||
|
* If you're using TypeScript, ElementHandle takes a generic argument that
|
||||||
|
* denotes the type of element the handle is holding within. For example, if you
|
||||||
|
* have a handle to a `<select>` element, you can type it as
|
||||||
|
* `ElementHandle<HTMLSelectElement>` and you get some nicer type checks.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class ElementHandle<
|
||||||
|
ElementType extends Node = Element
|
||||||
|
> extends JSHandle<ElementType> {
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
override executionContext(): ExecutionContext {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
override get client(): CDPSession {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
get frame(): Frame {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queries the current element for an element matching the given selector.
|
||||||
|
*
|
||||||
|
* @param selector - The selector to query for.
|
||||||
|
* @returns A {@link ElementHandle | element handle} to the first element
|
||||||
|
* matching the given selector. Otherwise, `null`.
|
||||||
|
*/
|
||||||
|
async $<Selector extends string>(
|
||||||
|
selector: Selector
|
||||||
|
): Promise<ElementHandle<NodeFor<Selector>> | null>;
|
||||||
|
async $<Selector extends string>(): Promise<ElementHandle<
|
||||||
|
NodeFor<Selector>
|
||||||
|
> | null> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queries the current element for all elements matching the given selector.
|
||||||
|
*
|
||||||
|
* @param selector - The selector to query for.
|
||||||
|
* @returns An array of {@link ElementHandle | element handles} that point to
|
||||||
|
* elements matching the given selector.
|
||||||
|
*/
|
||||||
|
async $$<Selector extends string>(
|
||||||
|
selector: Selector
|
||||||
|
): Promise<Array<ElementHandle<NodeFor<Selector>>>>;
|
||||||
|
async $$<Selector extends string>(): Promise<
|
||||||
|
Array<ElementHandle<NodeFor<Selector>>>
|
||||||
|
> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the given function on the first element matching the given selector in
|
||||||
|
* the current element.
|
||||||
|
*
|
||||||
|
* If the given function returns a promise, then this method will wait till
|
||||||
|
* the promise resolves.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* ```ts
|
||||||
|
* const tweetHandle = await page.$('.tweet');
|
||||||
|
* expect(await tweetHandle.$eval('.like', node => node.innerText)).toBe(
|
||||||
|
* '100'
|
||||||
|
* );
|
||||||
|
* expect(await tweetHandle.$eval('.retweets', node => node.innerText)).toBe(
|
||||||
|
* '10'
|
||||||
|
* );
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param selector - The selector to query for.
|
||||||
|
* @param pageFunction - The function to be evaluated in this element's page's
|
||||||
|
* context. The first element matching the selector will be passed in as the
|
||||||
|
* first argument.
|
||||||
|
* @param args - Additional arguments to pass to `pageFunction`.
|
||||||
|
* @returns A promise to the result of the function.
|
||||||
|
*/
|
||||||
|
async $eval<
|
||||||
|
Selector extends string,
|
||||||
|
Params extends unknown[],
|
||||||
|
Func extends EvaluateFuncWith<NodeFor<Selector>, Params> = EvaluateFuncWith<
|
||||||
|
NodeFor<Selector>,
|
||||||
|
Params
|
||||||
|
>
|
||||||
|
>(
|
||||||
|
selector: Selector,
|
||||||
|
pageFunction: Func | string,
|
||||||
|
...args: Params
|
||||||
|
): Promise<Awaited<ReturnType<Func>>>;
|
||||||
|
async $eval(): Promise<unknown> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the given function on an array of elements matching the given selector
|
||||||
|
* in the current element.
|
||||||
|
*
|
||||||
|
* If the given function returns a promise, then this method will wait till
|
||||||
|
* the promise resolves.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* HTML:
|
||||||
|
*
|
||||||
|
* ```html
|
||||||
|
* <div class="feed">
|
||||||
|
* <div class="tweet">Hello!</div>
|
||||||
|
* <div class="tweet">Hi!</div>
|
||||||
|
* </div>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* JavaScript:
|
||||||
|
*
|
||||||
|
* ```js
|
||||||
|
* const feedHandle = await page.$('.feed');
|
||||||
|
* expect(
|
||||||
|
* await feedHandle.$$eval('.tweet', nodes => nodes.map(n => n.innerText))
|
||||||
|
* ).toEqual(['Hello!', 'Hi!']);
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param selector - The selector to query for.
|
||||||
|
* @param pageFunction - The function to be evaluated in the element's page's
|
||||||
|
* context. An array of elements matching the given selector will be passed to
|
||||||
|
* the function as its first argument.
|
||||||
|
* @param args - Additional arguments to pass to `pageFunction`.
|
||||||
|
* @returns A promise to the result of the function.
|
||||||
|
*/
|
||||||
|
async $$eval<
|
||||||
|
Selector extends string,
|
||||||
|
Params extends unknown[],
|
||||||
|
Func extends EvaluateFuncWith<
|
||||||
|
Array<NodeFor<Selector>>,
|
||||||
|
Params
|
||||||
|
> = EvaluateFuncWith<Array<NodeFor<Selector>>, Params>
|
||||||
|
>(
|
||||||
|
selector: Selector,
|
||||||
|
pageFunction: Func | string,
|
||||||
|
...args: Params
|
||||||
|
): Promise<Awaited<ReturnType<Func>>>;
|
||||||
|
async $$eval(): Promise<unknown> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use {@link ElementHandle.$$} with the `xpath` prefix.
|
||||||
|
*
|
||||||
|
* Example: `await elementHandle.$$('xpath/' + xpathExpression)`
|
||||||
|
*
|
||||||
|
* The method evaluates the XPath expression relative to the elementHandle.
|
||||||
|
* If `xpath` starts with `//` instead of `.//`, the dot will be appended
|
||||||
|
* automatically.
|
||||||
|
*
|
||||||
|
* If there are no such elements, the method will resolve to an empty array.
|
||||||
|
* @param expression - Expression to {@link https://developer.mozilla.org/en-US/docs/Web/API/Document/evaluate | evaluate}
|
||||||
|
*/
|
||||||
|
async $x(expression: string): Promise<Array<ElementHandle<Node>>>;
|
||||||
|
async $x(): Promise<Array<ElementHandle<Node>>> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for an element matching the given selector to appear in the current
|
||||||
|
* element.
|
||||||
|
*
|
||||||
|
* Unlike {@link Frame.waitForSelector}, this method does not work across
|
||||||
|
* navigations or if the element is detached from DOM.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* ```ts
|
||||||
|
* import puppeteer from 'puppeteer';
|
||||||
|
*
|
||||||
|
* (async () => {
|
||||||
|
* const browser = await puppeteer.launch();
|
||||||
|
* const page = await browser.newPage();
|
||||||
|
* let currentURL;
|
||||||
|
* page
|
||||||
|
* .mainFrame()
|
||||||
|
* .waitForSelector('img')
|
||||||
|
* .then(() => console.log('First URL with image: ' + currentURL));
|
||||||
|
*
|
||||||
|
* for (currentURL of [
|
||||||
|
* 'https://example.com',
|
||||||
|
* 'https://google.com',
|
||||||
|
* 'https://bbc.com',
|
||||||
|
* ]) {
|
||||||
|
* await page.goto(currentURL);
|
||||||
|
* }
|
||||||
|
* await browser.close();
|
||||||
|
* })();
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param selector - The selector to query and wait for.
|
||||||
|
* @param options - Options for customizing waiting behavior.
|
||||||
|
* @returns An element matching the given selector.
|
||||||
|
* @throws Throws if an element matching the given selector doesn't appear.
|
||||||
|
*/
|
||||||
|
async waitForSelector<Selector extends string>(
|
||||||
|
selector: Selector,
|
||||||
|
options?: WaitForSelectorOptions
|
||||||
|
): Promise<ElementHandle<NodeFor<Selector>> | null>;
|
||||||
|
async waitForSelector<Selector extends string>(): Promise<ElementHandle<
|
||||||
|
NodeFor<Selector>
|
||||||
|
> | null> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use {@link ElementHandle.waitForSelector} with the `xpath`
|
||||||
|
* prefix.
|
||||||
|
*
|
||||||
|
* Example: `await elementHandle.waitForSelector('xpath/' + xpathExpression)`
|
||||||
|
*
|
||||||
|
* The method evaluates the XPath expression relative to the elementHandle.
|
||||||
|
*
|
||||||
|
* Wait for the `xpath` within the element. If at the moment of calling the
|
||||||
|
* method the `xpath` already exists, the method will return immediately. If
|
||||||
|
* the `xpath` doesn't appear after the `timeout` milliseconds of waiting, the
|
||||||
|
* function will throw.
|
||||||
|
*
|
||||||
|
* If `xpath` starts with `//` instead of `.//`, the dot will be appended
|
||||||
|
* automatically.
|
||||||
|
*
|
||||||
|
* This method works across navigation.
|
||||||
|
*
|
||||||
|
* ```ts
|
||||||
|
* import puppeteer from 'puppeteer';
|
||||||
|
* (async () => {
|
||||||
|
* const browser = await puppeteer.launch();
|
||||||
|
* const page = await browser.newPage();
|
||||||
|
* let currentURL;
|
||||||
|
* page
|
||||||
|
* .waitForXPath('//img')
|
||||||
|
* .then(() => console.log('First URL with image: ' + currentURL));
|
||||||
|
* for (currentURL of [
|
||||||
|
* 'https://example.com',
|
||||||
|
* 'https://google.com',
|
||||||
|
* 'https://bbc.com',
|
||||||
|
* ]) {
|
||||||
|
* await page.goto(currentURL);
|
||||||
|
* }
|
||||||
|
* await browser.close();
|
||||||
|
* })();
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param xpath - A
|
||||||
|
* {@link https://developer.mozilla.org/en-US/docs/Web/XPath | xpath} of an
|
||||||
|
* element to wait for
|
||||||
|
* @param options - Optional waiting parameters
|
||||||
|
* @returns Promise which resolves when element specified by xpath string is
|
||||||
|
* added to DOM. Resolves to `null` if waiting for `hidden: true` and xpath is
|
||||||
|
* not found in DOM, otherwise resolves to `ElementHandle`.
|
||||||
|
* @remarks
|
||||||
|
* The optional Argument `options` have properties:
|
||||||
|
*
|
||||||
|
* - `visible`: A boolean to wait for element to be present in DOM and to be
|
||||||
|
* visible, i.e. to not have `display: none` or `visibility: hidden` CSS
|
||||||
|
* properties. Defaults to `false`.
|
||||||
|
*
|
||||||
|
* - `hidden`: A boolean wait for element to not be found in the DOM or to be
|
||||||
|
* hidden, i.e. have `display: none` or `visibility: hidden` CSS properties.
|
||||||
|
* Defaults to `false`.
|
||||||
|
*
|
||||||
|
* - `timeout`: A number which is maximum time to wait for in milliseconds.
|
||||||
|
* Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The
|
||||||
|
* default value can be changed by using the {@link Page.setDefaultTimeout}
|
||||||
|
* method.
|
||||||
|
*/
|
||||||
|
async waitForXPath(
|
||||||
|
xpath: string,
|
||||||
|
options?: {
|
||||||
|
visible?: boolean;
|
||||||
|
hidden?: boolean;
|
||||||
|
timeout?: number;
|
||||||
|
}
|
||||||
|
): Promise<ElementHandle<Node> | null>;
|
||||||
|
async waitForXPath(): Promise<ElementHandle<Node> | null> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the current handle to the given element type.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* ```ts
|
||||||
|
* const element: ElementHandle<Element> = await page.$(
|
||||||
|
* '.class-name-of-anchor'
|
||||||
|
* );
|
||||||
|
* // DO NOT DISPOSE `element`, this will be always be the same handle.
|
||||||
|
* const anchor: ElementHandle<HTMLAnchorElement> = await element.toElement(
|
||||||
|
* 'a'
|
||||||
|
* );
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param tagName - The tag name of the desired element type.
|
||||||
|
* @throws An error if the handle does not match. **The handle will not be
|
||||||
|
* automatically disposed.**
|
||||||
|
*/
|
||||||
|
async toElement<
|
||||||
|
K extends keyof HTMLElementTagNameMap | keyof SVGElementTagNameMap
|
||||||
|
>(tagName: K): Promise<HandleFor<ElementFor<K>>>;
|
||||||
|
async toElement<
|
||||||
|
K extends keyof HTMLElementTagNameMap | keyof SVGElementTagNameMap
|
||||||
|
>(): Promise<HandleFor<ElementFor<K>>> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
override asElement(): ElementHandle<ElementType> | null {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves to the content frame for element handles referencing
|
||||||
|
* iframe nodes, or null otherwise
|
||||||
|
*/
|
||||||
|
async contentFrame(): Promise<Frame | null> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the middle point within an element unless a specific offset is provided.
|
||||||
|
*/
|
||||||
|
async clickablePoint(offset?: Offset): Promise<Point>;
|
||||||
|
async clickablePoint(): Promise<Point> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method scrolls element into view if needed, and then
|
||||||
|
* uses {@link Page.mouse} to hover over the center of the element.
|
||||||
|
* If the element is detached from DOM, the method throws an error.
|
||||||
|
*/
|
||||||
|
async hover(this: ElementHandle<Element>): Promise<void> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method scrolls element into view if needed, and then
|
||||||
|
* uses {@link Page.mouse} to click in the center of the element.
|
||||||
|
* If the element is detached from DOM, the method throws an error.
|
||||||
|
*/
|
||||||
|
async click(
|
||||||
|
this: ElementHandle<Element>,
|
||||||
|
options?: ClickOptions
|
||||||
|
): Promise<void>;
|
||||||
|
async click(this: ElementHandle<Element>): Promise<void> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method creates and captures a dragevent from the element.
|
||||||
|
*/
|
||||||
|
async drag(
|
||||||
|
this: ElementHandle<Element>,
|
||||||
|
target: Point
|
||||||
|
): Promise<Protocol.Input.DragData>;
|
||||||
|
async drag(this: ElementHandle<Element>): Promise<Protocol.Input.DragData> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method creates a `dragenter` event on the element.
|
||||||
|
*/
|
||||||
|
async dragEnter(
|
||||||
|
this: ElementHandle<Element>,
|
||||||
|
data?: Protocol.Input.DragData
|
||||||
|
): Promise<void>;
|
||||||
|
async dragEnter(this: ElementHandle<Element>): Promise<void> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method creates a `dragover` event on the element.
|
||||||
|
*/
|
||||||
|
async dragOver(
|
||||||
|
this: ElementHandle<Element>,
|
||||||
|
data?: Protocol.Input.DragData
|
||||||
|
): Promise<void>;
|
||||||
|
async dragOver(this: ElementHandle<Element>): Promise<void> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method triggers a drop on the element.
|
||||||
|
*/
|
||||||
|
async drop(
|
||||||
|
this: ElementHandle<Element>,
|
||||||
|
data?: Protocol.Input.DragData
|
||||||
|
): Promise<void>;
|
||||||
|
async drop(this: ElementHandle<Element>): Promise<void> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method triggers a dragenter, dragover, and drop on the element.
|
||||||
|
*/
|
||||||
|
async dragAndDrop(
|
||||||
|
this: ElementHandle<Element>,
|
||||||
|
target: ElementHandle<Node>,
|
||||||
|
options?: {delay: number}
|
||||||
|
): Promise<void>;
|
||||||
|
async dragAndDrop(this: ElementHandle<Element>): Promise<void> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggers a `change` and `input` event once all the provided options have been
|
||||||
|
* selected. If there's no `<select>` element matching `selector`, the method
|
||||||
|
* throws an error.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* ```ts
|
||||||
|
* handle.select('blue'); // single selection
|
||||||
|
* handle.select('red', 'green', 'blue'); // multiple selections
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param values - Values of options to select. If the `<select>` has the
|
||||||
|
* `multiple` attribute, all values are considered, otherwise only the first
|
||||||
|
* one is taken into account.
|
||||||
|
*/
|
||||||
|
async select(...values: string[]): Promise<string[]>;
|
||||||
|
async select(): Promise<string[]> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method expects `elementHandle` to point to an
|
||||||
|
* {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input | input element}.
|
||||||
|
*
|
||||||
|
* @param filePaths - Sets the value of the file input to these paths.
|
||||||
|
* If a path is relative, then it is resolved against the
|
||||||
|
* {@link https://nodejs.org/api/process.html#process_process_cwd | current working directory}.
|
||||||
|
* Note for locals script connecting to remote chrome environments,
|
||||||
|
* paths must be absolute.
|
||||||
|
*/
|
||||||
|
async uploadFile(
|
||||||
|
this: ElementHandle<HTMLInputElement>,
|
||||||
|
...filePaths: string[]
|
||||||
|
): Promise<void>;
|
||||||
|
async uploadFile(this: ElementHandle<HTMLInputElement>): Promise<void> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method scrolls element into view if needed, and then uses
|
||||||
|
* {@link Touchscreen.tap} to tap in the center of the element.
|
||||||
|
* If the element is detached from DOM, the method throws an error.
|
||||||
|
*/
|
||||||
|
async tap(this: ElementHandle<Element>): Promise<void> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
async touchStart(this: ElementHandle<Element>): Promise<void> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
async touchMove(this: ElementHandle<Element>): Promise<void> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
async touchEnd(this: ElementHandle<Element>): Promise<void> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus | focus} on the element.
|
||||||
|
*/
|
||||||
|
async focus(): Promise<void> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Focuses the element, and then sends a `keydown`, `keypress`/`input`, and
|
||||||
|
* `keyup` event for each character in the text.
|
||||||
|
*
|
||||||
|
* To press a special key, like `Control` or `ArrowDown`,
|
||||||
|
* use {@link ElementHandle.press}.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* ```ts
|
||||||
|
* await elementHandle.type('Hello'); // Types instantly
|
||||||
|
* await elementHandle.type('World', {delay: 100}); // Types slower, like a user
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* An example of typing into a text field and then submitting the form:
|
||||||
|
*
|
||||||
|
* ```ts
|
||||||
|
* const elementHandle = await page.$('input');
|
||||||
|
* await elementHandle.type('some text');
|
||||||
|
* await elementHandle.press('Enter');
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
async type(text: string, options?: {delay: number}): Promise<void>;
|
||||||
|
async type(): Promise<void> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Focuses the element, and then uses {@link Keyboard.down} and {@link Keyboard.up}.
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
* If `key` is a single character and no modifier keys besides `Shift`
|
||||||
|
* are being held down, a `keypress`/`input` event will also be generated.
|
||||||
|
* The `text` option can be specified to force an input event to be generated.
|
||||||
|
*
|
||||||
|
* **NOTE** Modifier keys DO affect `elementHandle.press`. Holding down `Shift`
|
||||||
|
* will type the text in upper case.
|
||||||
|
*
|
||||||
|
* @param key - Name of key to press, such as `ArrowLeft`.
|
||||||
|
* See {@link KeyInput} for a list of all key names.
|
||||||
|
*/
|
||||||
|
async press(key: KeyInput, options?: PressOptions): Promise<void>;
|
||||||
|
async press(): Promise<void> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the bounding box of the element (relative to the main frame),
|
||||||
|
* or `null` if the element is not visible.
|
||||||
|
*/
|
||||||
|
async boundingBox(): Promise<BoundingBox | null> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns boxes of the element, or `null` if the element is not visible.
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
*
|
||||||
|
* Boxes are represented as an array of points;
|
||||||
|
* Each Point is an object `{x, y}`. Box points are sorted clock-wise.
|
||||||
|
*/
|
||||||
|
async boxModel(): Promise<BoxModel | null> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method scrolls element into view if needed, and then uses
|
||||||
|
* {@link Page.screenshot} to take a screenshot of the element.
|
||||||
|
* If the element is detached from DOM, the method throws an error.
|
||||||
|
*/
|
||||||
|
async screenshot(
|
||||||
|
this: ElementHandle<Element>,
|
||||||
|
options?: ScreenshotOptions
|
||||||
|
): Promise<string | Buffer>;
|
||||||
|
async screenshot(this: ElementHandle<Element>): Promise<string | Buffer> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves to true if the element is visible in the current viewport.
|
||||||
|
*/
|
||||||
|
async isIntersectingViewport(
|
||||||
|
this: ElementHandle<Element>,
|
||||||
|
options?: {
|
||||||
|
threshold?: number;
|
||||||
|
}
|
||||||
|
): Promise<boolean>;
|
||||||
|
async isIntersectingViewport(): Promise<boolean> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,197 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2023 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Protocol from 'devtools-protocol';
|
||||||
|
|
||||||
|
import {CDPSession} from '../common/Connection.js';
|
||||||
|
import {ExecutionContext} from '../common/ExecutionContext.js';
|
||||||
|
import {EvaluateFuncWith, HandleFor, HandleOr} from '../common/types.js';
|
||||||
|
|
||||||
|
import {ElementHandle} from './ElementHandle.js';
|
||||||
|
|
||||||
|
declare const __JSHandleSymbol: unique symbol;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a reference to a JavaScript object. Instances can be created using
|
||||||
|
* {@link Page.evaluateHandle}.
|
||||||
|
*
|
||||||
|
* Handles prevent the referenced JavaScript object from being garbage-collected
|
||||||
|
* unless the handle is purposely {@link JSHandle.dispose | disposed}. JSHandles
|
||||||
|
* are auto-disposed when their associated frame is navigated away or the parent
|
||||||
|
* context gets destroyed.
|
||||||
|
*
|
||||||
|
* Handles can be used as arguments for any evaluation function such as
|
||||||
|
* {@link Page.$eval}, {@link Page.evaluate}, and {@link Page.evaluateHandle}.
|
||||||
|
* They are resolved to their referenced object.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* ```ts
|
||||||
|
* const windowHandle = await page.evaluateHandle(() => window);
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export class JSHandle<T = unknown> {
|
||||||
|
/**
|
||||||
|
* Used for nominally typing {@link JSHandle}.
|
||||||
|
*/
|
||||||
|
[__JSHandleSymbol]?: T;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
get disposed(): boolean {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
executionContext(): ExecutionContext {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
get client(): CDPSession {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluates the given function with the current handle as its first argument.
|
||||||
|
*/
|
||||||
|
async evaluate<
|
||||||
|
Params extends unknown[],
|
||||||
|
Func extends EvaluateFuncWith<T, Params> = EvaluateFuncWith<T, Params>
|
||||||
|
>(
|
||||||
|
pageFunction: Func | string,
|
||||||
|
...args: Params
|
||||||
|
): Promise<Awaited<ReturnType<Func>>>;
|
||||||
|
async evaluate(): Promise<unknown> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluates the given function with the current handle as its first argument.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
async evaluateHandle<
|
||||||
|
Params extends unknown[],
|
||||||
|
Func extends EvaluateFuncWith<T, Params> = EvaluateFuncWith<T, Params>
|
||||||
|
>(
|
||||||
|
pageFunction: Func | string,
|
||||||
|
...args: Params
|
||||||
|
): Promise<HandleFor<Awaited<ReturnType<Func>>>>;
|
||||||
|
async evaluateHandle(): Promise<HandleFor<unknown>> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches a single property from the referenced object.
|
||||||
|
*/
|
||||||
|
async getProperty<K extends keyof T>(
|
||||||
|
propertyName: HandleOr<K>
|
||||||
|
): Promise<HandleFor<T[K]>>;
|
||||||
|
async getProperty(propertyName: string): Promise<JSHandle<unknown>>;
|
||||||
|
async getProperty<K extends keyof T>(
|
||||||
|
propertyName: HandleOr<K>
|
||||||
|
): Promise<HandleFor<T[K]>>;
|
||||||
|
async getProperty<K extends keyof T>(): Promise<HandleFor<T[K]>> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a map of handles representing the properties of the current handle.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* ```ts
|
||||||
|
* const listHandle = await page.evaluateHandle(() => document.body.children);
|
||||||
|
* const properties = await listHandle.getProperties();
|
||||||
|
* const children = [];
|
||||||
|
* for (const property of properties.values()) {
|
||||||
|
* const element = property.asElement();
|
||||||
|
* if (element) {
|
||||||
|
* children.push(element);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* children; // holds elementHandles to all children of document.body
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
async getProperties(): Promise<Map<string, JSHandle>> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns A vanilla object representing the serializable portions of the
|
||||||
|
* referenced object.
|
||||||
|
* @throws Throws if the object cannot be serialized due to circularity.
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
* If the object has a `toJSON` function, it **will not** be called.
|
||||||
|
*/
|
||||||
|
async jsonValue(): Promise<T> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns Either `null` or the handle itself if the handle is an
|
||||||
|
* instance of {@link ElementHandle}.
|
||||||
|
*/
|
||||||
|
asElement(): ElementHandle<Node> | null {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Releases the object referenced by the handle for garbage collection.
|
||||||
|
*/
|
||||||
|
async dispose(): Promise<void> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string representation of the JSHandle.
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
* Useful during debugging.
|
||||||
|
*/
|
||||||
|
toString(): string {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
get id(): string | undefined {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides access to the
|
||||||
|
* [Protocol.Runtime.RemoteObject](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-RemoteObject)
|
||||||
|
* backing this handle.
|
||||||
|
*/
|
||||||
|
remoteObject(): Protocol.Runtime.RemoteObject {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,14 +14,15 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Protocol} from 'devtools-protocol';
|
|
||||||
import type {Readable} from 'stream';
|
import type {Readable} from 'stream';
|
||||||
|
|
||||||
|
import {Protocol} from 'devtools-protocol';
|
||||||
|
|
||||||
import type {Accessibility} from '../common/Accessibility.js';
|
import type {Accessibility} from '../common/Accessibility.js';
|
||||||
import type {ConsoleMessage} from '../common/ConsoleMessage.js';
|
import type {ConsoleMessage} from '../common/ConsoleMessage.js';
|
||||||
import type {Coverage} from '../common/Coverage.js';
|
import type {Coverage} from '../common/Coverage.js';
|
||||||
import {Device} from '../common/Device.js';
|
import {Device} from '../common/Device.js';
|
||||||
import type {Dialog} from '../common/Dialog.js';
|
import type {Dialog} from '../common/Dialog.js';
|
||||||
import type {ElementHandle} from '../common/ElementHandle.js';
|
|
||||||
import {EventEmitter, Handler} from '../common/EventEmitter.js';
|
import {EventEmitter, Handler} from '../common/EventEmitter.js';
|
||||||
import type {FileChooser} from '../common/FileChooser.js';
|
import type {FileChooser} from '../common/FileChooser.js';
|
||||||
import type {
|
import type {
|
||||||
|
|
@ -39,17 +40,24 @@ import type {
|
||||||
Touchscreen,
|
Touchscreen,
|
||||||
} from '../common/Input.js';
|
} from '../common/Input.js';
|
||||||
import type {WaitForSelectorOptions} from '../common/IsolatedWorld.js';
|
import type {WaitForSelectorOptions} from '../common/IsolatedWorld.js';
|
||||||
import type {JSHandle} from '../common/JSHandle.js';
|
|
||||||
import type {PuppeteerLifeCycleEvent} from '../common/LifecycleWatcher.js';
|
import type {PuppeteerLifeCycleEvent} from '../common/LifecycleWatcher.js';
|
||||||
import type {Credentials, NetworkConditions} from '../common/NetworkManager.js';
|
import type {Credentials, NetworkConditions} from '../common/NetworkManager.js';
|
||||||
import type {PDFOptions} from '../common/PDFOptions.js';
|
import type {PDFOptions} from '../common/PDFOptions.js';
|
||||||
import type {Viewport} from '../common/PuppeteerViewport.js';
|
import type {Viewport} from '../common/PuppeteerViewport.js';
|
||||||
import type {Target} from '../common/Target.js';
|
import type {Target} from '../common/Target.js';
|
||||||
import type {Tracing} from '../common/Tracing.js';
|
import type {Tracing} from '../common/Tracing.js';
|
||||||
import type {EvaluateFunc, HandleFor, NodeFor} from '../common/types.js';
|
import type {
|
||||||
|
EvaluateFunc,
|
||||||
|
EvaluateFuncWith,
|
||||||
|
HandleFor,
|
||||||
|
NodeFor,
|
||||||
|
} from '../common/types.js';
|
||||||
import type {WebWorker} from '../common/WebWorker.js';
|
import type {WebWorker} from '../common/WebWorker.js';
|
||||||
|
|
||||||
import type {Browser} from './Browser.js';
|
import type {Browser} from './Browser.js';
|
||||||
import type {BrowserContext} from './BrowserContext.js';
|
import type {BrowserContext} from './BrowserContext.js';
|
||||||
|
import type {ElementHandle} from './ElementHandle.js';
|
||||||
|
import type {JSHandle} from './JSHandle.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
|
|
@ -956,21 +964,16 @@ export class Page extends EventEmitter {
|
||||||
async $eval<
|
async $eval<
|
||||||
Selector extends string,
|
Selector extends string,
|
||||||
Params extends unknown[],
|
Params extends unknown[],
|
||||||
Func extends EvaluateFunc<
|
Func extends EvaluateFuncWith<NodeFor<Selector>, Params> = EvaluateFuncWith<
|
||||||
[ElementHandle<NodeFor<Selector>>, ...Params]
|
NodeFor<Selector>,
|
||||||
> = EvaluateFunc<[ElementHandle<NodeFor<Selector>>, ...Params]>
|
Params
|
||||||
|
>
|
||||||
>(
|
>(
|
||||||
selector: Selector,
|
selector: Selector,
|
||||||
pageFunction: Func | string,
|
pageFunction: Func | string,
|
||||||
...args: Params
|
...args: Params
|
||||||
): Promise<Awaited<ReturnType<Func>>>;
|
): Promise<Awaited<ReturnType<Func>>>;
|
||||||
async $eval<
|
async $eval(): Promise<unknown> {
|
||||||
Selector extends string,
|
|
||||||
Params extends unknown[],
|
|
||||||
Func extends EvaluateFunc<
|
|
||||||
[ElementHandle<NodeFor<Selector>>, ...Params]
|
|
||||||
> = EvaluateFunc<[ElementHandle<NodeFor<Selector>>, ...Params]>
|
|
||||||
>(): Promise<Awaited<ReturnType<Func>>> {
|
|
||||||
throw new Error('Not implemented');
|
throw new Error('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1039,21 +1042,16 @@ export class Page extends EventEmitter {
|
||||||
async $$eval<
|
async $$eval<
|
||||||
Selector extends string,
|
Selector extends string,
|
||||||
Params extends unknown[],
|
Params extends unknown[],
|
||||||
Func extends EvaluateFunc<
|
Func extends EvaluateFuncWith<
|
||||||
[Array<NodeFor<Selector>>, ...Params]
|
Array<NodeFor<Selector>>,
|
||||||
> = EvaluateFunc<[Array<NodeFor<Selector>>, ...Params]>
|
Params
|
||||||
|
> = EvaluateFuncWith<Array<NodeFor<Selector>>, Params>
|
||||||
>(
|
>(
|
||||||
selector: Selector,
|
selector: Selector,
|
||||||
pageFunction: Func | string,
|
pageFunction: Func | string,
|
||||||
...args: Params
|
...args: Params
|
||||||
): Promise<Awaited<ReturnType<Func>>>;
|
): Promise<Awaited<ReturnType<Func>>>;
|
||||||
async $$eval<
|
async $$eval(): Promise<unknown> {
|
||||||
Selector extends string,
|
|
||||||
Params extends unknown[],
|
|
||||||
Func extends EvaluateFunc<
|
|
||||||
[Array<NodeFor<Selector>>, ...Params]
|
|
||||||
> = EvaluateFunc<[Array<NodeFor<Selector>>, ...Params]>
|
|
||||||
>(): Promise<Awaited<ReturnType<Func>>> {
|
|
||||||
throw new Error('Not implemented');
|
throw new Error('Not implemented');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2465,7 +2463,7 @@ export class Page extends EventEmitter {
|
||||||
* @param options - Optional waiting parameters
|
* @param options - Optional waiting parameters
|
||||||
* @returns Promise which resolves when element specified by xpath string is
|
* @returns Promise which resolves when element specified by xpath string is
|
||||||
* added to DOM. Resolves to `null` if waiting for `hidden: true` and xpath is
|
* added to DOM. Resolves to `null` if waiting for `hidden: true` and xpath is
|
||||||
* not found in DOM.
|
* not found in DOM, otherwise resolves to `ElementHandle`.
|
||||||
* @remarks
|
* @remarks
|
||||||
* The optional Argument `options` have properties:
|
* The optional Argument `options` have properties:
|
||||||
*
|
*
|
||||||
|
|
@ -2483,11 +2481,7 @@ export class Page extends EventEmitter {
|
||||||
*/
|
*/
|
||||||
waitForXPath(
|
waitForXPath(
|
||||||
xpath: string,
|
xpath: string,
|
||||||
options?: {
|
options?: WaitForSelectorOptions
|
||||||
visible?: boolean;
|
|
||||||
hidden?: boolean;
|
|
||||||
timeout?: number;
|
|
||||||
}
|
|
||||||
): Promise<ElementHandle<Node> | null>;
|
): Promise<ElementHandle<Node> | null>;
|
||||||
waitForXPath(): Promise<ElementHandle<Node> | null> {
|
waitForXPath(): Promise<ElementHandle<Node> | null> {
|
||||||
throw new Error('Not implemented');
|
throw new Error('Not implemented');
|
||||||
|
|
|
||||||
|
|
@ -17,3 +17,5 @@
|
||||||
export * from './Browser.js';
|
export * from './Browser.js';
|
||||||
export * from './BrowserContext.js';
|
export * from './BrowserContext.js';
|
||||||
export * from './Page.js';
|
export * from './Page.js';
|
||||||
|
export * from './JSHandle.js';
|
||||||
|
export * from './ElementHandle.js';
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Protocol} from 'devtools-protocol';
|
import {Protocol} from 'devtools-protocol';
|
||||||
|
|
||||||
|
import {ElementHandle} from '../api/ElementHandle.js';
|
||||||
|
|
||||||
import {CDPSession} from './Connection.js';
|
import {CDPSession} from './Connection.js';
|
||||||
import {ElementHandle} from './ElementHandle.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a Node and the properties of it that are relevant to Accessibility.
|
* Represents a Node and the properties of it that are relevant to Accessibility.
|
||||||
|
|
@ -186,7 +188,7 @@ export class Accessibility {
|
||||||
let backendNodeId: number | undefined;
|
let backendNodeId: number | undefined;
|
||||||
if (root) {
|
if (root) {
|
||||||
const {node} = await this.#client.send('DOM.describeNode', {
|
const {node} = await this.#client.send('DOM.describeNode', {
|
||||||
objectId: root.remoteObject().objectId,
|
objectId: root.id,
|
||||||
});
|
});
|
||||||
backendNodeId = node.backendNodeId;
|
backendNodeId = node.backendNodeId;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,46 +16,42 @@
|
||||||
|
|
||||||
import {Protocol} from 'devtools-protocol';
|
import {Protocol} from 'devtools-protocol';
|
||||||
|
|
||||||
|
import {ElementHandle} from '../api/ElementHandle.js';
|
||||||
import {assert} from '../util/assert.js';
|
import {assert} from '../util/assert.js';
|
||||||
|
import {AsyncIterableUtil} from '../util/AsyncIterableUtil.js';
|
||||||
|
|
||||||
import {CDPSession} from './Connection.js';
|
import {CDPSession} from './Connection.js';
|
||||||
import {MAIN_WORLD, PUPPETEER_WORLD} from './IsolatedWorlds.js';
|
import {QueryHandler, QuerySelector} from './QueryHandler.js';
|
||||||
|
import {AwaitableIterable} from './types.js';
|
||||||
|
|
||||||
import type {ElementHandle} from './ElementHandle.js';
|
const queryAXTree = async (
|
||||||
import type {PuppeteerQueryHandler} from './QueryHandler.js';
|
|
||||||
import type {Frame} from './Frame.js';
|
|
||||||
|
|
||||||
async function queryAXTree(
|
|
||||||
client: CDPSession,
|
client: CDPSession,
|
||||||
element: ElementHandle<Node>,
|
element: ElementHandle<Node>,
|
||||||
accessibleName?: string,
|
accessibleName?: string,
|
||||||
role?: string
|
role?: string
|
||||||
): Promise<Protocol.Accessibility.AXNode[]> {
|
): Promise<Protocol.Accessibility.AXNode[]> => {
|
||||||
const {nodes} = await client.send('Accessibility.queryAXTree', {
|
const {nodes} = await client.send('Accessibility.queryAXTree', {
|
||||||
objectId: element.remoteObject().objectId,
|
objectId: element.id,
|
||||||
accessibleName,
|
accessibleName,
|
||||||
role,
|
role,
|
||||||
});
|
});
|
||||||
const filteredNodes: Protocol.Accessibility.AXNode[] = nodes.filter(
|
return nodes.filter((node: Protocol.Accessibility.AXNode) => {
|
||||||
(node: Protocol.Accessibility.AXNode) => {
|
|
||||||
return !node.role || node.role.value !== 'StaticText';
|
return !node.role || node.role.value !== 'StaticText';
|
||||||
}
|
});
|
||||||
);
|
};
|
||||||
return filteredNodes;
|
|
||||||
}
|
type ARIASelector = {name?: string; role?: string};
|
||||||
|
|
||||||
|
const KNOWN_ATTRIBUTES = Object.freeze(['name', 'role']);
|
||||||
|
const isKnownAttribute = (
|
||||||
|
attribute: string
|
||||||
|
): attribute is keyof ARIASelector => {
|
||||||
|
return KNOWN_ATTRIBUTES.includes(attribute);
|
||||||
|
};
|
||||||
|
|
||||||
const normalizeValue = (value: string): string => {
|
const normalizeValue = (value: string): string => {
|
||||||
return value.replace(/ +/g, ' ').trim();
|
return value.replace(/ +/g, ' ').trim();
|
||||||
};
|
};
|
||||||
const knownAttributes = new Set(['name', 'role']);
|
|
||||||
const attributeRegexp =
|
|
||||||
/\[\s*(?<attribute>\w+)\s*=\s*(?<quote>"|')(?<value>\\.|.*?(?=\k<quote>))\k<quote>\s*\]/g;
|
|
||||||
|
|
||||||
type ARIAQueryOption = {name?: string; role?: string};
|
|
||||||
function isKnownAttribute(
|
|
||||||
attribute: string
|
|
||||||
): attribute is keyof ARIAQueryOption {
|
|
||||||
return knownAttributes.has(attribute);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The selectors consist of an accessible name to query for and optionally
|
* The selectors consist of an accessible name to query for and optionally
|
||||||
|
|
@ -68,11 +64,13 @@ function isKnownAttribute(
|
||||||
* - 'label' queries for elements with name 'label' and any role.
|
* - 'label' queries for elements with name 'label' and any role.
|
||||||
* - '[name=""][role="button"]' queries for elements with no name and role 'button'.
|
* - '[name=""][role="button"]' queries for elements with no name and role 'button'.
|
||||||
*/
|
*/
|
||||||
function parseAriaSelector(selector: string): ARIAQueryOption {
|
const ATTRIBUTE_REGEXP =
|
||||||
const queryOptions: ARIAQueryOption = {};
|
/\[\s*(?<attribute>\w+)\s*=\s*(?<quote>"|')(?<value>\\.|.*?(?=\k<quote>))\k<quote>\s*\]/g;
|
||||||
|
const parseARIASelector = (selector: string): ARIASelector => {
|
||||||
|
const queryOptions: ARIASelector = {};
|
||||||
const defaultName = selector.replace(
|
const defaultName = selector.replace(
|
||||||
attributeRegexp,
|
ATTRIBUTE_REGEXP,
|
||||||
(_, attribute: string, _quote: string, value: string) => {
|
(_, attribute, __, value) => {
|
||||||
attribute = attribute.trim();
|
attribute = attribute.trim();
|
||||||
assert(
|
assert(
|
||||||
isKnownAttribute(attribute),
|
isKnownAttribute(attribute),
|
||||||
|
|
@ -86,104 +84,41 @@ function parseAriaSelector(selector: string): ARIAQueryOption {
|
||||||
queryOptions.name = normalizeValue(defaultName);
|
queryOptions.name = normalizeValue(defaultName);
|
||||||
}
|
}
|
||||||
return queryOptions;
|
return queryOptions;
|
||||||
}
|
|
||||||
|
|
||||||
const queryOneId = async (element: ElementHandle<Node>, selector: string) => {
|
|
||||||
const {name, role} = parseAriaSelector(selector);
|
|
||||||
const res = await queryAXTree(element.client, element, name, role);
|
|
||||||
if (!res[0] || !res[0].backendDOMNodeId) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return res[0].backendDOMNodeId;
|
|
||||||
};
|
|
||||||
|
|
||||||
const queryOne: PuppeteerQueryHandler['queryOne'] = async (
|
|
||||||
element,
|
|
||||||
selector
|
|
||||||
) => {
|
|
||||||
const id = await queryOneId(element, selector);
|
|
||||||
if (!id) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return (await element.frame.worlds[MAIN_WORLD].adoptBackendNode(
|
|
||||||
id
|
|
||||||
)) as ElementHandle<Node>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const waitFor: PuppeteerQueryHandler['waitFor'] = async (
|
|
||||||
elementOrFrame,
|
|
||||||
selector,
|
|
||||||
options
|
|
||||||
) => {
|
|
||||||
let frame: Frame;
|
|
||||||
let element: ElementHandle<Node> | undefined;
|
|
||||||
if ('isOOPFrame' in elementOrFrame) {
|
|
||||||
frame = elementOrFrame;
|
|
||||||
} else {
|
|
||||||
frame = elementOrFrame.frame;
|
|
||||||
element = await frame.worlds[PUPPETEER_WORLD].adoptHandle(elementOrFrame);
|
|
||||||
}
|
|
||||||
|
|
||||||
const ariaQuerySelector = async (selector: string) => {
|
|
||||||
const id = await queryOneId(
|
|
||||||
element || (await frame.worlds[PUPPETEER_WORLD].document()),
|
|
||||||
selector
|
|
||||||
);
|
|
||||||
if (!id) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return (await frame.worlds[PUPPETEER_WORLD].adoptBackendNode(
|
|
||||||
id
|
|
||||||
)) as ElementHandle<Node>;
|
|
||||||
};
|
|
||||||
|
|
||||||
const result = await frame.worlds[PUPPETEER_WORLD]._waitForSelectorInPage(
|
|
||||||
(_: Element, selector: string) => {
|
|
||||||
return (
|
|
||||||
globalThis as unknown as {
|
|
||||||
ariaQuerySelector(selector: string): Node | null;
|
|
||||||
}
|
|
||||||
).ariaQuerySelector(selector);
|
|
||||||
},
|
|
||||||
element,
|
|
||||||
selector,
|
|
||||||
options,
|
|
||||||
new Map([['ariaQuerySelector', ariaQuerySelector]])
|
|
||||||
);
|
|
||||||
if (element) {
|
|
||||||
await element.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
const handle = result?.asElement();
|
|
||||||
if (!handle) {
|
|
||||||
await result?.dispose();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return handle.frame.worlds[MAIN_WORLD].transferHandle(handle);
|
|
||||||
};
|
|
||||||
|
|
||||||
const queryAll: PuppeteerQueryHandler['queryAll'] = async (
|
|
||||||
element,
|
|
||||||
selector
|
|
||||||
) => {
|
|
||||||
const exeCtx = element.executionContext();
|
|
||||||
const {name, role} = parseAriaSelector(selector);
|
|
||||||
const res = await queryAXTree(exeCtx._client, element, name, role);
|
|
||||||
const world = exeCtx._world!;
|
|
||||||
return Promise.all(
|
|
||||||
res.map(axNode => {
|
|
||||||
return world.adoptBackendNode(axNode.backendDOMNodeId) as Promise<
|
|
||||||
ElementHandle<Node>
|
|
||||||
>;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export const ariaHandler: PuppeteerQueryHandler = {
|
export class ARIAQueryHandler extends QueryHandler {
|
||||||
queryOne,
|
static override querySelector: QuerySelector = async (
|
||||||
waitFor,
|
node,
|
||||||
queryAll,
|
selector,
|
||||||
};
|
{ariaQuerySelector}
|
||||||
|
) => {
|
||||||
|
return ariaQuerySelector(node, selector);
|
||||||
|
};
|
||||||
|
|
||||||
|
static override async *queryAll(
|
||||||
|
element: ElementHandle<Node>,
|
||||||
|
selector: string
|
||||||
|
): AwaitableIterable<ElementHandle<Node>> {
|
||||||
|
const context = element.executionContext();
|
||||||
|
const {name, role} = parseARIASelector(selector);
|
||||||
|
const results = await queryAXTree(context._client, element, name, role);
|
||||||
|
const world = context._world!;
|
||||||
|
yield* AsyncIterableUtil.map(results, node => {
|
||||||
|
return world.adoptBackendNode(node.backendDOMNodeId) as Promise<
|
||||||
|
ElementHandle<Node>
|
||||||
|
>;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static override queryOne = async (
|
||||||
|
element: ElementHandle<Node>,
|
||||||
|
selector: string
|
||||||
|
): Promise<ElementHandle<Node> | null> => {
|
||||||
|
return (
|
||||||
|
(await AsyncIterableUtil.first(this.queryAll(element, selector))) ?? null
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
import {JSHandle} from '../api/JSHandle.js';
|
||||||
|
import {isErrorLike} from '../util/ErrorLike.js';
|
||||||
|
|
||||||
|
import {ExecutionContext} from './ExecutionContext.js';
|
||||||
|
import {debugError} from './util.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export class Binding {
|
||||||
|
#name: string;
|
||||||
|
#fn: (...args: unknown[]) => unknown;
|
||||||
|
constructor(name: string, fn: (...args: unknown[]) => unknown) {
|
||||||
|
this.#name = name;
|
||||||
|
this.#fn = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
get name(): string {
|
||||||
|
return this.#name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param context - Context to run the binding in; the context should have
|
||||||
|
* the binding added to it beforehand.
|
||||||
|
* @param id - ID of the call. This should come from the CDP
|
||||||
|
* `onBindingCalled` response.
|
||||||
|
* @param args - Plain arguments from CDP.
|
||||||
|
*/
|
||||||
|
async run(
|
||||||
|
context: ExecutionContext,
|
||||||
|
id: number,
|
||||||
|
args: unknown[],
|
||||||
|
isTrivial: boolean
|
||||||
|
): Promise<void> {
|
||||||
|
const garbage = [];
|
||||||
|
try {
|
||||||
|
if (!isTrivial) {
|
||||||
|
// Getting non-trivial arguments.
|
||||||
|
const handles = await context.evaluateHandle(
|
||||||
|
(name, seq) => {
|
||||||
|
// @ts-expect-error Code is evaluated in a different context.
|
||||||
|
return globalThis[name].args.get(seq);
|
||||||
|
},
|
||||||
|
this.#name,
|
||||||
|
id
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
const properties = await handles.getProperties();
|
||||||
|
for (const [index, handle] of properties) {
|
||||||
|
// This is not straight-forward since some arguments can stringify, but
|
||||||
|
// aren't plain objects so add subtypes when the use-case arises.
|
||||||
|
if (index in args) {
|
||||||
|
switch (handle.remoteObject().subtype) {
|
||||||
|
case 'node':
|
||||||
|
args[+index] = handle;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
garbage.push(handle.dispose());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
garbage.push(handle.dispose());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
await handles.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await context.evaluate(
|
||||||
|
(name, seq, result) => {
|
||||||
|
// @ts-expect-error Code is evaluated in a different context.
|
||||||
|
const callbacks = globalThis[name].callbacks;
|
||||||
|
callbacks.get(seq).resolve(result);
|
||||||
|
callbacks.delete(seq);
|
||||||
|
},
|
||||||
|
this.#name,
|
||||||
|
id,
|
||||||
|
await this.#fn(...args)
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const arg of args) {
|
||||||
|
if (arg instanceof JSHandle) {
|
||||||
|
garbage.push(arg.dispose());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (isErrorLike(error)) {
|
||||||
|
await context
|
||||||
|
.evaluate(
|
||||||
|
(name, seq, message, stack) => {
|
||||||
|
const error = new Error(message);
|
||||||
|
error.stack = stack;
|
||||||
|
// @ts-expect-error Code is evaluated in a different context.
|
||||||
|
const callbacks = globalThis[name].callbacks;
|
||||||
|
callbacks.get(seq).reject(error);
|
||||||
|
callbacks.delete(seq);
|
||||||
|
},
|
||||||
|
this.#name,
|
||||||
|
id,
|
||||||
|
error.message,
|
||||||
|
error.stack
|
||||||
|
)
|
||||||
|
.catch(debugError);
|
||||||
|
} else {
|
||||||
|
await context
|
||||||
|
.evaluate(
|
||||||
|
(name, seq, error) => {
|
||||||
|
// @ts-expect-error Code is evaluated in a different context.
|
||||||
|
const callbacks = globalThis[name].callbacks;
|
||||||
|
callbacks.get(seq).reject(error);
|
||||||
|
callbacks.delete(seq);
|
||||||
|
},
|
||||||
|
this.#name,
|
||||||
|
id,
|
||||||
|
error
|
||||||
|
)
|
||||||
|
.catch(debugError);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
await Promise.all(garbage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,17 +15,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ChildProcess} from 'child_process';
|
import {ChildProcess} from 'child_process';
|
||||||
|
|
||||||
import {Protocol} from 'devtools-protocol';
|
import {Protocol} from 'devtools-protocol';
|
||||||
import {assert} from '../util/assert.js';
|
|
||||||
import {CDPSession, Connection, ConnectionEmittedEvents} from './Connection.js';
|
|
||||||
import {waitWithTimeout} from './util.js';
|
|
||||||
import {Page} from '../api/Page.js';
|
|
||||||
import {Viewport} from './PuppeteerViewport.js';
|
|
||||||
import {Target} from './Target.js';
|
|
||||||
import {TaskQueue} from './TaskQueue.js';
|
|
||||||
import {TargetManager, TargetManagerEmittedEvents} from './TargetManager.js';
|
|
||||||
import {ChromeTargetManager} from './ChromeTargetManager.js';
|
|
||||||
import {FirefoxTargetManager} from './FirefoxTargetManager.js';
|
|
||||||
import {
|
import {
|
||||||
Browser as BrowserBase,
|
Browser as BrowserBase,
|
||||||
BrowserCloseCallback,
|
BrowserCloseCallback,
|
||||||
|
|
@ -39,6 +31,17 @@ import {
|
||||||
Permission,
|
Permission,
|
||||||
} from '../api/Browser.js';
|
} from '../api/Browser.js';
|
||||||
import {BrowserContext} from '../api/BrowserContext.js';
|
import {BrowserContext} from '../api/BrowserContext.js';
|
||||||
|
import {Page} from '../api/Page.js';
|
||||||
|
import {assert} from '../util/assert.js';
|
||||||
|
|
||||||
|
import {ChromeTargetManager} from './ChromeTargetManager.js';
|
||||||
|
import {CDPSession, Connection, ConnectionEmittedEvents} from './Connection.js';
|
||||||
|
import {FirefoxTargetManager} from './FirefoxTargetManager.js';
|
||||||
|
import {Viewport} from './PuppeteerViewport.js';
|
||||||
|
import {Target} from './Target.js';
|
||||||
|
import {TargetManager, TargetManagerEmittedEvents} from './TargetManager.js';
|
||||||
|
import {TaskQueue} from './TaskQueue.js';
|
||||||
|
import {waitWithTimeout} from './util.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
|
|
|
||||||
|
|
@ -14,18 +14,18 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {debugError} from './util.js';
|
import {IsPageTargetCallback, TargetFilterCallback} from '../api/Browser.js';
|
||||||
import {isErrorLike} from '../util/ErrorLike.js';
|
|
||||||
import {isNode} from '../environment.js';
|
import {isNode} from '../environment.js';
|
||||||
import {assert} from '../util/assert.js';
|
import {assert} from '../util/assert.js';
|
||||||
import {IsPageTargetCallback, TargetFilterCallback} from '../api/Browser.js';
|
import {isErrorLike} from '../util/ErrorLike.js';
|
||||||
|
|
||||||
import {CDPBrowser} from './Browser.js';
|
import {CDPBrowser} from './Browser.js';
|
||||||
import {Connection} from './Connection.js';
|
import {Connection} from './Connection.js';
|
||||||
import {ConnectionTransport} from './ConnectionTransport.js';
|
import {ConnectionTransport} from './ConnectionTransport.js';
|
||||||
import {getFetch} from './fetch.js';
|
import {getFetch} from './fetch.js';
|
||||||
import {Viewport} from './PuppeteerViewport.js';
|
|
||||||
|
|
||||||
import type {ConnectOptions} from './Puppeteer.js';
|
import type {ConnectOptions} from './Puppeteer.js';
|
||||||
|
import {Viewport} from './PuppeteerViewport.js';
|
||||||
|
import {debugError} from './util.js';
|
||||||
/**
|
/**
|
||||||
* Generic browser options that can be passed when launching any browser or when
|
* Generic browser options that can be passed when launching any browser or when
|
||||||
* connecting to an existing browser instance.
|
* connecting to an existing browser instance.
|
||||||
|
|
|
||||||
|
|
@ -15,18 +15,20 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Protocol} from 'devtools-protocol';
|
import {Protocol} from 'devtools-protocol';
|
||||||
|
|
||||||
|
import {TargetFilterCallback} from '../api/Browser.js';
|
||||||
import {assert} from '../util/assert.js';
|
import {assert} from '../util/assert.js';
|
||||||
|
|
||||||
import {CDPSession, Connection} from './Connection.js';
|
import {CDPSession, Connection} from './Connection.js';
|
||||||
import {EventEmitter} from './EventEmitter.js';
|
import {EventEmitter} from './EventEmitter.js';
|
||||||
import {Target} from './Target.js';
|
import {Target} from './Target.js';
|
||||||
import {debugError} from './util.js';
|
|
||||||
import {TargetFilterCallback} from '../api/Browser.js';
|
|
||||||
import {
|
import {
|
||||||
TargetInterceptor,
|
TargetInterceptor,
|
||||||
TargetFactory,
|
TargetFactory,
|
||||||
TargetManager,
|
TargetManager,
|
||||||
TargetManagerEmittedEvents,
|
TargetManagerEmittedEvents,
|
||||||
} from './TargetManager.js';
|
} from './TargetManager.js';
|
||||||
|
import {debugError} from './util.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ChromeTargetManager uses the CDP's auto-attach mechanism to intercept
|
* ChromeTargetManager uses the CDP's auto-attach mechanism to intercept
|
||||||
|
|
|
||||||
|
|
@ -13,16 +13,20 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* eslint-disable import/order */
|
||||||
import {assert} from '../util/assert.js';
|
import {assert} from '../util/assert.js';
|
||||||
|
|
||||||
import {debug} from './Debug.js';
|
import {debug} from './Debug.js';
|
||||||
const debugProtocolSend = debug('puppeteer:protocol:SEND ►');
|
const debugProtocolSend = debug('puppeteer:protocol:SEND ►');
|
||||||
const debugProtocolReceive = debug('puppeteer:protocol:RECV ◀');
|
const debugProtocolReceive = debug('puppeteer:protocol:RECV ◀');
|
||||||
|
|
||||||
import {Protocol} from 'devtools-protocol';
|
import {Protocol} from 'devtools-protocol';
|
||||||
import {ProtocolMapping} from 'devtools-protocol/types/protocol-mapping.js';
|
import {ProtocolMapping} from 'devtools-protocol/types/protocol-mapping.js';
|
||||||
|
|
||||||
import {ConnectionTransport} from './ConnectionTransport.js';
|
import {ConnectionTransport} from './ConnectionTransport.js';
|
||||||
import {EventEmitter} from './EventEmitter.js';
|
|
||||||
import {ProtocolError} from './Errors.js';
|
import {ProtocolError} from './Errors.js';
|
||||||
|
import {EventEmitter} from './EventEmitter.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {JSHandle} from './JSHandle.js';
|
import {JSHandle} from '../api/JSHandle.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,13 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {assert} from '../util/assert.js';
|
|
||||||
import {addEventListener, debugError, PuppeteerEventListener} from './util.js';
|
|
||||||
import {Protocol} from 'devtools-protocol';
|
import {Protocol} from 'devtools-protocol';
|
||||||
import {CDPSession} from './Connection.js';
|
|
||||||
|
|
||||||
|
import {assert} from '../util/assert.js';
|
||||||
|
|
||||||
|
import {CDPSession} from './Connection.js';
|
||||||
import {EVALUATION_SCRIPT_URL} from './ExecutionContext.js';
|
import {EVALUATION_SCRIPT_URL} from './ExecutionContext.js';
|
||||||
|
import {addEventListener, debugError, PuppeteerEventListener} from './util.js';
|
||||||
import {removeEventListeners} from './util.js';
|
import {removeEventListeners} from './util.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,227 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2023 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type PuppeteerUtil from '../injected/injected.js';
|
||||||
|
import {assert} from '../util/assert.js';
|
||||||
|
import {interpolateFunction, stringifyFunction} from '../util/Function.js';
|
||||||
|
|
||||||
|
import {QueryHandler, QuerySelector, QuerySelectorAll} from './QueryHandler.js';
|
||||||
|
import {scriptInjector} from './ScriptInjector.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export interface CustomQueryHandler {
|
||||||
|
/**
|
||||||
|
* @returns A {@link Node} matching the given `selector` from {@link node}.
|
||||||
|
*/
|
||||||
|
queryOne?: (node: Node, selector: string) => Node | null;
|
||||||
|
/**
|
||||||
|
* @returns Some {@link Node}s matching the given `selector` from {@link node}.
|
||||||
|
*/
|
||||||
|
queryAll?: (node: Node, selector: string) => Iterable<Node>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The registry of {@link CustomQueryHandler | custom query handlers}.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* ```ts
|
||||||
|
* Puppeteer.customQueryHandlers.register('lit', { … });
|
||||||
|
* const aHandle = await page.$('lit/…');
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export class CustomQueryHandlerRegistry {
|
||||||
|
#handlers = new Map<
|
||||||
|
string,
|
||||||
|
[registerScript: string, Handler: typeof QueryHandler]
|
||||||
|
>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
get(name: string): typeof QueryHandler | undefined {
|
||||||
|
const handler = this.#handlers.get(name);
|
||||||
|
return handler ? handler[1] : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a {@link CustomQueryHandler | custom query handler}.
|
||||||
|
*
|
||||||
|
* @remarks
|
||||||
|
* After registration, the handler can be used everywhere where a selector is
|
||||||
|
* expected by prepending the selection string with `<name>/`. The name is
|
||||||
|
* only allowed to consist of lower- and upper case latin letters.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* ```ts
|
||||||
|
* Puppeteer.customQueryHandlers.register('lit', { … });
|
||||||
|
* const aHandle = await page.$('lit/…');
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @param name - Name to register under.
|
||||||
|
* @param queryHandler - {@link CustomQueryHandler | Custom query handler} to
|
||||||
|
* register.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
register(name: string, handler: CustomQueryHandler): void {
|
||||||
|
if (this.#handlers.has(name)) {
|
||||||
|
throw new Error(`Cannot register over existing handler: ${name}`);
|
||||||
|
}
|
||||||
|
assert(
|
||||||
|
!this.#handlers.has(name),
|
||||||
|
`Cannot register over existing handler: ${name}`
|
||||||
|
);
|
||||||
|
assert(
|
||||||
|
/^[a-zA-Z]+$/.test(name),
|
||||||
|
`Custom query handler names may only contain [a-zA-Z]`
|
||||||
|
);
|
||||||
|
assert(
|
||||||
|
handler.queryAll || handler.queryOne,
|
||||||
|
`At least one query method must be implemented.`
|
||||||
|
);
|
||||||
|
|
||||||
|
const Handler = class extends QueryHandler {
|
||||||
|
static override querySelectorAll: QuerySelectorAll = interpolateFunction(
|
||||||
|
(node, selector, PuppeteerUtil) => {
|
||||||
|
return PuppeteerUtil.customQuerySelectors
|
||||||
|
.get(PLACEHOLDER('name'))!
|
||||||
|
.querySelectorAll(node, selector);
|
||||||
|
},
|
||||||
|
{name: JSON.stringify(name)}
|
||||||
|
);
|
||||||
|
static override querySelector: QuerySelector = interpolateFunction(
|
||||||
|
(node, selector, PuppeteerUtil) => {
|
||||||
|
return PuppeteerUtil.customQuerySelectors
|
||||||
|
.get(PLACEHOLDER('name'))!
|
||||||
|
.querySelector(node, selector);
|
||||||
|
},
|
||||||
|
{name: JSON.stringify(name)}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
const registerScript = interpolateFunction(
|
||||||
|
(PuppeteerUtil: PuppeteerUtil) => {
|
||||||
|
PuppeteerUtil.customQuerySelectors.register(PLACEHOLDER('name'), {
|
||||||
|
queryAll: PLACEHOLDER('queryAll'),
|
||||||
|
queryOne: PLACEHOLDER('queryOne'),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: JSON.stringify(name),
|
||||||
|
queryAll: handler.queryAll
|
||||||
|
? stringifyFunction(handler.queryAll)
|
||||||
|
: String(undefined),
|
||||||
|
queryOne: handler.queryOne
|
||||||
|
? stringifyFunction(handler.queryOne)
|
||||||
|
: String(undefined),
|
||||||
|
}
|
||||||
|
).toString();
|
||||||
|
|
||||||
|
this.#handlers.set(name, [registerScript, Handler]);
|
||||||
|
scriptInjector.append(registerScript);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters the {@link CustomQueryHandler | custom query handler} for the
|
||||||
|
* given name.
|
||||||
|
*
|
||||||
|
* @throws `Error` if there is no handler under the given name.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
unregister(name: string): void {
|
||||||
|
const handler = this.#handlers.get(name);
|
||||||
|
if (!handler) {
|
||||||
|
throw new Error(`Cannot unregister unknown handler: ${name}`);
|
||||||
|
}
|
||||||
|
scriptInjector.pop(handler[0]);
|
||||||
|
this.#handlers.delete(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the names of all {@link CustomQueryHandler | custom query handlers}.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
names(): string[] {
|
||||||
|
return [...this.#handlers.keys()];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters all custom query handlers.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
clear(): void {
|
||||||
|
for (const [registerScript] of this.#handlers) {
|
||||||
|
scriptInjector.pop(registerScript);
|
||||||
|
}
|
||||||
|
this.#handlers.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export const customQueryHandlers = new CustomQueryHandlerRegistry();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Import {@link Puppeteer} and use the static method
|
||||||
|
* {@link Puppeteer.registerCustomQueryHandler}
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export function registerCustomQueryHandler(
|
||||||
|
name: string,
|
||||||
|
handler: CustomQueryHandler
|
||||||
|
): void {
|
||||||
|
customQueryHandlers.register(name, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Import {@link Puppeteer} and use the static method
|
||||||
|
* {@link Puppeteer.unregisterCustomQueryHandler}
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export function unregisterCustomQueryHandler(name: string): void {
|
||||||
|
customQueryHandlers.unregister(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Import {@link Puppeteer} and use the static method
|
||||||
|
* {@link Puppeteer.customQueryHandlerNames}
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export function customQueryHandlerNames(): string[] {
|
||||||
|
return customQueryHandlers.names();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Import {@link Puppeteer} and use the static method
|
||||||
|
* {@link Puppeteer.clearCustomQueryHandlers}
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export function clearCustomQueryHandlers(): void {
|
||||||
|
customQueryHandlers.clear();
|
||||||
|
}
|
||||||
|
|
@ -14,10 +14,12 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {assert} from '../util/assert.js';
|
|
||||||
import {CDPSession} from './Connection.js';
|
|
||||||
import {Protocol} from 'devtools-protocol';
|
import {Protocol} from 'devtools-protocol';
|
||||||
|
|
||||||
|
import {assert} from '../util/assert.js';
|
||||||
|
|
||||||
|
import {CDPSession} from './Connection.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dialog instances are dispatched by the {@link Page} via the `dialog` event.
|
* Dialog instances are dispatched by the {@link Page} via the `dialog` event.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -15,26 +15,38 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Protocol} from 'devtools-protocol';
|
import {Protocol} from 'devtools-protocol';
|
||||||
import {assert} from '../util/assert.js';
|
|
||||||
import {ExecutionContext} from './ExecutionContext.js';
|
|
||||||
import {Frame} from './Frame.js';
|
|
||||||
import {FrameManager} from './FrameManager.js';
|
|
||||||
import {WaitForSelectorOptions} from './IsolatedWorld.js';
|
|
||||||
import {
|
import {
|
||||||
BoundingBox,
|
BoundingBox,
|
||||||
BoxModel,
|
BoxModel,
|
||||||
ClickOptions,
|
ClickOptions,
|
||||||
JSHandle,
|
ElementHandle,
|
||||||
Offset,
|
Offset,
|
||||||
Point,
|
Point,
|
||||||
PressOptions,
|
PressOptions,
|
||||||
} from './JSHandle.js';
|
} from '../api/ElementHandle.js';
|
||||||
|
import {JSHandle} from '../api/JSHandle.js';
|
||||||
import {Page, ScreenshotOptions} from '../api/Page.js';
|
import {Page, ScreenshotOptions} from '../api/Page.js';
|
||||||
import {getQueryHandlerAndSelector} from './QueryHandler.js';
|
import {assert} from '../util/assert.js';
|
||||||
import {ElementFor, EvaluateFunc, HandleFor, NodeFor} from './types.js';
|
import {AsyncIterableUtil} from '../util/AsyncIterableUtil.js';
|
||||||
|
|
||||||
|
import {CDPSession} from './Connection.js';
|
||||||
|
import {ExecutionContext} from './ExecutionContext.js';
|
||||||
|
import {Frame} from './Frame.js';
|
||||||
|
import {FrameManager} from './FrameManager.js';
|
||||||
|
import {getQueryHandlerAndSelector} from './GetQueryHandler.js';
|
||||||
|
import {WaitForSelectorOptions} from './IsolatedWorld.js';
|
||||||
|
import {CDPJSHandle} from './JSHandle.js';
|
||||||
|
import {CDPPage} from './Page.js';
|
||||||
|
import {
|
||||||
|
ElementFor,
|
||||||
|
EvaluateFuncWith,
|
||||||
|
HandleFor,
|
||||||
|
HandleOr,
|
||||||
|
NodeFor,
|
||||||
|
} from './types.js';
|
||||||
import {KeyInput} from './USKeyboardLayout.js';
|
import {KeyInput} from './USKeyboardLayout.js';
|
||||||
import {debugError, isString} from './util.js';
|
import {debugError, isString} from './util.js';
|
||||||
import {CDPPage} from './Page.js';
|
|
||||||
|
|
||||||
const applyOffsetsToQuad = (
|
const applyOffsetsToQuad = (
|
||||||
quad: Point[],
|
quad: Point[],
|
||||||
|
|
@ -47,56 +59,76 @@ const applyOffsetsToQuad = (
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ElementHandle represents an in-page DOM element.
|
* The CDPElementHandle extends ElementHandle now to keep compatibility
|
||||||
|
* with `instanceof` because of that we need to have methods for
|
||||||
|
* CDPJSHandle to in this implementation as well.
|
||||||
*
|
*
|
||||||
* @remarks
|
|
||||||
* ElementHandles can be created with the {@link Page.$} method.
|
|
||||||
*
|
|
||||||
* ```ts
|
|
||||||
* import puppeteer from 'puppeteer';
|
|
||||||
*
|
|
||||||
* (async () => {
|
|
||||||
* const browser = await puppeteer.launch();
|
|
||||||
* const page = await browser.newPage();
|
|
||||||
* await page.goto('https://example.com');
|
|
||||||
* const hrefElement = await page.$('a');
|
|
||||||
* await hrefElement.click();
|
|
||||||
* // ...
|
|
||||||
* })();
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* ElementHandle prevents the DOM element from being garbage-collected unless the
|
|
||||||
* handle is {@link JSHandle.dispose | disposed}. ElementHandles are auto-disposed
|
|
||||||
* when their origin frame gets navigated.
|
|
||||||
*
|
|
||||||
* ElementHandle instances can be used as arguments in {@link Page.$eval} and
|
|
||||||
* {@link Page.evaluate} methods.
|
|
||||||
*
|
|
||||||
* If you're using TypeScript, ElementHandle takes a generic argument that
|
|
||||||
* denotes the type of element the handle is holding within. For example, if you
|
|
||||||
* have a handle to a `<select>` element, you can type it as
|
|
||||||
* `ElementHandle<HTMLSelectElement>` and you get some nicer type checks.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
|
|
||||||
export class ElementHandle<
|
|
||||||
ElementType extends Node = Element
|
|
||||||
> extends JSHandle<ElementType> {
|
|
||||||
#frame: Frame;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
|
export class CDPElementHandle<
|
||||||
|
ElementType extends Node = Element
|
||||||
|
> extends ElementHandle<ElementType> {
|
||||||
|
#frame: Frame;
|
||||||
|
#jsHandle: CDPJSHandle<ElementType>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
context: ExecutionContext,
|
context: ExecutionContext,
|
||||||
remoteObject: Protocol.Runtime.RemoteObject,
|
remoteObject: Protocol.Runtime.RemoteObject,
|
||||||
frame: Frame
|
frame: Frame
|
||||||
) {
|
) {
|
||||||
super(context, remoteObject);
|
super();
|
||||||
|
this.#jsHandle = new CDPJSHandle(context, remoteObject);
|
||||||
this.#frame = frame;
|
this.#frame = frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
override executionContext(): ExecutionContext {
|
||||||
|
return this.#jsHandle.executionContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
override get client(): CDPSession {
|
||||||
|
return this.#jsHandle.client;
|
||||||
|
}
|
||||||
|
|
||||||
|
override get id(): string | undefined {
|
||||||
|
return this.#jsHandle.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
override remoteObject(): Protocol.Runtime.RemoteObject {
|
||||||
|
return this.#jsHandle.remoteObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
override async evaluate<
|
||||||
|
Params extends unknown[],
|
||||||
|
Func extends EvaluateFuncWith<ElementType, Params> = EvaluateFuncWith<
|
||||||
|
ElementType,
|
||||||
|
Params
|
||||||
|
>
|
||||||
|
>(
|
||||||
|
pageFunction: Func | string,
|
||||||
|
...args: Params
|
||||||
|
): Promise<Awaited<ReturnType<Func>>> {
|
||||||
|
return this.executionContext().evaluate(pageFunction, this, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
override evaluateHandle<
|
||||||
|
Params extends unknown[],
|
||||||
|
Func extends EvaluateFuncWith<ElementType, Params> = EvaluateFuncWith<
|
||||||
|
ElementType,
|
||||||
|
Params
|
||||||
|
>
|
||||||
|
>(
|
||||||
|
pageFunction: Func | string,
|
||||||
|
...args: Params
|
||||||
|
): Promise<HandleFor<Awaited<ReturnType<Func>>>> {
|
||||||
|
return this.executionContext().evaluateHandle(pageFunction, this, ...args);
|
||||||
|
}
|
||||||
|
|
||||||
get #frameManager(): FrameManager {
|
get #frameManager(): FrameManager {
|
||||||
return this.#frame._frameManager;
|
return this.#frame._frameManager;
|
||||||
}
|
}
|
||||||
|
|
@ -105,85 +137,72 @@ export class ElementHandle<
|
||||||
return this.#frame.page();
|
return this.#frame.page();
|
||||||
}
|
}
|
||||||
|
|
||||||
get frame(): Frame {
|
override get frame(): Frame {
|
||||||
return this.#frame;
|
return this.#frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override get disposed(): boolean {
|
||||||
* Queries the current element for an element matching the given selector.
|
return this.#jsHandle.disposed;
|
||||||
*
|
}
|
||||||
* @param selector - The selector to query for.
|
|
||||||
* @returns A {@link ElementHandle | element handle} to the first element
|
override async getProperty<K extends keyof ElementType>(
|
||||||
* matching the given selector. Otherwise, `null`.
|
propertyName: HandleOr<K>
|
||||||
*/
|
): Promise<HandleFor<ElementType[K]>>;
|
||||||
async $<Selector extends string>(
|
override async getProperty(propertyName: string): Promise<JSHandle<unknown>>;
|
||||||
|
override async getProperty<K extends keyof ElementType>(
|
||||||
|
propertyName: HandleOr<K>
|
||||||
|
): Promise<HandleFor<ElementType[K]>> {
|
||||||
|
return this.#jsHandle.getProperty(propertyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
override async getProperties(): Promise<Map<string, JSHandle>> {
|
||||||
|
return this.#jsHandle.getProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
override asElement(): CDPElementHandle<ElementType> | null {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
override async jsonValue(): Promise<ElementType> {
|
||||||
|
return this.#jsHandle.jsonValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
override toString(): string {
|
||||||
|
return this.#jsHandle.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
override async dispose(): Promise<void> {
|
||||||
|
return await this.#jsHandle.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
override async $<Selector extends string>(
|
||||||
selector: Selector
|
selector: Selector
|
||||||
): Promise<ElementHandle<NodeFor<Selector>> | null> {
|
): Promise<CDPElementHandle<NodeFor<Selector>> | null> {
|
||||||
const {updatedSelector, queryHandler} =
|
const {updatedSelector, QueryHandler} =
|
||||||
getQueryHandlerAndSelector(selector);
|
getQueryHandlerAndSelector(selector);
|
||||||
assert(
|
return (await QueryHandler.queryOne(
|
||||||
queryHandler.queryOne,
|
|
||||||
'Cannot handle queries for a single element with the given selector'
|
|
||||||
);
|
|
||||||
return (await queryHandler.queryOne(
|
|
||||||
this,
|
this,
|
||||||
updatedSelector
|
updatedSelector
|
||||||
)) as ElementHandle<NodeFor<Selector>> | null;
|
)) as CDPElementHandle<NodeFor<Selector>> | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override async $$<Selector extends string>(
|
||||||
* Queries the current element for all elements matching the given selector.
|
|
||||||
*
|
|
||||||
* @param selector - The selector to query for.
|
|
||||||
* @returns An array of {@link ElementHandle | element handles} that point to
|
|
||||||
* elements matching the given selector.
|
|
||||||
*/
|
|
||||||
async $$<Selector extends string>(
|
|
||||||
selector: Selector
|
selector: Selector
|
||||||
): Promise<Array<ElementHandle<NodeFor<Selector>>>> {
|
): Promise<Array<CDPElementHandle<NodeFor<Selector>>>> {
|
||||||
const {updatedSelector, queryHandler} =
|
const {updatedSelector, QueryHandler} =
|
||||||
getQueryHandlerAndSelector(selector);
|
getQueryHandlerAndSelector(selector);
|
||||||
assert(
|
return AsyncIterableUtil.collect(
|
||||||
queryHandler.queryAll,
|
QueryHandler.queryAll(this, updatedSelector)
|
||||||
'Cannot handle queries for a multiple element with the given selector'
|
) as Promise<Array<CDPElementHandle<NodeFor<Selector>>>>;
|
||||||
);
|
|
||||||
return (await queryHandler.queryAll(this, updatedSelector)) as Array<
|
|
||||||
ElementHandle<NodeFor<Selector>>
|
|
||||||
>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override async $eval<
|
||||||
* Runs the given function on the first element matching the given selector in
|
|
||||||
* the current element.
|
|
||||||
*
|
|
||||||
* If the given function returns a promise, then this method will wait till
|
|
||||||
* the promise resolves.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
*
|
|
||||||
* ```ts
|
|
||||||
* const tweetHandle = await page.$('.tweet');
|
|
||||||
* expect(await tweetHandle.$eval('.like', node => node.innerText)).toBe(
|
|
||||||
* '100'
|
|
||||||
* );
|
|
||||||
* expect(await tweetHandle.$eval('.retweets', node => node.innerText)).toBe(
|
|
||||||
* '10'
|
|
||||||
* );
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* @param selector - The selector to query for.
|
|
||||||
* @param pageFunction - The function to be evaluated in this element's page's
|
|
||||||
* context. The first element matching the selector will be passed in as the
|
|
||||||
* first argument.
|
|
||||||
* @param args - Additional arguments to pass to `pageFunction`.
|
|
||||||
* @returns A promise to the result of the function.
|
|
||||||
*/
|
|
||||||
async $eval<
|
|
||||||
Selector extends string,
|
Selector extends string,
|
||||||
Params extends unknown[],
|
Params extends unknown[],
|
||||||
Func extends EvaluateFunc<
|
Func extends EvaluateFuncWith<NodeFor<Selector>, Params> = EvaluateFuncWith<
|
||||||
[ElementHandle<NodeFor<Selector>>, ...Params]
|
NodeFor<Selector>,
|
||||||
> = EvaluateFunc<[ElementHandle<NodeFor<Selector>>, ...Params]>
|
Params
|
||||||
|
>
|
||||||
>(
|
>(
|
||||||
selector: Selector,
|
selector: Selector,
|
||||||
pageFunction: Func | string,
|
pageFunction: Func | string,
|
||||||
|
|
@ -200,238 +219,69 @@ export class ElementHandle<
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override async $$eval<
|
||||||
* Runs the given function on an array of elements matching the given selector
|
|
||||||
* in the current element.
|
|
||||||
*
|
|
||||||
* If the given function returns a promise, then this method will wait till
|
|
||||||
* the promise resolves.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* HTML:
|
|
||||||
*
|
|
||||||
* ```html
|
|
||||||
* <div class="feed">
|
|
||||||
* <div class="tweet">Hello!</div>
|
|
||||||
* <div class="tweet">Hi!</div>
|
|
||||||
* </div>
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* JavaScript:
|
|
||||||
*
|
|
||||||
* ```js
|
|
||||||
* const feedHandle = await page.$('.feed');
|
|
||||||
* expect(
|
|
||||||
* await feedHandle.$$eval('.tweet', nodes => nodes.map(n => n.innerText))
|
|
||||||
* ).toEqual(['Hello!', 'Hi!']);
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* @param selector - The selector to query for.
|
|
||||||
* @param pageFunction - The function to be evaluated in the element's page's
|
|
||||||
* context. An array of elements matching the given selector will be passed to
|
|
||||||
* the function as its first argument.
|
|
||||||
* @param args - Additional arguments to pass to `pageFunction`.
|
|
||||||
* @returns A promise to the result of the function.
|
|
||||||
*/
|
|
||||||
async $$eval<
|
|
||||||
Selector extends string,
|
Selector extends string,
|
||||||
Params extends unknown[],
|
Params extends unknown[],
|
||||||
Func extends EvaluateFunc<
|
Func extends EvaluateFuncWith<
|
||||||
[HandleFor<Array<NodeFor<Selector>>>, ...Params]
|
Array<NodeFor<Selector>>,
|
||||||
> = EvaluateFunc<[HandleFor<Array<NodeFor<Selector>>>, ...Params]>
|
Params
|
||||||
|
> = EvaluateFuncWith<Array<NodeFor<Selector>>, Params>
|
||||||
>(
|
>(
|
||||||
selector: Selector,
|
selector: Selector,
|
||||||
pageFunction: Func | string,
|
pageFunction: Func | string,
|
||||||
...args: Params
|
...args: Params
|
||||||
): Promise<Awaited<ReturnType<Func>>> {
|
): Promise<Awaited<ReturnType<Func>>> {
|
||||||
const {updatedSelector, queryHandler} =
|
const results = await this.$$(selector);
|
||||||
getQueryHandlerAndSelector(selector);
|
const elements = await this.evaluateHandle((_, ...elements) => {
|
||||||
assert(
|
|
||||||
queryHandler.queryAll,
|
|
||||||
'Cannot handle queries for a multiple element with the given selector'
|
|
||||||
);
|
|
||||||
const handles = (await queryHandler.queryAll(
|
|
||||||
this,
|
|
||||||
updatedSelector
|
|
||||||
)) as Array<HandleFor<NodeFor<Selector>>>;
|
|
||||||
const elements = (await this.evaluateHandle((_, ...elements) => {
|
|
||||||
return elements;
|
return elements;
|
||||||
}, ...handles)) as JSHandle<Array<NodeFor<Selector>>>;
|
}, ...results);
|
||||||
const [result] = await Promise.all([
|
const [result] = await Promise.all([
|
||||||
elements.evaluate(pageFunction, ...args),
|
elements.evaluate(pageFunction, ...args),
|
||||||
...handles.map(handle => {
|
...results.map(results => {
|
||||||
return handle.dispose();
|
return results.dispose();
|
||||||
}),
|
}),
|
||||||
]);
|
]);
|
||||||
await elements.dispose();
|
await elements.dispose();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override async $x(
|
||||||
* @deprecated Use {@link ElementHandle.$$} with the `xpath` prefix.
|
expression: string
|
||||||
*
|
): Promise<Array<CDPElementHandle<Node>>> {
|
||||||
* Example: `await elementHandle.$$('xpath/' + xpathExpression)`
|
|
||||||
*
|
|
||||||
* The method evaluates the XPath expression relative to the elementHandle.
|
|
||||||
* If `xpath` starts with `//` instead of `.//`, the dot will be appended
|
|
||||||
* automatically.
|
|
||||||
*
|
|
||||||
* If there are no such elements, the method will resolve to an empty array.
|
|
||||||
* @param expression - Expression to {@link https://developer.mozilla.org/en-US/docs/Web/API/Document/evaluate | evaluate}
|
|
||||||
*/
|
|
||||||
async $x(expression: string): Promise<Array<ElementHandle<Node>>> {
|
|
||||||
if (expression.startsWith('//')) {
|
if (expression.startsWith('//')) {
|
||||||
expression = `.${expression}`;
|
expression = `.${expression}`;
|
||||||
}
|
}
|
||||||
return this.$$(`xpath/${expression}`);
|
return this.$$(`xpath/${expression}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override async waitForSelector<Selector extends string>(
|
||||||
* Wait for an element matching the given selector to appear in the current
|
|
||||||
* element.
|
|
||||||
*
|
|
||||||
* Unlike {@link Frame.waitForSelector}, this method does not work across
|
|
||||||
* navigations or if the element is detached from DOM.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
*
|
|
||||||
* ```ts
|
|
||||||
* import puppeteer from 'puppeteer';
|
|
||||||
*
|
|
||||||
* (async () => {
|
|
||||||
* const browser = await puppeteer.launch();
|
|
||||||
* const page = await browser.newPage();
|
|
||||||
* let currentURL;
|
|
||||||
* page
|
|
||||||
* .mainFrame()
|
|
||||||
* .waitForSelector('img')
|
|
||||||
* .then(() => console.log('First URL with image: ' + currentURL));
|
|
||||||
*
|
|
||||||
* for (currentURL of [
|
|
||||||
* 'https://example.com',
|
|
||||||
* 'https://google.com',
|
|
||||||
* 'https://bbc.com',
|
|
||||||
* ]) {
|
|
||||||
* await page.goto(currentURL);
|
|
||||||
* }
|
|
||||||
* await browser.close();
|
|
||||||
* })();
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* @param selector - The selector to query and wait for.
|
|
||||||
* @param options - Options for customizing waiting behavior.
|
|
||||||
* @returns An element matching the given selector.
|
|
||||||
* @throws Throws if an element matching the given selector doesn't appear.
|
|
||||||
*/
|
|
||||||
async waitForSelector<Selector extends string>(
|
|
||||||
selector: Selector,
|
selector: Selector,
|
||||||
options: WaitForSelectorOptions = {}
|
options: WaitForSelectorOptions = {}
|
||||||
): Promise<ElementHandle<NodeFor<Selector>> | null> {
|
): Promise<CDPElementHandle<NodeFor<Selector>> | null> {
|
||||||
const {updatedSelector, queryHandler} =
|
const {updatedSelector, QueryHandler} =
|
||||||
getQueryHandlerAndSelector(selector);
|
getQueryHandlerAndSelector(selector);
|
||||||
assert(queryHandler.waitFor, 'Query handler does not support waiting');
|
return (await QueryHandler.waitFor(
|
||||||
return (await queryHandler.waitFor(
|
|
||||||
this,
|
this,
|
||||||
updatedSelector,
|
updatedSelector,
|
||||||
options
|
options
|
||||||
)) as ElementHandle<NodeFor<Selector>> | null;
|
)) as CDPElementHandle<NodeFor<Selector>> | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override async waitForXPath(
|
||||||
* @deprecated Use {@link ElementHandle.waitForSelector} with the `xpath`
|
|
||||||
* prefix.
|
|
||||||
*
|
|
||||||
* Example: `await elementHandle.waitForSelector('xpath/' + xpathExpression)`
|
|
||||||
*
|
|
||||||
* The method evaluates the XPath expression relative to the elementHandle.
|
|
||||||
*
|
|
||||||
* Wait for the `xpath` within the element. If at the moment of calling the
|
|
||||||
* method the `xpath` already exists, the method will return immediately. If
|
|
||||||
* the `xpath` doesn't appear after the `timeout` milliseconds of waiting, the
|
|
||||||
* function will throw.
|
|
||||||
*
|
|
||||||
* If `xpath` starts with `//` instead of `.//`, the dot will be appended
|
|
||||||
* automatically.
|
|
||||||
*
|
|
||||||
* This method works across navigation.
|
|
||||||
*
|
|
||||||
* ```ts
|
|
||||||
* import puppeteer from 'puppeteer';
|
|
||||||
* (async () => {
|
|
||||||
* const browser = await puppeteer.launch();
|
|
||||||
* const page = await browser.newPage();
|
|
||||||
* let currentURL;
|
|
||||||
* page
|
|
||||||
* .waitForXPath('//img')
|
|
||||||
* .then(() => console.log('First URL with image: ' + currentURL));
|
|
||||||
* for (currentURL of [
|
|
||||||
* 'https://example.com',
|
|
||||||
* 'https://google.com',
|
|
||||||
* 'https://bbc.com',
|
|
||||||
* ]) {
|
|
||||||
* await page.goto(currentURL);
|
|
||||||
* }
|
|
||||||
* await browser.close();
|
|
||||||
* })();
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* @param xpath - A
|
|
||||||
* {@link https://developer.mozilla.org/en-US/docs/Web/XPath | xpath} of an
|
|
||||||
* element to wait for
|
|
||||||
* @param options - Optional waiting parameters
|
|
||||||
* @returns Promise which resolves when element specified by xpath string is
|
|
||||||
* added to DOM. Resolves to `null` if waiting for `hidden: true` and xpath is
|
|
||||||
* not found in DOM.
|
|
||||||
* @remarks
|
|
||||||
* The optional Argument `options` have properties:
|
|
||||||
*
|
|
||||||
* - `visible`: A boolean to wait for element to be present in DOM and to be
|
|
||||||
* visible, i.e. to not have `display: none` or `visibility: hidden` CSS
|
|
||||||
* properties. Defaults to `false`.
|
|
||||||
*
|
|
||||||
* - `hidden`: A boolean wait for element to not be found in the DOM or to be
|
|
||||||
* hidden, i.e. have `display: none` or `visibility: hidden` CSS properties.
|
|
||||||
* Defaults to `false`.
|
|
||||||
*
|
|
||||||
* - `timeout`: A number which is maximum time to wait for in milliseconds.
|
|
||||||
* Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The
|
|
||||||
* default value can be changed by using the {@link Page.setDefaultTimeout}
|
|
||||||
* method.
|
|
||||||
*/
|
|
||||||
async waitForXPath(
|
|
||||||
xpath: string,
|
xpath: string,
|
||||||
options: {
|
options: {
|
||||||
visible?: boolean;
|
visible?: boolean;
|
||||||
hidden?: boolean;
|
hidden?: boolean;
|
||||||
timeout?: number;
|
timeout?: number;
|
||||||
} = {}
|
} = {}
|
||||||
): Promise<ElementHandle<Node> | null> {
|
): Promise<CDPElementHandle<Node> | null> {
|
||||||
if (xpath.startsWith('//')) {
|
if (xpath.startsWith('//')) {
|
||||||
xpath = `.${xpath}`;
|
xpath = `.${xpath}`;
|
||||||
}
|
}
|
||||||
return this.waitForSelector(`xpath/${xpath}`, options);
|
return this.waitForSelector(`xpath/${xpath}`, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override async toElement<
|
||||||
* Converts the current handle to the given element type.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
*
|
|
||||||
* ```ts
|
|
||||||
* const element: ElementHandle<Element> = await page.$(
|
|
||||||
* '.class-name-of-anchor'
|
|
||||||
* );
|
|
||||||
* // DO NOT DISPOSE `element`, this will be always be the same handle.
|
|
||||||
* const anchor: ElementHandle<HTMLAnchorElement> = await element.toElement(
|
|
||||||
* 'a'
|
|
||||||
* );
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* @param tagName - The tag name of the desired element type.
|
|
||||||
* @throws An error if the handle does not match. **The handle will not be
|
|
||||||
* automatically disposed.**
|
|
||||||
*/
|
|
||||||
async toElement<
|
|
||||||
K extends keyof HTMLElementTagNameMap | keyof SVGElementTagNameMap
|
K extends keyof HTMLElementTagNameMap | keyof SVGElementTagNameMap
|
||||||
>(tagName: K): Promise<HandleFor<ElementFor<K>>> {
|
>(tagName: K): Promise<HandleFor<ElementFor<K>>> {
|
||||||
const isMatchingTagName = await this.evaluate((node, tagName) => {
|
const isMatchingTagName = await this.evaluate((node, tagName) => {
|
||||||
|
|
@ -443,15 +293,7 @@ export class ElementHandle<
|
||||||
return this as unknown as HandleFor<ElementFor<K>>;
|
return this as unknown as HandleFor<ElementFor<K>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
override asElement(): ElementHandle<ElementType> | null {
|
override async contentFrame(): Promise<Frame | null> {
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolves to the content frame for element handles referencing
|
|
||||||
* iframe nodes, or null otherwise
|
|
||||||
*/
|
|
||||||
async contentFrame(): Promise<Frame | null> {
|
|
||||||
const nodeInfo = await this.client.send('DOM.describeNode', {
|
const nodeInfo = await this.client.send('DOM.describeNode', {
|
||||||
objectId: this.remoteObject().objectId,
|
objectId: this.remoteObject().objectId,
|
||||||
});
|
});
|
||||||
|
|
@ -461,7 +303,9 @@ export class ElementHandle<
|
||||||
return this.#frameManager.frame(nodeInfo.node.frameId);
|
return this.#frameManager.frame(nodeInfo.node.frameId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async #scrollIntoViewIfNeeded(this: ElementHandle<Element>): Promise<void> {
|
async #scrollIntoViewIfNeeded(
|
||||||
|
this: CDPElementHandle<Element>
|
||||||
|
): Promise<void> {
|
||||||
const error = await this.evaluate(
|
const error = await this.evaluate(
|
||||||
async (element): Promise<string | undefined> => {
|
async (element): Promise<string | undefined> => {
|
||||||
if (!element.isConnected) {
|
if (!element.isConnected) {
|
||||||
|
|
@ -541,10 +385,7 @@ export class ElementHandle<
|
||||||
return {offsetX, offsetY};
|
return {offsetX, offsetY};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override async clickablePoint(offset?: Offset): Promise<Point> {
|
||||||
* Returns the middle point within an element unless a specific offset is provided.
|
|
||||||
*/
|
|
||||||
async clickablePoint(offset?: Offset): Promise<Point> {
|
|
||||||
const [result, layoutMetrics] = await Promise.all([
|
const [result, layoutMetrics] = await Promise.all([
|
||||||
this.client
|
this.client
|
||||||
.send('DOM.getContentQuads', {
|
.send('DOM.getContentQuads', {
|
||||||
|
|
@ -615,7 +456,7 @@ export class ElementHandle<
|
||||||
|
|
||||||
#getBoxModel(): Promise<void | Protocol.DOM.GetBoxModelResponse> {
|
#getBoxModel(): Promise<void | Protocol.DOM.GetBoxModelResponse> {
|
||||||
const params: Protocol.DOM.GetBoxModelRequest = {
|
const params: Protocol.DOM.GetBoxModelRequest = {
|
||||||
objectId: this.remoteObject().objectId,
|
objectId: this.id,
|
||||||
};
|
};
|
||||||
return this.client.send('DOM.getBoxModel', params).catch(error => {
|
return this.client.send('DOM.getBoxModel', params).catch(error => {
|
||||||
return debugError(error);
|
return debugError(error);
|
||||||
|
|
@ -649,7 +490,7 @@ export class ElementHandle<
|
||||||
* uses {@link Page.mouse} to hover over the center of the element.
|
* uses {@link Page.mouse} to hover over the center of the element.
|
||||||
* If the element is detached from DOM, the method throws an error.
|
* If the element is detached from DOM, the method throws an error.
|
||||||
*/
|
*/
|
||||||
async hover(this: ElementHandle<Element>): Promise<void> {
|
override async hover(this: CDPElementHandle<Element>): Promise<void> {
|
||||||
await this.#scrollIntoViewIfNeeded();
|
await this.#scrollIntoViewIfNeeded();
|
||||||
const {x, y} = await this.clickablePoint();
|
const {x, y} = await this.clickablePoint();
|
||||||
await this.#page.mouse.move(x, y);
|
await this.#page.mouse.move(x, y);
|
||||||
|
|
@ -660,8 +501,8 @@ export class ElementHandle<
|
||||||
* uses {@link Page.mouse} to click in the center of the element.
|
* uses {@link Page.mouse} to click in the center of the element.
|
||||||
* If the element is detached from DOM, the method throws an error.
|
* If the element is detached from DOM, the method throws an error.
|
||||||
*/
|
*/
|
||||||
async click(
|
override async click(
|
||||||
this: ElementHandle<Element>,
|
this: CDPElementHandle<Element>,
|
||||||
options: ClickOptions = {}
|
options: ClickOptions = {}
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await this.#scrollIntoViewIfNeeded();
|
await this.#scrollIntoViewIfNeeded();
|
||||||
|
|
@ -672,8 +513,8 @@ export class ElementHandle<
|
||||||
/**
|
/**
|
||||||
* This method creates and captures a dragevent from the element.
|
* This method creates and captures a dragevent from the element.
|
||||||
*/
|
*/
|
||||||
async drag(
|
override async drag(
|
||||||
this: ElementHandle<Element>,
|
this: CDPElementHandle<Element>,
|
||||||
target: Point
|
target: Point
|
||||||
): Promise<Protocol.Input.DragData> {
|
): Promise<Protocol.Input.DragData> {
|
||||||
assert(
|
assert(
|
||||||
|
|
@ -685,11 +526,8 @@ export class ElementHandle<
|
||||||
return await this.#page.mouse.drag(start, target);
|
return await this.#page.mouse.drag(start, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override async dragEnter(
|
||||||
* This method creates a `dragenter` event on the element.
|
this: CDPElementHandle<Element>,
|
||||||
*/
|
|
||||||
async dragEnter(
|
|
||||||
this: ElementHandle<Element>,
|
|
||||||
data: Protocol.Input.DragData = {items: [], dragOperationsMask: 1}
|
data: Protocol.Input.DragData = {items: [], dragOperationsMask: 1}
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await this.#scrollIntoViewIfNeeded();
|
await this.#scrollIntoViewIfNeeded();
|
||||||
|
|
@ -697,11 +535,8 @@ export class ElementHandle<
|
||||||
await this.#page.mouse.dragEnter(target, data);
|
await this.#page.mouse.dragEnter(target, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override async dragOver(
|
||||||
* This method creates a `dragover` event on the element.
|
this: CDPElementHandle<Element>,
|
||||||
*/
|
|
||||||
async dragOver(
|
|
||||||
this: ElementHandle<Element>,
|
|
||||||
data: Protocol.Input.DragData = {items: [], dragOperationsMask: 1}
|
data: Protocol.Input.DragData = {items: [], dragOperationsMask: 1}
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await this.#scrollIntoViewIfNeeded();
|
await this.#scrollIntoViewIfNeeded();
|
||||||
|
|
@ -709,11 +544,8 @@ export class ElementHandle<
|
||||||
await this.#page.mouse.dragOver(target, data);
|
await this.#page.mouse.dragOver(target, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override async drop(
|
||||||
* This method triggers a drop on the element.
|
this: CDPElementHandle<Element>,
|
||||||
*/
|
|
||||||
async drop(
|
|
||||||
this: ElementHandle<Element>,
|
|
||||||
data: Protocol.Input.DragData = {items: [], dragOperationsMask: 1}
|
data: Protocol.Input.DragData = {items: [], dragOperationsMask: 1}
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await this.#scrollIntoViewIfNeeded();
|
await this.#scrollIntoViewIfNeeded();
|
||||||
|
|
@ -721,12 +553,9 @@ export class ElementHandle<
|
||||||
await this.#page.mouse.drop(destination, data);
|
await this.#page.mouse.drop(destination, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override async dragAndDrop(
|
||||||
* This method triggers a dragenter, dragover, and drop on the element.
|
this: CDPElementHandle<Element>,
|
||||||
*/
|
target: CDPElementHandle<Node>,
|
||||||
async dragAndDrop(
|
|
||||||
this: ElementHandle<Element>,
|
|
||||||
target: ElementHandle<Node>,
|
|
||||||
options?: {delay: number}
|
options?: {delay: number}
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await this.#scrollIntoViewIfNeeded();
|
await this.#scrollIntoViewIfNeeded();
|
||||||
|
|
@ -735,23 +564,7 @@ export class ElementHandle<
|
||||||
await this.#page.mouse.dragAndDrop(startPoint, targetPoint, options);
|
await this.#page.mouse.dragAndDrop(startPoint, targetPoint, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override async select(...values: string[]): Promise<string[]> {
|
||||||
* Triggers a `change` and `input` event once all the provided options have been
|
|
||||||
* selected. If there's no `<select>` element matching `selector`, the method
|
|
||||||
* throws an error.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
*
|
|
||||||
* ```ts
|
|
||||||
* handle.select('blue'); // single selection
|
|
||||||
* handle.select('red', 'green', 'blue'); // multiple selections
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* @param values - Values of options to select. If the `<select>` has the
|
|
||||||
* `multiple` attribute, all values are considered, otherwise only the first
|
|
||||||
* one is taken into account.
|
|
||||||
*/
|
|
||||||
async select(...values: string[]): Promise<string[]> {
|
|
||||||
for (const value of values) {
|
for (const value of values) {
|
||||||
assert(
|
assert(
|
||||||
isString(value),
|
isString(value),
|
||||||
|
|
@ -795,18 +608,8 @@ export class ElementHandle<
|
||||||
}, values);
|
}, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override async uploadFile(
|
||||||
* This method expects `elementHandle` to point to an
|
this: CDPElementHandle<HTMLInputElement>,
|
||||||
* {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input | input element}.
|
|
||||||
*
|
|
||||||
* @param filePaths - Sets the value of the file input to these paths.
|
|
||||||
* If a path is relative, then it is resolved against the
|
|
||||||
* {@link https://nodejs.org/api/process.html#process_process_cwd | current working directory}.
|
|
||||||
* Note for locals script connecting to remote chrome environments,
|
|
||||||
* paths must be absolute.
|
|
||||||
*/
|
|
||||||
async uploadFile(
|
|
||||||
this: ElementHandle<HTMLInputElement>,
|
|
||||||
...filePaths: string[]
|
...filePaths: string[]
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const isMultiple = await this.evaluate(element => {
|
const isMultiple = await this.evaluate(element => {
|
||||||
|
|
@ -837,7 +640,9 @@ export class ElementHandle<
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const {objectId} = this.remoteObject();
|
const {objectId} = this.remoteObject();
|
||||||
const {node} = await this.client.send('DOM.describeNode', {objectId});
|
const {node} = await this.client.send('DOM.describeNode', {
|
||||||
|
objectId,
|
||||||
|
});
|
||||||
const {backendNodeId} = node;
|
const {backendNodeId} = node;
|
||||||
|
|
||||||
/* The zero-length array is a special case, it seems that
|
/* The zero-length array is a special case, it seems that
|
||||||
|
|
@ -861,21 +666,31 @@ export class ElementHandle<
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override async tap(this: CDPElementHandle<Element>): Promise<void> {
|
||||||
* This method scrolls element into view if needed, and then uses
|
|
||||||
* {@link Touchscreen.tap} to tap in the center of the element.
|
|
||||||
* If the element is detached from DOM, the method throws an error.
|
|
||||||
*/
|
|
||||||
async tap(this: ElementHandle<Element>): Promise<void> {
|
|
||||||
await this.#scrollIntoViewIfNeeded();
|
await this.#scrollIntoViewIfNeeded();
|
||||||
const {x, y} = await this.clickablePoint();
|
const {x, y} = await this.clickablePoint();
|
||||||
await this.#page.touchscreen.tap(x, y);
|
await this.#page.touchscreen.touchStart(x, y);
|
||||||
|
await this.#page.touchscreen.touchEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override async touchStart(this: CDPElementHandle<Element>): Promise<void> {
|
||||||
* Calls {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus | focus} on the element.
|
await this.#scrollIntoViewIfNeeded();
|
||||||
*/
|
const {x, y} = await this.clickablePoint();
|
||||||
async focus(): Promise<void> {
|
await this.#page.touchscreen.touchStart(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
override async touchMove(this: CDPElementHandle<Element>): Promise<void> {
|
||||||
|
await this.#scrollIntoViewIfNeeded();
|
||||||
|
const {x, y} = await this.clickablePoint();
|
||||||
|
await this.#page.touchscreen.touchMove(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
override async touchEnd(this: CDPElementHandle<Element>): Promise<void> {
|
||||||
|
await this.#scrollIntoViewIfNeeded();
|
||||||
|
await this.#page.touchscreen.touchEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
override async focus(): Promise<void> {
|
||||||
await this.evaluate(element => {
|
await this.evaluate(element => {
|
||||||
if (!(element instanceof HTMLElement)) {
|
if (!(element instanceof HTMLElement)) {
|
||||||
throw new Error('Cannot focus non-HTMLElement');
|
throw new Error('Cannot focus non-HTMLElement');
|
||||||
|
|
@ -884,58 +699,17 @@ export class ElementHandle<
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override async type(text: string, options?: {delay: number}): Promise<void> {
|
||||||
* Focuses the element, and then sends a `keydown`, `keypress`/`input`, and
|
|
||||||
* `keyup` event for each character in the text.
|
|
||||||
*
|
|
||||||
* To press a special key, like `Control` or `ArrowDown`,
|
|
||||||
* use {@link ElementHandle.press}.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
*
|
|
||||||
* ```ts
|
|
||||||
* await elementHandle.type('Hello'); // Types instantly
|
|
||||||
* await elementHandle.type('World', {delay: 100}); // Types slower, like a user
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* An example of typing into a text field and then submitting the form:
|
|
||||||
*
|
|
||||||
* ```ts
|
|
||||||
* const elementHandle = await page.$('input');
|
|
||||||
* await elementHandle.type('some text');
|
|
||||||
* await elementHandle.press('Enter');
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
async type(text: string, options?: {delay: number}): Promise<void> {
|
|
||||||
await this.focus();
|
await this.focus();
|
||||||
await this.#page.keyboard.type(text, options);
|
await this.#page.keyboard.type(text, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override async press(key: KeyInput, options?: PressOptions): Promise<void> {
|
||||||
* Focuses the element, and then uses {@link Keyboard.down} and {@link Keyboard.up}.
|
|
||||||
*
|
|
||||||
* @remarks
|
|
||||||
* If `key` is a single character and no modifier keys besides `Shift`
|
|
||||||
* are being held down, a `keypress`/`input` event will also be generated.
|
|
||||||
* The `text` option can be specified to force an input event to be generated.
|
|
||||||
*
|
|
||||||
* **NOTE** Modifier keys DO affect `elementHandle.press`. Holding down `Shift`
|
|
||||||
* will type the text in upper case.
|
|
||||||
*
|
|
||||||
* @param key - Name of key to press, such as `ArrowLeft`.
|
|
||||||
* See {@link KeyInput} for a list of all key names.
|
|
||||||
*/
|
|
||||||
async press(key: KeyInput, options?: PressOptions): Promise<void> {
|
|
||||||
await this.focus();
|
await this.focus();
|
||||||
await this.#page.keyboard.press(key, options);
|
await this.#page.keyboard.press(key, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override async boundingBox(): Promise<BoundingBox | null> {
|
||||||
* This method returns the bounding box of the element (relative to the main frame),
|
|
||||||
* or `null` if the element is not visible.
|
|
||||||
*/
|
|
||||||
async boundingBox(): Promise<BoundingBox | null> {
|
|
||||||
const result = await this.#getBoxModel();
|
const result = await this.#getBoxModel();
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
|
|
@ -952,15 +726,7 @@ export class ElementHandle<
|
||||||
return {x: x + offsetX, y: y + offsetY, width, height};
|
return {x: x + offsetX, y: y + offsetY, width, height};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override async boxModel(): Promise<BoxModel | null> {
|
||||||
* This method returns boxes of the element, or `null` if the element is not visible.
|
|
||||||
*
|
|
||||||
* @remarks
|
|
||||||
*
|
|
||||||
* Boxes are represented as an array of points;
|
|
||||||
* Each Point is an object `{x, y}`. Box points are sorted clock-wise.
|
|
||||||
*/
|
|
||||||
async boxModel(): Promise<BoxModel | null> {
|
|
||||||
const result = await this.#getBoxModel();
|
const result = await this.#getBoxModel();
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
|
|
@ -996,13 +762,8 @@ export class ElementHandle<
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override async screenshot(
|
||||||
* This method scrolls element into view if needed, and then uses
|
this: CDPElementHandle<Element>,
|
||||||
* {@link Page.screenshot} to take a screenshot of the element.
|
|
||||||
* If the element is detached from DOM, the method throws an error.
|
|
||||||
*/
|
|
||||||
async screenshot(
|
|
||||||
this: ElementHandle<Element>,
|
|
||||||
options: ScreenshotOptions = {}
|
options: ScreenshotOptions = {}
|
||||||
): Promise<string | Buffer> {
|
): Promise<string | Buffer> {
|
||||||
let needsViewportReset = false;
|
let needsViewportReset = false;
|
||||||
|
|
@ -1059,11 +820,8 @@ export class ElementHandle<
|
||||||
return imageData;
|
return imageData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override async isIntersectingViewport(
|
||||||
* Resolves to true if the element is visible in the current viewport.
|
this: CDPElementHandle<Element>,
|
||||||
*/
|
|
||||||
async isIntersectingViewport(
|
|
||||||
this: ElementHandle<Element>,
|
|
||||||
options?: {
|
options?: {
|
||||||
threshold?: number;
|
threshold?: number;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,10 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
import {Protocol} from 'devtools-protocol';
|
||||||
|
|
||||||
import {CDPSession} from './Connection.js';
|
import {CDPSession} from './Connection.js';
|
||||||
import {Viewport} from './PuppeteerViewport.js';
|
import {Viewport} from './PuppeteerViewport.js';
|
||||||
import {Protocol} from 'devtools-protocol';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,21 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Protocol} from 'devtools-protocol';
|
import {Protocol} from 'devtools-protocol';
|
||||||
|
|
||||||
|
import type {ElementHandle} from '../api/ElementHandle.js';
|
||||||
|
import {JSHandle} from '../api/JSHandle.js';
|
||||||
|
import type PuppeteerUtil from '../injected/injected.js';
|
||||||
|
import {AsyncIterableUtil} from '../util/AsyncIterableUtil.js';
|
||||||
|
import {stringifyFunction} from '../util/Function.js';
|
||||||
|
|
||||||
|
import {ARIAQueryHandler} from './AriaQueryHandler.js';
|
||||||
|
import {Binding} from './Binding.js';
|
||||||
import {CDPSession} from './Connection.js';
|
import {CDPSession} from './Connection.js';
|
||||||
|
import {CDPElementHandle} from './ElementHandle.js';
|
||||||
import {IsolatedWorld} from './IsolatedWorld.js';
|
import {IsolatedWorld} from './IsolatedWorld.js';
|
||||||
import {JSHandle} from './JSHandle.js';
|
import {CDPJSHandle} from './JSHandle.js';
|
||||||
import {LazyArg} from './LazyArg.js';
|
import {LazyArg} from './LazyArg.js';
|
||||||
|
import {scriptInjector} from './ScriptInjector.js';
|
||||||
import {EvaluateFunc, HandleFor} from './types.js';
|
import {EvaluateFunc, HandleFor} from './types.js';
|
||||||
import {
|
import {
|
||||||
createJSHandle,
|
createJSHandle,
|
||||||
|
|
@ -71,7 +82,7 @@ export class ExecutionContext {
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
_contextName: string;
|
_contextName?: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
|
|
@ -84,8 +95,56 @@ export class ExecutionContext {
|
||||||
this._client = client;
|
this._client = client;
|
||||||
this._world = world;
|
this._world = world;
|
||||||
this._contextId = contextPayload.id;
|
this._contextId = contextPayload.id;
|
||||||
|
if (contextPayload.name) {
|
||||||
this._contextName = contextPayload.name;
|
this._contextName = contextPayload.name;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#puppeteerUtil?: Promise<JSHandle<PuppeteerUtil>>;
|
||||||
|
get puppeteerUtil(): Promise<JSHandle<PuppeteerUtil>> {
|
||||||
|
scriptInjector.inject(script => {
|
||||||
|
if (this.#puppeteerUtil) {
|
||||||
|
this.#puppeteerUtil.then(handle => {
|
||||||
|
handle.dispose();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.#puppeteerUtil = Promise.all([
|
||||||
|
this.#installGlobalBinding(
|
||||||
|
new Binding(
|
||||||
|
'__ariaQuerySelector',
|
||||||
|
ARIAQueryHandler.queryOne as (...args: unknown[]) => unknown
|
||||||
|
)
|
||||||
|
),
|
||||||
|
this.#installGlobalBinding(
|
||||||
|
new Binding('__ariaQuerySelectorAll', (async (
|
||||||
|
element: ElementHandle<Node>,
|
||||||
|
selector: string
|
||||||
|
): Promise<JSHandle<Node[]>> => {
|
||||||
|
const results = ARIAQueryHandler.queryAll(element, selector);
|
||||||
|
return element.executionContext().evaluateHandle((...elements) => {
|
||||||
|
return elements;
|
||||||
|
}, ...(await AsyncIterableUtil.collect(results)));
|
||||||
|
}) as (...args: unknown[]) => unknown)
|
||||||
|
),
|
||||||
|
]).then(() => {
|
||||||
|
return this.evaluateHandle(script) as Promise<JSHandle<PuppeteerUtil>>;
|
||||||
|
});
|
||||||
|
}, !this.#puppeteerUtil);
|
||||||
|
return this.#puppeteerUtil as Promise<JSHandle<PuppeteerUtil>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
async #installGlobalBinding(binding: Binding) {
|
||||||
|
try {
|
||||||
|
if (this._world) {
|
||||||
|
this._world._bindings.set(binding.name, binding);
|
||||||
|
await this._world._addBindingToContext(this, binding.name);
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// If the binding cannot be added, then either the browser doesn't support
|
||||||
|
// bindings (e.g. Firefox) or the context is broken. Either breakage is
|
||||||
|
// okay, so we ignore the error.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Evaluates the given function.
|
* Evaluates the given function.
|
||||||
|
|
@ -250,29 +309,10 @@ export class ExecutionContext {
|
||||||
: createJSHandle(this, remoteObject);
|
: createJSHandle(this, remoteObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
let functionText = pageFunction.toString();
|
|
||||||
try {
|
|
||||||
new Function('(' + functionText + ')');
|
|
||||||
} catch (error) {
|
|
||||||
// This means we might have a function shorthand. Try another
|
|
||||||
// time prefixing 'function '.
|
|
||||||
if (functionText.startsWith('async ')) {
|
|
||||||
functionText =
|
|
||||||
'async function ' + functionText.substring('async '.length);
|
|
||||||
} else {
|
|
||||||
functionText = 'function ' + functionText;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
new Function('(' + functionText + ')');
|
|
||||||
} catch (error) {
|
|
||||||
// We tried hard to serialize, but there's a weird beast here.
|
|
||||||
throw new Error('Passed function is not well-serializable!');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let callFunctionOnPromise;
|
let callFunctionOnPromise;
|
||||||
try {
|
try {
|
||||||
callFunctionOnPromise = this._client.send('Runtime.callFunctionOn', {
|
callFunctionOnPromise = this._client.send('Runtime.callFunctionOn', {
|
||||||
functionDeclaration: functionText + '\n' + suffix + '\n',
|
functionDeclaration: `${stringifyFunction(pageFunction)}\n${suffix}\n`,
|
||||||
executionContextId: this._contextId,
|
executionContextId: this._contextId,
|
||||||
arguments: await Promise.all(args.map(convertArgument.bind(this))),
|
arguments: await Promise.all(args.map(convertArgument.bind(this))),
|
||||||
returnByValue,
|
returnByValue,
|
||||||
|
|
@ -304,7 +344,7 @@ export class ExecutionContext {
|
||||||
arg: unknown
|
arg: unknown
|
||||||
): Promise<Protocol.Runtime.CallArgument> {
|
): Promise<Protocol.Runtime.CallArgument> {
|
||||||
if (arg instanceof LazyArg) {
|
if (arg instanceof LazyArg) {
|
||||||
arg = await arg.get();
|
arg = await arg.get(this);
|
||||||
}
|
}
|
||||||
if (typeof arg === 'bigint') {
|
if (typeof arg === 'bigint') {
|
||||||
// eslint-disable-line valid-typeof
|
// eslint-disable-line valid-typeof
|
||||||
|
|
@ -322,7 +362,10 @@ export class ExecutionContext {
|
||||||
if (Object.is(arg, NaN)) {
|
if (Object.is(arg, NaN)) {
|
||||||
return {unserializableValue: 'NaN'};
|
return {unserializableValue: 'NaN'};
|
||||||
}
|
}
|
||||||
const objectHandle = arg && arg instanceof JSHandle ? arg : null;
|
const objectHandle =
|
||||||
|
arg && (arg instanceof CDPJSHandle || arg instanceof CDPElementHandle)
|
||||||
|
? arg
|
||||||
|
: null;
|
||||||
if (objectHandle) {
|
if (objectHandle) {
|
||||||
if (objectHandle.executionContext() !== this) {
|
if (objectHandle.executionContext() !== this) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Protocol} from 'devtools-protocol';
|
import {Protocol} from 'devtools-protocol';
|
||||||
|
|
||||||
|
import {ElementHandle} from '../api/ElementHandle.js';
|
||||||
import {assert} from '../util/assert.js';
|
import {assert} from '../util/assert.js';
|
||||||
import {ElementHandle} from './ElementHandle.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* File choosers let you react to the page requesting for a file.
|
* File choosers let you react to the page requesting for a file.
|
||||||
|
|
|
||||||
|
|
@ -15,17 +15,19 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Protocol} from 'devtools-protocol';
|
import {Protocol} from 'devtools-protocol';
|
||||||
import {assert} from '../util/assert.js';
|
|
||||||
import {CDPSession, Connection} from './Connection.js';
|
|
||||||
import {Target} from './Target.js';
|
|
||||||
import {TargetFilterCallback} from '../api/Browser.js';
|
import {TargetFilterCallback} from '../api/Browser.js';
|
||||||
|
import {assert} from '../util/assert.js';
|
||||||
|
|
||||||
|
import {CDPSession, Connection} from './Connection.js';
|
||||||
|
import {EventEmitter} from './EventEmitter.js';
|
||||||
|
import {Target} from './Target.js';
|
||||||
import {
|
import {
|
||||||
TargetFactory,
|
TargetFactory,
|
||||||
TargetInterceptor,
|
TargetInterceptor,
|
||||||
TargetManagerEmittedEvents,
|
TargetManagerEmittedEvents,
|
||||||
TargetManager,
|
TargetManager,
|
||||||
} from './TargetManager.js';
|
} from './TargetManager.js';
|
||||||
import {EventEmitter} from './EventEmitter.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FirefoxTargetManager implements target management using
|
* FirefoxTargetManager implements target management using
|
||||||
|
|
|
||||||
|
|
@ -15,12 +15,15 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Protocol} from 'devtools-protocol';
|
import {Protocol} from 'devtools-protocol';
|
||||||
import {assert} from '../util/assert.js';
|
|
||||||
|
import {ElementHandle} from '../api/ElementHandle.js';
|
||||||
|
import {Page} from '../api/Page.js';
|
||||||
import {isErrorLike} from '../util/ErrorLike.js';
|
import {isErrorLike} from '../util/ErrorLike.js';
|
||||||
|
|
||||||
import {CDPSession} from './Connection.js';
|
import {CDPSession} from './Connection.js';
|
||||||
import {ElementHandle} from './ElementHandle.js';
|
|
||||||
import {ExecutionContext} from './ExecutionContext.js';
|
import {ExecutionContext} from './ExecutionContext.js';
|
||||||
import {FrameManager} from './FrameManager.js';
|
import {FrameManager} from './FrameManager.js';
|
||||||
|
import {getQueryHandlerAndSelector} from './GetQueryHandler.js';
|
||||||
import {HTTPResponse} from './HTTPResponse.js';
|
import {HTTPResponse} from './HTTPResponse.js';
|
||||||
import {MouseButton} from './Input.js';
|
import {MouseButton} from './Input.js';
|
||||||
import {
|
import {
|
||||||
|
|
@ -29,10 +32,9 @@ import {
|
||||||
WaitForSelectorOptions,
|
WaitForSelectorOptions,
|
||||||
} from './IsolatedWorld.js';
|
} from './IsolatedWorld.js';
|
||||||
import {MAIN_WORLD, PUPPETEER_WORLD} from './IsolatedWorlds.js';
|
import {MAIN_WORLD, PUPPETEER_WORLD} from './IsolatedWorlds.js';
|
||||||
|
import {LazyArg} from './LazyArg.js';
|
||||||
import {LifecycleWatcher, PuppeteerLifeCycleEvent} from './LifecycleWatcher.js';
|
import {LifecycleWatcher, PuppeteerLifeCycleEvent} from './LifecycleWatcher.js';
|
||||||
import {Page} from '../api/Page.js';
|
import {EvaluateFunc, EvaluateFuncWith, HandleFor, NodeFor} from './types.js';
|
||||||
import {getQueryHandlerAndSelector} from './QueryHandler.js';
|
|
||||||
import {EvaluateFunc, HandleFor, NodeFor} from './types.js';
|
|
||||||
import {importFS} from './util.js';
|
import {importFS} from './util.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -353,6 +355,9 @@ export class Frame {
|
||||||
referrerPolicy,
|
referrerPolicy,
|
||||||
});
|
});
|
||||||
ensureNewDocumentNavigation = !!response.loaderId;
|
ensureNewDocumentNavigation = !!response.loaderId;
|
||||||
|
if (response.errorText === 'net::ERR_HTTP_RESPONSE_CODE_FAILURE') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return response.errorText
|
return response.errorText
|
||||||
? new Error(`${response.errorText} at ${url}`)
|
? new Error(`${response.errorText} at ${url}`)
|
||||||
: null;
|
: null;
|
||||||
|
|
@ -514,9 +519,10 @@ export class Frame {
|
||||||
async $eval<
|
async $eval<
|
||||||
Selector extends string,
|
Selector extends string,
|
||||||
Params extends unknown[],
|
Params extends unknown[],
|
||||||
Func extends EvaluateFunc<
|
Func extends EvaluateFuncWith<NodeFor<Selector>, Params> = EvaluateFuncWith<
|
||||||
[ElementHandle<NodeFor<Selector>>, ...Params]
|
NodeFor<Selector>,
|
||||||
> = EvaluateFunc<[ElementHandle<NodeFor<Selector>>, ...Params]>
|
Params
|
||||||
|
>
|
||||||
>(
|
>(
|
||||||
selector: Selector,
|
selector: Selector,
|
||||||
pageFunction: Func | string,
|
pageFunction: Func | string,
|
||||||
|
|
@ -548,9 +554,10 @@ export class Frame {
|
||||||
async $$eval<
|
async $$eval<
|
||||||
Selector extends string,
|
Selector extends string,
|
||||||
Params extends unknown[],
|
Params extends unknown[],
|
||||||
Func extends EvaluateFunc<
|
Func extends EvaluateFuncWith<
|
||||||
[Array<NodeFor<Selector>>, ...Params]
|
Array<NodeFor<Selector>>,
|
||||||
> = EvaluateFunc<[Array<NodeFor<Selector>>, ...Params]>
|
Params
|
||||||
|
> = EvaluateFuncWith<Array<NodeFor<Selector>>, Params>
|
||||||
>(
|
>(
|
||||||
selector: Selector,
|
selector: Selector,
|
||||||
pageFunction: Func | string,
|
pageFunction: Func | string,
|
||||||
|
|
@ -612,10 +619,9 @@ export class Frame {
|
||||||
selector: Selector,
|
selector: Selector,
|
||||||
options: WaitForSelectorOptions = {}
|
options: WaitForSelectorOptions = {}
|
||||||
): Promise<ElementHandle<NodeFor<Selector>> | null> {
|
): Promise<ElementHandle<NodeFor<Selector>> | null> {
|
||||||
const {updatedSelector, queryHandler} =
|
const {updatedSelector, QueryHandler} =
|
||||||
getQueryHandlerAndSelector(selector);
|
getQueryHandlerAndSelector(selector);
|
||||||
assert(queryHandler.waitFor, 'Query handler does not support waiting');
|
return (await QueryHandler.waitFor(
|
||||||
return (await queryHandler.waitFor(
|
|
||||||
this,
|
this,
|
||||||
updatedSelector,
|
updatedSelector,
|
||||||
options
|
options
|
||||||
|
|
@ -839,7 +845,9 @@ export class Frame {
|
||||||
await promise;
|
await promise;
|
||||||
return script;
|
return script;
|
||||||
},
|
},
|
||||||
await this.worlds[PUPPETEER_WORLD].puppeteerUtil,
|
LazyArg.create(context => {
|
||||||
|
return context.puppeteerUtil;
|
||||||
|
}),
|
||||||
{...options, type, content}
|
{...options, type, content}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
@ -923,7 +931,9 @@ export class Frame {
|
||||||
await promise;
|
await promise;
|
||||||
return element;
|
return element;
|
||||||
},
|
},
|
||||||
await this.worlds[PUPPETEER_WORLD].puppeteerUtil,
|
LazyArg.create(context => {
|
||||||
|
return context.puppeteerUtil;
|
||||||
|
}),
|
||||||
options
|
options
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Protocol} from 'devtools-protocol';
|
import {Protocol} from 'devtools-protocol';
|
||||||
|
|
||||||
|
import {Page} from '../api/Page.js';
|
||||||
import {assert} from '../util/assert.js';
|
import {assert} from '../util/assert.js';
|
||||||
import {isErrorLike} from '../util/ErrorLike.js';
|
import {isErrorLike} from '../util/ErrorLike.js';
|
||||||
|
|
||||||
import {CDPSession, isTargetClosedError} from './Connection.js';
|
import {CDPSession, isTargetClosedError} from './Connection.js';
|
||||||
import {EventEmitter} from './EventEmitter.js';
|
import {EventEmitter} from './EventEmitter.js';
|
||||||
import {EVALUATION_SCRIPT_URL, ExecutionContext} from './ExecutionContext.js';
|
import {EVALUATION_SCRIPT_URL, ExecutionContext} from './ExecutionContext.js';
|
||||||
|
|
@ -25,7 +28,6 @@ import {FrameTree} from './FrameTree.js';
|
||||||
import {IsolatedWorld} from './IsolatedWorld.js';
|
import {IsolatedWorld} from './IsolatedWorld.js';
|
||||||
import {MAIN_WORLD, PUPPETEER_WORLD} from './IsolatedWorlds.js';
|
import {MAIN_WORLD, PUPPETEER_WORLD} from './IsolatedWorlds.js';
|
||||||
import {NetworkManager} from './NetworkManager.js';
|
import {NetworkManager} from './NetworkManager.js';
|
||||||
import {Page} from '../api/Page.js';
|
|
||||||
import {Target} from './Target.js';
|
import {Target} from './Target.js';
|
||||||
import {TimeoutSettings} from './TimeoutSettings.js';
|
import {TimeoutSettings} from './TimeoutSettings.js';
|
||||||
import {debugError} from './util.js';
|
import {debugError} from './util.js';
|
||||||
|
|
@ -174,12 +176,18 @@ export class FrameManager extends EventEmitter {
|
||||||
contextId: number,
|
contextId: number,
|
||||||
session: CDPSession = this.#client
|
session: CDPSession = this.#client
|
||||||
): ExecutionContext {
|
): ExecutionContext {
|
||||||
const key = `${session.id()}:${contextId}`;
|
const context = this.getExecutionContextById(contextId, session);
|
||||||
const context = this.#contextIdToContext.get(key);
|
|
||||||
assert(context, 'INTERNAL ERROR: missing context with id = ' + contextId);
|
assert(context, 'INTERNAL ERROR: missing context with id = ' + contextId);
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getExecutionContextById(
|
||||||
|
contextId: number,
|
||||||
|
session: CDPSession = this.#client
|
||||||
|
): ExecutionContext | undefined {
|
||||||
|
return this.#contextIdToContext.get(`${session.id()}:${contextId}`);
|
||||||
|
}
|
||||||
|
|
||||||
page(): Page {
|
page(): Page {
|
||||||
return this.#page;
|
return this.#page;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import {
|
||||||
createDeferredPromise,
|
createDeferredPromise,
|
||||||
DeferredPromise,
|
DeferredPromise,
|
||||||
} from '../util/DeferredPromise.js';
|
} from '../util/DeferredPromise.js';
|
||||||
|
|
||||||
import type {Frame} from './Frame.js';
|
import type {Frame} from './Frame.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2023 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {ARIAQueryHandler} from './AriaQueryHandler.js';
|
||||||
|
import {customQueryHandlers} from './CustomQueryHandler.js';
|
||||||
|
import {PierceQueryHandler} from './PierceQueryHandler.js';
|
||||||
|
import {PQueryHandler} from './PQueryHandler.js';
|
||||||
|
import type {QueryHandler} from './QueryHandler.js';
|
||||||
|
import {TextQueryHandler} from './TextQueryHandler.js';
|
||||||
|
import {XPathQueryHandler} from './XPathQueryHandler.js';
|
||||||
|
|
||||||
|
export const BUILTIN_QUERY_HANDLERS = Object.freeze({
|
||||||
|
aria: ARIAQueryHandler,
|
||||||
|
pierce: PierceQueryHandler,
|
||||||
|
xpath: XPathQueryHandler,
|
||||||
|
text: TextQueryHandler,
|
||||||
|
});
|
||||||
|
|
||||||
|
const QUERY_SEPARATORS = ['=', '/'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export function getQueryHandlerByName(
|
||||||
|
name: string
|
||||||
|
): typeof QueryHandler | undefined {
|
||||||
|
if (name in BUILTIN_QUERY_HANDLERS) {
|
||||||
|
return BUILTIN_QUERY_HANDLERS[name as 'aria'];
|
||||||
|
}
|
||||||
|
return customQueryHandlers.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export function getQueryHandlerAndSelector(selector: string): {
|
||||||
|
updatedSelector: string;
|
||||||
|
QueryHandler: typeof QueryHandler;
|
||||||
|
} {
|
||||||
|
for (const handlerMap of [
|
||||||
|
customQueryHandlers.names().map(name => {
|
||||||
|
return [name, customQueryHandlers.get(name)!] as const;
|
||||||
|
}),
|
||||||
|
Object.entries(BUILTIN_QUERY_HANDLERS),
|
||||||
|
]) {
|
||||||
|
for (const [name, QueryHandler] of handlerMap) {
|
||||||
|
for (const separator of QUERY_SEPARATORS) {
|
||||||
|
const prefix = `${name}${separator}`;
|
||||||
|
if (selector.startsWith(prefix)) {
|
||||||
|
selector = selector.slice(prefix.length);
|
||||||
|
return {updatedSelector: selector, QueryHandler};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {updatedSelector: selector, QueryHandler: PQueryHandler};
|
||||||
|
}
|
||||||
|
|
@ -15,12 +15,14 @@
|
||||||
*/
|
*/
|
||||||
import {Protocol} from 'devtools-protocol';
|
import {Protocol} from 'devtools-protocol';
|
||||||
import {ProtocolMapping} from 'devtools-protocol/types/protocol-mapping.js';
|
import {ProtocolMapping} from 'devtools-protocol/types/protocol-mapping.js';
|
||||||
|
|
||||||
import {assert} from '../util/assert.js';
|
import {assert} from '../util/assert.js';
|
||||||
|
|
||||||
import {ProtocolError} from './Errors.js';
|
import {ProtocolError} from './Errors.js';
|
||||||
import {EventEmitter} from './EventEmitter.js';
|
import {EventEmitter} from './EventEmitter.js';
|
||||||
import {Frame} from './Frame.js';
|
import {Frame} from './Frame.js';
|
||||||
import {debugError, isString} from './util.js';
|
|
||||||
import {HTTPResponse} from './HTTPResponse.js';
|
import {HTTPResponse} from './HTTPResponse.js';
|
||||||
|
import {debugError, isString} from './util.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
|
|
|
||||||
|
|
@ -13,14 +13,14 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
import {Protocol} from 'devtools-protocol';
|
||||||
import {ProtocolMapping} from 'devtools-protocol/types/protocol-mapping.js';
|
import {ProtocolMapping} from 'devtools-protocol/types/protocol-mapping.js';
|
||||||
|
|
||||||
|
import {ProtocolError} from './Errors.js';
|
||||||
import {EventEmitter} from './EventEmitter.js';
|
import {EventEmitter} from './EventEmitter.js';
|
||||||
import {Frame} from './Frame.js';
|
import {Frame} from './Frame.js';
|
||||||
import {HTTPRequest} from './HTTPRequest.js';
|
import {HTTPRequest} from './HTTPRequest.js';
|
||||||
import {SecurityDetails} from './SecurityDetails.js';
|
import {SecurityDetails} from './SecurityDetails.js';
|
||||||
import {Protocol} from 'devtools-protocol';
|
|
||||||
import {ProtocolError} from './Errors.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2023 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {JSHandle} from '../api/JSHandle.js';
|
||||||
|
|
||||||
|
import {AwaitableIterable, HandleFor} from './types.js';
|
||||||
|
|
||||||
|
const DEFAULT_BATCH_SIZE = 20;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This will transpose an iterator JSHandle into a fast, Puppeteer-side iterator
|
||||||
|
* of JSHandles.
|
||||||
|
*
|
||||||
|
* @param size - The number of elements to transpose. This should be something
|
||||||
|
* reasonable.
|
||||||
|
*/
|
||||||
|
async function* fastTransposeIteratorHandle<T>(
|
||||||
|
iterator: JSHandle<AwaitableIterator<T>>,
|
||||||
|
size = DEFAULT_BATCH_SIZE
|
||||||
|
) {
|
||||||
|
const array = await iterator.evaluateHandle(async (iterator, size) => {
|
||||||
|
const results = [];
|
||||||
|
while (results.length < size) {
|
||||||
|
const result = await iterator.next();
|
||||||
|
if (result.done) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
results.push(result.value);
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}, size);
|
||||||
|
const properties = (await array.getProperties()) as Map<string, HandleFor<T>>;
|
||||||
|
await array.dispose();
|
||||||
|
yield* properties.values();
|
||||||
|
return properties.size === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This will transpose an iterator JSHandle in batches based on the default size
|
||||||
|
* of {@link fastTransposeIteratorHandle}.
|
||||||
|
*/
|
||||||
|
|
||||||
|
async function* transposeIteratorHandle<T>(
|
||||||
|
iterator: JSHandle<AwaitableIterator<T>>
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
while (!(yield* fastTransposeIteratorHandle(iterator))) {}
|
||||||
|
} finally {
|
||||||
|
await iterator.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type AwaitableIterator<T> = Iterator<T> | AsyncIterator<T>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export async function* transposeIterableHandle<T>(
|
||||||
|
handle: JSHandle<AwaitableIterable<T>>
|
||||||
|
): AsyncIterableIterator<HandleFor<T>> {
|
||||||
|
yield* transposeIteratorHandle(
|
||||||
|
await handle.evaluateHandle(iterable => {
|
||||||
|
return (async function* () {
|
||||||
|
yield* iterable;
|
||||||
|
})();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -14,11 +14,13 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {Protocol} from 'devtools-protocol';
|
||||||
|
|
||||||
|
import {Point} from '../api/ElementHandle.js';
|
||||||
import {assert} from '../util/assert.js';
|
import {assert} from '../util/assert.js';
|
||||||
|
|
||||||
import {CDPSession} from './Connection.js';
|
import {CDPSession} from './Connection.js';
|
||||||
import {_keyDefinitions, KeyDefinition, KeyInput} from './USKeyboardLayout.js';
|
import {_keyDefinitions, KeyDefinition, KeyInput} from './USKeyboardLayout.js';
|
||||||
import {Protocol} from 'devtools-protocol';
|
|
||||||
import {Point} from './JSHandle.js';
|
|
||||||
|
|
||||||
type KeyDescription = Required<
|
type KeyDescription = Required<
|
||||||
Pick<KeyDefinition, 'keyCode' | 'key' | 'text' | 'code' | 'location'>
|
Pick<KeyDefinition, 'keyCode' | 'key' | 'text' | 'code' | 'location'>
|
||||||
|
|
@ -670,12 +672,40 @@ export class Touchscreen {
|
||||||
* @param y - Vertical position of the tap.
|
* @param y - Vertical position of the tap.
|
||||||
*/
|
*/
|
||||||
async tap(x: number, y: number): Promise<void> {
|
async tap(x: number, y: number): Promise<void> {
|
||||||
|
await this.touchStart(x, y);
|
||||||
|
await this.touchEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatches a `touchstart` event.
|
||||||
|
* @param x - Horizontal position of the tap.
|
||||||
|
* @param y - Vertical position of the tap.
|
||||||
|
*/
|
||||||
|
async touchStart(x: number, y: number): Promise<void> {
|
||||||
const touchPoints = [{x: Math.round(x), y: Math.round(y)}];
|
const touchPoints = [{x: Math.round(x), y: Math.round(y)}];
|
||||||
await this.#client.send('Input.dispatchTouchEvent', {
|
await this.#client.send('Input.dispatchTouchEvent', {
|
||||||
type: 'touchStart',
|
type: 'touchStart',
|
||||||
touchPoints,
|
touchPoints,
|
||||||
modifiers: this.#keyboard._modifiers,
|
modifiers: this.#keyboard._modifiers,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Dispatches a `touchMove` event.
|
||||||
|
* @param x - Horizontal position of the move.
|
||||||
|
* @param y - Vertical position of the move.
|
||||||
|
*/
|
||||||
|
async touchMove(x: number, y: number): Promise<void> {
|
||||||
|
const movePoints = [{x: Math.round(x), y: Math.round(y)}];
|
||||||
|
await this.#client.send('Input.dispatchTouchEvent', {
|
||||||
|
type: 'touchMove',
|
||||||
|
touchPoints: movePoints,
|
||||||
|
modifiers: this.#keyboard._modifiers,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Dispatches a `touchend` event.
|
||||||
|
*/
|
||||||
|
async touchEnd(): Promise<void> {
|
||||||
await this.#client.send('Input.dispatchTouchEvent', {
|
await this.#client.send('Input.dispatchTouchEvent', {
|
||||||
type: 'touchEnd',
|
type: 'touchEnd',
|
||||||
touchPoints: [],
|
touchPoints: [],
|
||||||
|
|
|
||||||
|
|
@ -15,26 +15,31 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Protocol} from 'devtools-protocol';
|
import {Protocol} from 'devtools-protocol';
|
||||||
import {source as injectedSource} from '../generated/injected.js';
|
|
||||||
|
import type {ElementHandle} from '../api/ElementHandle.js';
|
||||||
|
import {JSHandle} from '../api/JSHandle.js';
|
||||||
import {assert} from '../util/assert.js';
|
import {assert} from '../util/assert.js';
|
||||||
import {createDeferredPromise} from '../util/DeferredPromise.js';
|
import {createDeferredPromise} from '../util/DeferredPromise.js';
|
||||||
import {isErrorLike} from '../util/ErrorLike.js';
|
|
||||||
|
import {Binding} from './Binding.js';
|
||||||
import {CDPSession} from './Connection.js';
|
import {CDPSession} from './Connection.js';
|
||||||
import {ExecutionContext} from './ExecutionContext.js';
|
import {ExecutionContext} from './ExecutionContext.js';
|
||||||
import {Frame} from './Frame.js';
|
import {Frame} from './Frame.js';
|
||||||
import {FrameManager} from './FrameManager.js';
|
import {FrameManager} from './FrameManager.js';
|
||||||
import {MouseButton} from './Input.js';
|
import {MouseButton} from './Input.js';
|
||||||
import {JSHandle} from './JSHandle.js';
|
import {MAIN_WORLD, PUPPETEER_WORLD} from './IsolatedWorlds.js';
|
||||||
import {LazyArg} from './LazyArg.js';
|
|
||||||
import {LifecycleWatcher, PuppeteerLifeCycleEvent} from './LifecycleWatcher.js';
|
import {LifecycleWatcher, PuppeteerLifeCycleEvent} from './LifecycleWatcher.js';
|
||||||
import {TimeoutSettings} from './TimeoutSettings.js';
|
import {TimeoutSettings} from './TimeoutSettings.js';
|
||||||
import {EvaluateFunc, HandleFor, InnerLazyParams, NodeFor} from './types.js';
|
import {
|
||||||
import {createJSHandle, debugError, pageBindingInitString} from './util.js';
|
BindingPayload,
|
||||||
|
EvaluateFunc,
|
||||||
|
EvaluateFuncWith,
|
||||||
|
HandleFor,
|
||||||
|
InnerLazyParams,
|
||||||
|
NodeFor,
|
||||||
|
} from './types.js';
|
||||||
|
import {addPageBinding, createJSHandle, debugError} from './util.js';
|
||||||
import {TaskManager, WaitTask} from './WaitTask.js';
|
import {TaskManager, WaitTask} from './WaitTask.js';
|
||||||
import {MAIN_WORLD, PUPPETEER_WORLD} from './IsolatedWorlds.js';
|
|
||||||
|
|
||||||
import type PuppeteerUtil from '../injected/injected.js';
|
|
||||||
import type {ElementHandle} from './ElementHandle.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
|
|
@ -91,29 +96,20 @@ export class IsolatedWorld {
|
||||||
#detached = false;
|
#detached = false;
|
||||||
|
|
||||||
// Set of bindings that have been registered in the current context.
|
// Set of bindings that have been registered in the current context.
|
||||||
#ctxBindings = new Set<string>();
|
#contextBindings = new Set<string>();
|
||||||
|
|
||||||
// Contains mapping from functions that should be bound to Puppeteer functions.
|
// Contains mapping from functions that should be bound to Puppeteer functions.
|
||||||
#boundFunctions = new Map<string, Function>();
|
#bindings = new Map<string, Binding>();
|
||||||
#taskManager = new TaskManager();
|
#taskManager = new TaskManager();
|
||||||
#puppeteerUtil = createDeferredPromise<JSHandle<PuppeteerUtil>>();
|
|
||||||
|
|
||||||
get puppeteerUtil(): Promise<JSHandle<PuppeteerUtil>> {
|
|
||||||
return this.#puppeteerUtil;
|
|
||||||
}
|
|
||||||
|
|
||||||
get taskManager(): TaskManager {
|
get taskManager(): TaskManager {
|
||||||
return this.#taskManager;
|
return this.#taskManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
get _boundFunctions(): Map<string, Function> {
|
get _bindings(): Map<string, Binding> {
|
||||||
return this.#boundFunctions;
|
return this.#bindings;
|
||||||
}
|
}
|
||||||
|
|
||||||
static #bindingIdentifier = (name: string, contextId: number) => {
|
|
||||||
return `${name}_${contextId}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(frame: Frame) {
|
constructor(frame: Frame) {
|
||||||
// Keep own reference to client because it might differ from the FrameManager's
|
// Keep own reference to client because it might differ from the FrameManager's
|
||||||
// client for OOP iframes.
|
// client for OOP iframes.
|
||||||
|
|
@ -139,31 +135,13 @@ export class IsolatedWorld {
|
||||||
|
|
||||||
clearContext(): void {
|
clearContext(): void {
|
||||||
this.#document = undefined;
|
this.#document = undefined;
|
||||||
this.#puppeteerUtil = createDeferredPromise();
|
|
||||||
this.#context = createDeferredPromise();
|
this.#context = createDeferredPromise();
|
||||||
}
|
}
|
||||||
|
|
||||||
setContext(context: ExecutionContext): void {
|
setContext(context: ExecutionContext): void {
|
||||||
this.#injectPuppeteerUtil(context);
|
this.#contextBindings.clear();
|
||||||
this.#ctxBindings.clear();
|
|
||||||
this.#context.resolve(context);
|
this.#context.resolve(context);
|
||||||
}
|
|
||||||
|
|
||||||
async #injectPuppeteerUtil(context: ExecutionContext): Promise<void> {
|
|
||||||
try {
|
|
||||||
this.#puppeteerUtil.resolve(
|
|
||||||
(await context.evaluateHandle(
|
|
||||||
`(() => {
|
|
||||||
const module = {};
|
|
||||||
${injectedSource}
|
|
||||||
return module.exports.default;
|
|
||||||
})()`
|
|
||||||
)) as JSHandle<PuppeteerUtil>
|
|
||||||
);
|
|
||||||
this.#taskManager.rerunAll();
|
this.#taskManager.rerunAll();
|
||||||
} catch (error: unknown) {
|
|
||||||
debugError(error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hasContext(): boolean {
|
hasContext(): boolean {
|
||||||
|
|
@ -245,9 +223,10 @@ export class IsolatedWorld {
|
||||||
async $eval<
|
async $eval<
|
||||||
Selector extends string,
|
Selector extends string,
|
||||||
Params extends unknown[],
|
Params extends unknown[],
|
||||||
Func extends EvaluateFunc<
|
Func extends EvaluateFuncWith<NodeFor<Selector>, Params> = EvaluateFuncWith<
|
||||||
[ElementHandle<NodeFor<Selector>>, ...Params]
|
NodeFor<Selector>,
|
||||||
> = EvaluateFunc<[ElementHandle<NodeFor<Selector>>, ...Params]>
|
Params
|
||||||
|
>
|
||||||
>(
|
>(
|
||||||
selector: Selector,
|
selector: Selector,
|
||||||
pageFunction: Func | string,
|
pageFunction: Func | string,
|
||||||
|
|
@ -260,9 +239,10 @@ export class IsolatedWorld {
|
||||||
async $$eval<
|
async $$eval<
|
||||||
Selector extends string,
|
Selector extends string,
|
||||||
Params extends unknown[],
|
Params extends unknown[],
|
||||||
Func extends EvaluateFunc<
|
Func extends EvaluateFuncWith<
|
||||||
[Array<NodeFor<Selector>>, ...Params]
|
Array<NodeFor<Selector>>,
|
||||||
> = EvaluateFunc<[Array<NodeFor<Selector>>, ...Params]>
|
Params
|
||||||
|
> = EvaluateFuncWith<Array<NodeFor<Selector>>, Params>
|
||||||
>(
|
>(
|
||||||
selector: Selector,
|
selector: Selector,
|
||||||
pageFunction: Func | string,
|
pageFunction: Func | string,
|
||||||
|
|
@ -371,35 +351,25 @@ export class IsolatedWorld {
|
||||||
|
|
||||||
// If multiple waitFor are set up asynchronously, we need to wait for the
|
// If multiple waitFor are set up asynchronously, we need to wait for the
|
||||||
// first one to set up the binding in the page before running the others.
|
// first one to set up the binding in the page before running the others.
|
||||||
#settingUpBinding: Promise<void> | null = null;
|
#mutex = new Mutex();
|
||||||
|
|
||||||
async _addBindingToContext(
|
async _addBindingToContext(
|
||||||
context: ExecutionContext,
|
context: ExecutionContext,
|
||||||
name: string
|
name: string
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// Previous operation added the binding so we are done.
|
if (this.#contextBindings.has(name)) {
|
||||||
if (
|
|
||||||
this.#ctxBindings.has(
|
|
||||||
IsolatedWorld.#bindingIdentifier(name, context._contextId)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Wait for other operation to finish
|
|
||||||
if (this.#settingUpBinding) {
|
|
||||||
await this.#settingUpBinding;
|
|
||||||
return this._addBindingToContext(context, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
const bind = async (name: string) => {
|
await this.#mutex.acquire();
|
||||||
const expression = pageBindingInitString('internal', name);
|
|
||||||
try {
|
try {
|
||||||
// TODO: In theory, it would be enough to call this just once
|
|
||||||
await context._client.send('Runtime.addBinding', {
|
await context._client.send('Runtime.addBinding', {
|
||||||
name,
|
name,
|
||||||
executionContextName: context._contextName,
|
executionContextName: context._contextName,
|
||||||
});
|
});
|
||||||
await context.evaluate(expression);
|
|
||||||
|
await context.evaluate(addPageBinding, 'internal', name);
|
||||||
|
|
||||||
|
this.#contextBindings.add(name);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// We could have tried to evaluate in a context which was already
|
// We could have tried to evaluate in a context which was already
|
||||||
// destroyed. This happens, for example, if the page is navigated while
|
// destroyed. This happens, for example, if the page is navigated while
|
||||||
|
|
@ -416,26 +386,15 @@ export class IsolatedWorld {
|
||||||
}
|
}
|
||||||
|
|
||||||
debugError(error);
|
debugError(error);
|
||||||
return;
|
} finally {
|
||||||
|
this.#mutex.release();
|
||||||
}
|
}
|
||||||
this.#ctxBindings.add(
|
|
||||||
IsolatedWorld.#bindingIdentifier(name, context._contextId)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.#settingUpBinding = bind(name);
|
|
||||||
await this.#settingUpBinding;
|
|
||||||
this.#settingUpBinding = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#onBindingCalled = async (
|
#onBindingCalled = async (
|
||||||
event: Protocol.Runtime.BindingCalledEvent
|
event: Protocol.Runtime.BindingCalledEvent
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
let payload: {type: string; name: string; seq: number; args: unknown[]};
|
let payload: BindingPayload;
|
||||||
if (!this.hasContext()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const context = await this.executionContext();
|
|
||||||
try {
|
try {
|
||||||
payload = JSON.parse(event.payload);
|
payload = JSON.parse(event.payload);
|
||||||
} catch {
|
} catch {
|
||||||
|
|
@ -443,108 +402,23 @@ export class IsolatedWorld {
|
||||||
// called before our wrapper was initialized.
|
// called before our wrapper was initialized.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const {type, name, seq, args} = payload;
|
const {type, name, seq, args, isTrivial} = payload;
|
||||||
if (
|
if (type !== 'internal') {
|
||||||
type !== 'internal' ||
|
|
||||||
!this.#ctxBindings.has(
|
|
||||||
IsolatedWorld.#bindingIdentifier(name, context._contextId)
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (context._contextId !== event.executionContextId) {
|
if (!this.#contextBindings.has(name)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
const fn = this._boundFunctions.get(name);
|
const context = await this.#context;
|
||||||
if (!fn) {
|
if (event.executionContextId !== context._contextId) {
|
||||||
throw new Error(`Bound function $name is not found`);
|
|
||||||
}
|
|
||||||
const result = await fn(...args);
|
|
||||||
await context.evaluate(
|
|
||||||
(name: string, seq: number, result: unknown) => {
|
|
||||||
// @ts-expect-error Code is evaluated in a different context.
|
|
||||||
const callbacks = self[name].callbacks;
|
|
||||||
callbacks.get(seq).resolve(result);
|
|
||||||
callbacks.delete(seq);
|
|
||||||
},
|
|
||||||
name,
|
|
||||||
seq,
|
|
||||||
result
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
// The WaitTask may already have been resolved by timing out, or the
|
|
||||||
// execution context may have been destroyed.
|
|
||||||
// In both caes, the promises above are rejected with a protocol error.
|
|
||||||
// We can safely ignores these, as the WaitTask is re-installed in
|
|
||||||
// the next execution context if needed.
|
|
||||||
if ((error as Error).message.includes('Protocol error')) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
debugError(error);
|
|
||||||
}
|
const binding = this._bindings.get(name);
|
||||||
|
await binding?.run(context, seq, args, isTrivial);
|
||||||
};
|
};
|
||||||
|
|
||||||
async _waitForSelectorInPage(
|
|
||||||
queryOne: Function,
|
|
||||||
root: ElementHandle<Node> | undefined,
|
|
||||||
selector: string,
|
|
||||||
options: WaitForSelectorOptions,
|
|
||||||
bindings = new Map<string, (...args: never[]) => unknown>()
|
|
||||||
): Promise<JSHandle<unknown> | null> {
|
|
||||||
const {
|
|
||||||
visible: waitForVisible = false,
|
|
||||||
hidden: waitForHidden = false,
|
|
||||||
timeout = this.#timeoutSettings.timeout(),
|
|
||||||
} = options;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const handle = await this.waitForFunction(
|
|
||||||
async (PuppeteerUtil, query, selector, root, visible) => {
|
|
||||||
if (!PuppeteerUtil) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const node = (await PuppeteerUtil.createFunction(query)(
|
|
||||||
root || document,
|
|
||||||
selector,
|
|
||||||
PuppeteerUtil
|
|
||||||
)) as Node | null;
|
|
||||||
return PuppeteerUtil.checkVisibility(node, visible);
|
|
||||||
},
|
|
||||||
{
|
|
||||||
bindings,
|
|
||||||
polling: waitForVisible || waitForHidden ? 'raf' : 'mutation',
|
|
||||||
root,
|
|
||||||
timeout,
|
|
||||||
},
|
|
||||||
new LazyArg(async () => {
|
|
||||||
try {
|
|
||||||
// In case CDP fails.
|
|
||||||
return await this.puppeteerUtil;
|
|
||||||
} catch {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
queryOne.toString(),
|
|
||||||
selector,
|
|
||||||
root,
|
|
||||||
waitForVisible ? true : waitForHidden ? false : undefined
|
|
||||||
);
|
|
||||||
const elementHandle = handle.asElement();
|
|
||||||
if (!elementHandle) {
|
|
||||||
await handle.dispose();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return elementHandle;
|
|
||||||
} catch (error) {
|
|
||||||
if (!isErrorLike(error)) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
error.message = `Waiting for selector \`${selector}\` failed: ${error.message}`;
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
waitForFunction<
|
waitForFunction<
|
||||||
Params extends unknown[],
|
Params extends unknown[],
|
||||||
Func extends EvaluateFunc<InnerLazyParams<Params>> = EvaluateFunc<
|
Func extends EvaluateFunc<InnerLazyParams<Params>> = EvaluateFunc<
|
||||||
|
|
@ -556,14 +430,12 @@ export class IsolatedWorld {
|
||||||
polling?: 'raf' | 'mutation' | number;
|
polling?: 'raf' | 'mutation' | number;
|
||||||
timeout?: number;
|
timeout?: number;
|
||||||
root?: ElementHandle<Node>;
|
root?: ElementHandle<Node>;
|
||||||
bindings?: Map<string, (...args: never[]) => unknown>;
|
|
||||||
} = {},
|
} = {},
|
||||||
...args: Params
|
...args: Params
|
||||||
): Promise<HandleFor<Awaited<ReturnType<Func>>>> {
|
): Promise<HandleFor<Awaited<ReturnType<Func>>>> {
|
||||||
const {
|
const {
|
||||||
polling = 'raf',
|
polling = 'raf',
|
||||||
timeout = this.#timeoutSettings.timeout(),
|
timeout = this.#timeoutSettings.timeout(),
|
||||||
bindings,
|
|
||||||
root,
|
root,
|
||||||
} = options;
|
} = options;
|
||||||
if (typeof polling === 'number' && polling < 0) {
|
if (typeof polling === 'number' && polling < 0) {
|
||||||
|
|
@ -572,7 +444,6 @@ export class IsolatedWorld {
|
||||||
const waitTask = new WaitTask(
|
const waitTask = new WaitTask(
|
||||||
this,
|
this,
|
||||||
{
|
{
|
||||||
bindings,
|
|
||||||
polling,
|
polling,
|
||||||
root,
|
root,
|
||||||
timeout,
|
timeout,
|
||||||
|
|
@ -603,20 +474,57 @@ export class IsolatedWorld {
|
||||||
}
|
}
|
||||||
|
|
||||||
async adoptHandle<T extends JSHandle<Node>>(handle: T): Promise<T> {
|
async adoptHandle<T extends JSHandle<Node>>(handle: T): Promise<T> {
|
||||||
const executionContext = await this.executionContext();
|
const context = await this.executionContext();
|
||||||
assert(
|
assert(
|
||||||
handle.executionContext() !== executionContext,
|
handle.executionContext() !== context,
|
||||||
'Cannot adopt handle that already belongs to this execution context'
|
'Cannot adopt handle that already belongs to this execution context'
|
||||||
);
|
);
|
||||||
const nodeInfo = await this.#client.send('DOM.describeNode', {
|
const nodeInfo = await this.#client.send('DOM.describeNode', {
|
||||||
objectId: handle.remoteObject().objectId,
|
objectId: handle.id,
|
||||||
});
|
});
|
||||||
return (await this.adoptBackendNode(nodeInfo.node.backendNodeId)) as T;
|
return (await this.adoptBackendNode(nodeInfo.node.backendNodeId)) as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
async transferHandle<T extends JSHandle<Node>>(handle: T): Promise<T> {
|
async transferHandle<T extends JSHandle<Node>>(handle: T): Promise<T> {
|
||||||
const result = await this.adoptHandle(handle);
|
const context = await this.executionContext();
|
||||||
|
if (handle.executionContext() === context) {
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
const info = await this.#client.send('DOM.describeNode', {
|
||||||
|
objectId: handle.remoteObject().objectId,
|
||||||
|
});
|
||||||
|
const newHandle = (await this.adoptBackendNode(
|
||||||
|
info.node.backendNodeId
|
||||||
|
)) as T;
|
||||||
await handle.dispose();
|
await handle.dispose();
|
||||||
return result;
|
return newHandle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Mutex {
|
||||||
|
#locked = false;
|
||||||
|
#acquirers: Array<() => void> = [];
|
||||||
|
|
||||||
|
// This is FIFO.
|
||||||
|
acquire(): Promise<void> {
|
||||||
|
if (!this.#locked) {
|
||||||
|
this.#locked = true;
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
let resolve!: () => void;
|
||||||
|
const promise = new Promise<void>(res => {
|
||||||
|
resolve = res;
|
||||||
|
});
|
||||||
|
this.#acquirers.push(resolve);
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
release(): void {
|
||||||
|
const resolve = this.#acquirers.shift();
|
||||||
|
if (!resolve) {
|
||||||
|
this.#locked = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,64 +15,22 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Protocol} from 'devtools-protocol';
|
import {Protocol} from 'devtools-protocol';
|
||||||
|
|
||||||
|
import {JSHandle} from '../api/JSHandle.js';
|
||||||
import {assert} from '../util/assert.js';
|
import {assert} from '../util/assert.js';
|
||||||
|
|
||||||
import {CDPSession} from './Connection.js';
|
import {CDPSession} from './Connection.js';
|
||||||
import type {ElementHandle} from './ElementHandle.js';
|
import type {CDPElementHandle} from './ElementHandle.js';
|
||||||
import {ExecutionContext} from './ExecutionContext.js';
|
import {ExecutionContext} from './ExecutionContext.js';
|
||||||
import {MouseButton} from './Input.js';
|
import {EvaluateFuncWith, HandleFor, HandleOr} from './types.js';
|
||||||
import {EvaluateFunc, HandleFor, HandleOr} from './types.js';
|
|
||||||
import {createJSHandle, releaseObject, valueFromRemoteObject} from './util.js';
|
import {createJSHandle, releaseObject, valueFromRemoteObject} from './util.js';
|
||||||
|
|
||||||
declare const __JSHandleSymbol: unique symbol;
|
declare const __JSHandleSymbol: unique symbol;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @internal
|
||||||
*/
|
*/
|
||||||
export interface BoxModel {
|
export class CDPJSHandle<T = unknown> extends JSHandle<T> {
|
||||||
content: Point[];
|
|
||||||
padding: Point[];
|
|
||||||
border: Point[];
|
|
||||||
margin: Point[];
|
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export interface BoundingBox extends Point {
|
|
||||||
/**
|
|
||||||
* the width of the element in pixels.
|
|
||||||
*/
|
|
||||||
width: number;
|
|
||||||
/**
|
|
||||||
* the height of the element in pixels.
|
|
||||||
*/
|
|
||||||
height: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a reference to a JavaScript object. Instances can be created using
|
|
||||||
* {@link Page.evaluateHandle}.
|
|
||||||
*
|
|
||||||
* Handles prevent the referenced JavaScript object from being garbage-collected
|
|
||||||
* unless the handle is purposely {@link JSHandle.dispose | disposed}. JSHandles
|
|
||||||
* are auto-disposed when their associated frame is navigated away or the parent
|
|
||||||
* context gets destroyed.
|
|
||||||
*
|
|
||||||
* Handles can be used as arguments for any evaluation function such as
|
|
||||||
* {@link Page.$eval}, {@link Page.evaluate}, and {@link Page.evaluateHandle}.
|
|
||||||
* They are resolved to their referenced object.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
*
|
|
||||||
* ```ts
|
|
||||||
* const windowHandle = await page.evaluateHandle(() => window);
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export class JSHandle<T = unknown> {
|
|
||||||
/**
|
/**
|
||||||
* Used for nominally typing {@link JSHandle}.
|
* Used for nominally typing {@link JSHandle}.
|
||||||
*/
|
*/
|
||||||
|
|
@ -82,73 +40,50 @@ export class JSHandle<T = unknown> {
|
||||||
#context: ExecutionContext;
|
#context: ExecutionContext;
|
||||||
#remoteObject: Protocol.Runtime.RemoteObject;
|
#remoteObject: Protocol.Runtime.RemoteObject;
|
||||||
|
|
||||||
/**
|
override get disposed(): boolean {
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
get client(): CDPSession {
|
|
||||||
return this.#context._client;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
get disposed(): boolean {
|
|
||||||
return this.#disposed;
|
return this.#disposed;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
constructor(
|
constructor(
|
||||||
context: ExecutionContext,
|
context: ExecutionContext,
|
||||||
remoteObject: Protocol.Runtime.RemoteObject
|
remoteObject: Protocol.Runtime.RemoteObject
|
||||||
) {
|
) {
|
||||||
|
super();
|
||||||
this.#context = context;
|
this.#context = context;
|
||||||
this.#remoteObject = remoteObject;
|
this.#remoteObject = remoteObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override executionContext(): ExecutionContext {
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
executionContext(): ExecutionContext {
|
|
||||||
return this.#context;
|
return this.#context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override get client(): CDPSession {
|
||||||
|
return this.#context._client;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Evaluates the given function with the current handle as its first argument.
|
|
||||||
*
|
|
||||||
* @see {@link ExecutionContext.evaluate} for more details.
|
* @see {@link ExecutionContext.evaluate} for more details.
|
||||||
*/
|
*/
|
||||||
async evaluate<
|
override async evaluate<
|
||||||
Params extends unknown[],
|
Params extends unknown[],
|
||||||
Func extends EvaluateFunc<[this, ...Params]> = EvaluateFunc<
|
Func extends EvaluateFuncWith<T, Params> = EvaluateFuncWith<T, Params>
|
||||||
[this, ...Params]
|
|
||||||
>
|
|
||||||
>(
|
>(
|
||||||
pageFunction: Func | string,
|
pageFunction: Func | string,
|
||||||
...args: Params
|
...args: Params
|
||||||
): // @ts-expect-error Circularity here is okay because we only need the return
|
): Promise<Awaited<ReturnType<Func>>> {
|
||||||
// type which doesn't use `this`.
|
|
||||||
Promise<Awaited<ReturnType<Func>>> {
|
|
||||||
return await this.executionContext().evaluate(pageFunction, this, ...args);
|
return await this.executionContext().evaluate(pageFunction, this, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Evaluates the given function with the current handle as its first argument.
|
|
||||||
*
|
|
||||||
* @see {@link ExecutionContext.evaluateHandle} for more details.
|
* @see {@link ExecutionContext.evaluateHandle} for more details.
|
||||||
*/
|
*/
|
||||||
async evaluateHandle<
|
override async evaluateHandle<
|
||||||
Params extends unknown[],
|
Params extends unknown[],
|
||||||
Func extends EvaluateFunc<[this, ...Params]> = EvaluateFunc<
|
Func extends EvaluateFuncWith<T, Params> = EvaluateFuncWith<T, Params>
|
||||||
[this, ...Params]
|
|
||||||
>
|
|
||||||
>(
|
>(
|
||||||
pageFunction: Func | string,
|
pageFunction: Func | string,
|
||||||
...args: Params
|
...args: Params
|
||||||
): // @ts-expect-error Circularity here is okay because we only need the return
|
): Promise<HandleFor<Awaited<ReturnType<Func>>>> {
|
||||||
// type which doesn't use `this`.
|
|
||||||
Promise<HandleFor<Awaited<ReturnType<Func>>>> {
|
|
||||||
return await this.executionContext().evaluateHandle(
|
return await this.executionContext().evaluateHandle(
|
||||||
pageFunction,
|
pageFunction,
|
||||||
this,
|
this,
|
||||||
|
|
@ -156,14 +91,11 @@ export class JSHandle<T = unknown> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override async getProperty<K extends keyof T>(
|
||||||
* Fetches a single property from the referenced object.
|
|
||||||
*/
|
|
||||||
async getProperty<K extends keyof T>(
|
|
||||||
propertyName: HandleOr<K>
|
propertyName: HandleOr<K>
|
||||||
): Promise<HandleFor<T[K]>>;
|
): Promise<HandleFor<T[K]>>;
|
||||||
async getProperty(propertyName: string): Promise<JSHandle<unknown>>;
|
override async getProperty(propertyName: string): Promise<JSHandle<unknown>>;
|
||||||
async getProperty<K extends keyof T>(
|
override async getProperty<K extends keyof T>(
|
||||||
propertyName: HandleOr<K>
|
propertyName: HandleOr<K>
|
||||||
): Promise<HandleFor<T[K]>> {
|
): Promise<HandleFor<T[K]>> {
|
||||||
return this.evaluateHandle((object, propertyName) => {
|
return this.evaluateHandle((object, propertyName) => {
|
||||||
|
|
@ -171,25 +103,7 @@ export class JSHandle<T = unknown> {
|
||||||
}, propertyName);
|
}, propertyName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override async getProperties(): Promise<Map<string, JSHandle>> {
|
||||||
* Gets a map of handles representing the properties of the current handle.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
*
|
|
||||||
* ```ts
|
|
||||||
* const listHandle = await page.evaluateHandle(() => document.body.children);
|
|
||||||
* const properties = await listHandle.getProperties();
|
|
||||||
* const children = [];
|
|
||||||
* for (const property of properties.values()) {
|
|
||||||
* const element = property.asElement();
|
|
||||||
* if (element) {
|
|
||||||
* children.push(element);
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* children; // holds elementHandles to all children of document.body
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
async getProperties(): Promise<Map<string, JSHandle>> {
|
|
||||||
assert(this.#remoteObject.objectId);
|
assert(this.#remoteObject.objectId);
|
||||||
// We use Runtime.getProperties rather than iterative building because the
|
// We use Runtime.getProperties rather than iterative building because the
|
||||||
// iterative approach might create a distorted snapshot.
|
// iterative approach might create a distorted snapshot.
|
||||||
|
|
@ -207,15 +121,7 @@ export class JSHandle<T = unknown> {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override async jsonValue(): Promise<T> {
|
||||||
* @returns A vanilla object representing the serializable portions of the
|
|
||||||
* referenced object.
|
|
||||||
* @throws Throws if the object cannot be serialized due to circularity.
|
|
||||||
*
|
|
||||||
* @remarks
|
|
||||||
* If the object has a `toJSON` function, it **will not** be called.
|
|
||||||
*/
|
|
||||||
async jsonValue(): Promise<T> {
|
|
||||||
if (!this.#remoteObject.objectId) {
|
if (!this.#remoteObject.objectId) {
|
||||||
return valueFromRemoteObject(this.#remoteObject);
|
return valueFromRemoteObject(this.#remoteObject);
|
||||||
}
|
}
|
||||||
|
|
@ -232,14 +138,11 @@ export class JSHandle<T = unknown> {
|
||||||
* @returns Either `null` or the handle itself if the handle is an
|
* @returns Either `null` or the handle itself if the handle is an
|
||||||
* instance of {@link ElementHandle}.
|
* instance of {@link ElementHandle}.
|
||||||
*/
|
*/
|
||||||
asElement(): ElementHandle<Node> | null {
|
override asElement(): CDPElementHandle<Node> | null {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override async dispose(): Promise<void> {
|
||||||
* Releases the object referenced by the handle for garbage collection.
|
|
||||||
*/
|
|
||||||
async dispose(): Promise<void> {
|
|
||||||
if (this.#disposed) {
|
if (this.#disposed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -247,13 +150,7 @@ export class JSHandle<T = unknown> {
|
||||||
await releaseObject(this.client, this.#remoteObject);
|
await releaseObject(this.client, this.#remoteObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override toString(): string {
|
||||||
* Returns a string representation of the JSHandle.
|
|
||||||
*
|
|
||||||
* @remarks
|
|
||||||
* Useful during debugging.
|
|
||||||
*/
|
|
||||||
toString(): string {
|
|
||||||
if (!this.#remoteObject.objectId) {
|
if (!this.#remoteObject.objectId) {
|
||||||
return 'JSHandle:' + valueFromRemoteObject(this.#remoteObject);
|
return 'JSHandle:' + valueFromRemoteObject(this.#remoteObject);
|
||||||
}
|
}
|
||||||
|
|
@ -261,72 +158,11 @@ export class JSHandle<T = unknown> {
|
||||||
return 'JSHandle@' + type;
|
return 'JSHandle@' + type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
override get id(): string | undefined {
|
||||||
* Provides access to the
|
return this.#remoteObject.objectId;
|
||||||
* [Protocol.Runtime.RemoteObject](https://chromedevtools.github.io/devtools-protocol/tot/Runtime/#type-RemoteObject)
|
}
|
||||||
* backing this handle.
|
|
||||||
*/
|
override remoteObject(): Protocol.Runtime.RemoteObject {
|
||||||
remoteObject(): Protocol.Runtime.RemoteObject {
|
|
||||||
return this.#remoteObject;
|
return this.#remoteObject;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export interface Offset {
|
|
||||||
/**
|
|
||||||
* x-offset for the clickable point relative to the top-left corner of the border box.
|
|
||||||
*/
|
|
||||||
x: number;
|
|
||||||
/**
|
|
||||||
* y-offset for the clickable point relative to the top-left corner of the border box.
|
|
||||||
*/
|
|
||||||
y: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export interface ClickOptions {
|
|
||||||
/**
|
|
||||||
* Time to wait between `mousedown` and `mouseup` in milliseconds.
|
|
||||||
*
|
|
||||||
* @defaultValue 0
|
|
||||||
*/
|
|
||||||
delay?: number;
|
|
||||||
/**
|
|
||||||
* @defaultValue 'left'
|
|
||||||
*/
|
|
||||||
button?: MouseButton;
|
|
||||||
/**
|
|
||||||
* @defaultValue 1
|
|
||||||
*/
|
|
||||||
clickCount?: number;
|
|
||||||
/**
|
|
||||||
* Offset for the clickable point relative to the top-left corner of the border box.
|
|
||||||
*/
|
|
||||||
offset?: Offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export interface PressOptions {
|
|
||||||
/**
|
|
||||||
* Time to wait between `keydown` and `keyup` in milliseconds. Defaults to 0.
|
|
||||||
*/
|
|
||||||
delay?: number;
|
|
||||||
/**
|
|
||||||
* If specified, generates an input event with this text.
|
|
||||||
*/
|
|
||||||
text?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export interface Point {
|
|
||||||
x: number;
|
|
||||||
y: number;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -14,16 +14,26 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {ExecutionContext} from './ExecutionContext.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export class LazyArg<T> {
|
export class LazyArg<T> {
|
||||||
#get: () => Promise<T>;
|
static create = <T>(
|
||||||
constructor(get: () => Promise<T>) {
|
get: (context: ExecutionContext) => Promise<T> | T
|
||||||
|
): T => {
|
||||||
|
// We don't want to introduce LazyArg to the type system, otherwise we would
|
||||||
|
// have to make it public.
|
||||||
|
return new LazyArg(get) as unknown as T;
|
||||||
|
};
|
||||||
|
|
||||||
|
#get: (context: ExecutionContext) => Promise<T> | T;
|
||||||
|
private constructor(get: (context: ExecutionContext) => Promise<T> | T) {
|
||||||
this.#get = get;
|
this.#get = get;
|
||||||
}
|
}
|
||||||
|
|
||||||
get(): Promise<T> {
|
async get(context: ExecutionContext): Promise<T> {
|
||||||
return this.#get();
|
return this.#get(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,22 +15,23 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {assert} from '../util/assert.js';
|
import {assert} from '../util/assert.js';
|
||||||
|
import {
|
||||||
|
DeferredPromise,
|
||||||
|
createDeferredPromise,
|
||||||
|
} from '../util/DeferredPromise.js';
|
||||||
|
|
||||||
|
import {CDPSessionEmittedEvents} from './Connection.js';
|
||||||
|
import {TimeoutError} from './Errors.js';
|
||||||
|
import {Frame} from './Frame.js';
|
||||||
|
import {FrameManager, FrameManagerEmittedEvents} from './FrameManager.js';
|
||||||
|
import {HTTPRequest} from './HTTPRequest.js';
|
||||||
|
import {HTTPResponse} from './HTTPResponse.js';
|
||||||
|
import {NetworkManagerEmittedEvents} from './NetworkManager.js';
|
||||||
import {
|
import {
|
||||||
addEventListener,
|
addEventListener,
|
||||||
PuppeteerEventListener,
|
PuppeteerEventListener,
|
||||||
removeEventListeners,
|
removeEventListeners,
|
||||||
} from './util.js';
|
} from './util.js';
|
||||||
import {
|
|
||||||
DeferredPromise,
|
|
||||||
createDeferredPromise,
|
|
||||||
} from '../util/DeferredPromise.js';
|
|
||||||
import {TimeoutError} from './Errors.js';
|
|
||||||
import {FrameManager, FrameManagerEmittedEvents} from './FrameManager.js';
|
|
||||||
import {Frame} from './Frame.js';
|
|
||||||
import {HTTPRequest} from './HTTPRequest.js';
|
|
||||||
import {HTTPResponse} from './HTTPResponse.js';
|
|
||||||
import {NetworkManagerEmittedEvents} from './NetworkManager.js';
|
|
||||||
import {CDPSessionEmittedEvents} from './Connection.js';
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Protocol} from 'devtools-protocol';
|
import {Protocol} from 'devtools-protocol';
|
||||||
|
|
||||||
import {HTTPRequest} from './HTTPRequest.js';
|
import {HTTPRequest} from './HTTPRequest.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -15,16 +15,18 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Protocol} from 'devtools-protocol';
|
import {Protocol} from 'devtools-protocol';
|
||||||
|
|
||||||
import {assert} from '../util/assert.js';
|
import {assert} from '../util/assert.js';
|
||||||
|
import {createDebuggableDeferredPromise} from '../util/DebuggableDeferredPromise.js';
|
||||||
|
import {DeferredPromise} from '../util/DeferredPromise.js';
|
||||||
|
|
||||||
|
import {CDPSession} from './Connection.js';
|
||||||
import {EventEmitter} from './EventEmitter.js';
|
import {EventEmitter} from './EventEmitter.js';
|
||||||
import {Frame} from './Frame.js';
|
import {Frame} from './Frame.js';
|
||||||
import {HTTPRequest} from './HTTPRequest.js';
|
import {HTTPRequest} from './HTTPRequest.js';
|
||||||
import {HTTPResponse} from './HTTPResponse.js';
|
import {HTTPResponse} from './HTTPResponse.js';
|
||||||
import {FetchRequestId, NetworkEventManager} from './NetworkEventManager.js';
|
import {FetchRequestId, NetworkEventManager} from './NetworkEventManager.js';
|
||||||
import {debugError, isString} from './util.js';
|
import {debugError, isString} from './util.js';
|
||||||
import {DeferredPromise} from '../util/DeferredPromise.js';
|
|
||||||
import {createDebuggableDeferredPromise} from '../util/DebuggableDeferredPromise.js';
|
|
||||||
import {CDPSession} from './Connection.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import NodeWebSocket from 'ws';
|
import NodeWebSocket from 'ws';
|
||||||
|
|
||||||
import {ConnectionTransport} from '../common/ConnectionTransport.js';
|
import {ConnectionTransport} from '../common/ConnectionTransport.js';
|
||||||
import {packageVersion} from '../generated/version.js';
|
import {packageVersion} from '../generated/version.js';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2023 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {QueryHandler, QuerySelector, QuerySelectorAll} from './QueryHandler.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export class PQueryHandler extends QueryHandler {
|
||||||
|
static override querySelectorAll: QuerySelectorAll = (
|
||||||
|
element,
|
||||||
|
selector,
|
||||||
|
{pQuerySelectorAll}
|
||||||
|
) => {
|
||||||
|
return pQuerySelectorAll(element, selector);
|
||||||
|
};
|
||||||
|
static override querySelector: QuerySelector = (
|
||||||
|
element,
|
||||||
|
selector,
|
||||||
|
{pQuerySelector}
|
||||||
|
) => {
|
||||||
|
return pQuerySelector(element, selector);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -14,10 +14,14 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Protocol} from 'devtools-protocol';
|
|
||||||
import type {Readable} from 'stream';
|
import type {Readable} from 'stream';
|
||||||
|
|
||||||
|
import {Protocol} from 'devtools-protocol';
|
||||||
|
|
||||||
import type {Browser} from '../api/Browser.js';
|
import type {Browser} from '../api/Browser.js';
|
||||||
import type {BrowserContext} from '../api/BrowserContext.js';
|
import type {BrowserContext} from '../api/BrowserContext.js';
|
||||||
|
import {ElementHandle} from '../api/ElementHandle.js';
|
||||||
|
import {JSHandle} from '../api/JSHandle.js';
|
||||||
import {
|
import {
|
||||||
GeolocationOptions,
|
GeolocationOptions,
|
||||||
MediaFeature,
|
MediaFeature,
|
||||||
|
|
@ -35,7 +39,9 @@ import {
|
||||||
DeferredPromise,
|
DeferredPromise,
|
||||||
} from '../util/DeferredPromise.js';
|
} from '../util/DeferredPromise.js';
|
||||||
import {isErrorLike} from '../util/ErrorLike.js';
|
import {isErrorLike} from '../util/ErrorLike.js';
|
||||||
|
|
||||||
import {Accessibility} from './Accessibility.js';
|
import {Accessibility} from './Accessibility.js';
|
||||||
|
import {Binding} from './Binding.js';
|
||||||
import {
|
import {
|
||||||
CDPSession,
|
CDPSession,
|
||||||
CDPSessionEmittedEvents,
|
CDPSessionEmittedEvents,
|
||||||
|
|
@ -44,7 +50,6 @@ import {
|
||||||
import {ConsoleMessage, ConsoleMessageType} from './ConsoleMessage.js';
|
import {ConsoleMessage, ConsoleMessageType} from './ConsoleMessage.js';
|
||||||
import {Coverage} from './Coverage.js';
|
import {Coverage} from './Coverage.js';
|
||||||
import {Dialog} from './Dialog.js';
|
import {Dialog} from './Dialog.js';
|
||||||
import {ElementHandle} from './ElementHandle.js';
|
|
||||||
import {EmulationManager} from './EmulationManager.js';
|
import {EmulationManager} from './EmulationManager.js';
|
||||||
import {FileChooser} from './FileChooser.js';
|
import {FileChooser} from './FileChooser.js';
|
||||||
import {
|
import {
|
||||||
|
|
@ -59,7 +64,6 @@ import {HTTPResponse} from './HTTPResponse.js';
|
||||||
import {Keyboard, Mouse, MouseButton, Touchscreen} from './Input.js';
|
import {Keyboard, Mouse, MouseButton, Touchscreen} from './Input.js';
|
||||||
import {WaitForSelectorOptions} from './IsolatedWorld.js';
|
import {WaitForSelectorOptions} from './IsolatedWorld.js';
|
||||||
import {MAIN_WORLD} from './IsolatedWorlds.js';
|
import {MAIN_WORLD} from './IsolatedWorlds.js';
|
||||||
import {JSHandle} from './JSHandle.js';
|
|
||||||
import {
|
import {
|
||||||
Credentials,
|
Credentials,
|
||||||
NetworkConditions,
|
NetworkConditions,
|
||||||
|
|
@ -72,7 +76,13 @@ import {TargetManagerEmittedEvents} from './TargetManager.js';
|
||||||
import {TaskQueue} from './TaskQueue.js';
|
import {TaskQueue} from './TaskQueue.js';
|
||||||
import {TimeoutSettings} from './TimeoutSettings.js';
|
import {TimeoutSettings} from './TimeoutSettings.js';
|
||||||
import {Tracing} from './Tracing.js';
|
import {Tracing} from './Tracing.js';
|
||||||
import {EvaluateFunc, HandleFor, NodeFor} from './types.js';
|
import {
|
||||||
|
BindingPayload,
|
||||||
|
EvaluateFunc,
|
||||||
|
EvaluateFuncWith,
|
||||||
|
HandleFor,
|
||||||
|
NodeFor,
|
||||||
|
} from './types.js';
|
||||||
import {
|
import {
|
||||||
createJSHandle,
|
createJSHandle,
|
||||||
debugError,
|
debugError,
|
||||||
|
|
@ -83,9 +93,6 @@ import {
|
||||||
importFS,
|
importFS,
|
||||||
isNumber,
|
isNumber,
|
||||||
isString,
|
isString,
|
||||||
pageBindingDeliverErrorString,
|
|
||||||
pageBindingDeliverErrorValueString,
|
|
||||||
pageBindingDeliverResultString,
|
|
||||||
pageBindingInitString,
|
pageBindingInitString,
|
||||||
releaseObject,
|
releaseObject,
|
||||||
valueFromRemoteObject,
|
valueFromRemoteObject,
|
||||||
|
|
@ -140,7 +147,7 @@ export class CDPPage extends Page {
|
||||||
#frameManager: FrameManager;
|
#frameManager: FrameManager;
|
||||||
#emulationManager: EmulationManager;
|
#emulationManager: EmulationManager;
|
||||||
#tracing: Tracing;
|
#tracing: Tracing;
|
||||||
#pageBindings = new Map<string, Function>();
|
#bindings = new Map<string, Binding>();
|
||||||
#coverage: Coverage;
|
#coverage: Coverage;
|
||||||
#javascriptEnabled = true;
|
#javascriptEnabled = true;
|
||||||
#viewport: Viewport | null;
|
#viewport: Viewport | null;
|
||||||
|
|
@ -521,13 +528,12 @@ export class CDPPage extends Page {
|
||||||
): Promise<JSHandle<Prototype[]>> {
|
): Promise<JSHandle<Prototype[]>> {
|
||||||
const context = await this.mainFrame().executionContext();
|
const context = await this.mainFrame().executionContext();
|
||||||
assert(!prototypeHandle.disposed, 'Prototype JSHandle is disposed!');
|
assert(!prototypeHandle.disposed, 'Prototype JSHandle is disposed!');
|
||||||
const remoteObject = prototypeHandle.remoteObject();
|
|
||||||
assert(
|
assert(
|
||||||
remoteObject.objectId,
|
prototypeHandle.id,
|
||||||
'Prototype JSHandle must not be referencing primitive value'
|
'Prototype JSHandle must not be referencing primitive value'
|
||||||
);
|
);
|
||||||
const response = await context._client.send('Runtime.queryObjects', {
|
const response = await context._client.send('Runtime.queryObjects', {
|
||||||
prototypeObjectId: remoteObject.objectId,
|
prototypeObjectId: prototypeHandle.id,
|
||||||
});
|
});
|
||||||
return createJSHandle(context, response.objects) as HandleFor<Prototype[]>;
|
return createJSHandle(context, response.objects) as HandleFor<Prototype[]>;
|
||||||
}
|
}
|
||||||
|
|
@ -535,9 +541,10 @@ export class CDPPage extends Page {
|
||||||
override async $eval<
|
override async $eval<
|
||||||
Selector extends string,
|
Selector extends string,
|
||||||
Params extends unknown[],
|
Params extends unknown[],
|
||||||
Func extends EvaluateFunc<
|
Func extends EvaluateFuncWith<NodeFor<Selector>, Params> = EvaluateFuncWith<
|
||||||
[ElementHandle<NodeFor<Selector>>, ...Params]
|
NodeFor<Selector>,
|
||||||
> = EvaluateFunc<[ElementHandle<NodeFor<Selector>>, ...Params]>
|
Params
|
||||||
|
>
|
||||||
>(
|
>(
|
||||||
selector: Selector,
|
selector: Selector,
|
||||||
pageFunction: Func | string,
|
pageFunction: Func | string,
|
||||||
|
|
@ -549,9 +556,10 @@ export class CDPPage extends Page {
|
||||||
override async $$eval<
|
override async $$eval<
|
||||||
Selector extends string,
|
Selector extends string,
|
||||||
Params extends unknown[],
|
Params extends unknown[],
|
||||||
Func extends EvaluateFunc<
|
Func extends EvaluateFuncWith<
|
||||||
[Array<NodeFor<Selector>>, ...Params]
|
Array<NodeFor<Selector>>,
|
||||||
> = EvaluateFunc<[Array<NodeFor<Selector>>, ...Params]>
|
Params
|
||||||
|
> = EvaluateFuncWith<Array<NodeFor<Selector>>, Params>
|
||||||
>(
|
>(
|
||||||
selector: Selector,
|
selector: Selector,
|
||||||
pageFunction: Func | string,
|
pageFunction: Func | string,
|
||||||
|
|
@ -646,23 +654,29 @@ export class CDPPage extends Page {
|
||||||
name: string,
|
name: string,
|
||||||
pptrFunction: Function | {default: Function}
|
pptrFunction: Function | {default: Function}
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (this.#pageBindings.has(name)) {
|
if (this.#bindings.has(name)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Failed to add page binding with name ${name}: window['${name}'] already exists!`
|
`Failed to add page binding with name ${name}: window['${name}'] already exists!`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let exposedFunction: Function;
|
let binding: Binding;
|
||||||
switch (typeof pptrFunction) {
|
switch (typeof pptrFunction) {
|
||||||
case 'function':
|
case 'function':
|
||||||
exposedFunction = pptrFunction;
|
binding = new Binding(
|
||||||
|
name,
|
||||||
|
pptrFunction as (...args: unknown[]) => unknown
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
exposedFunction = pptrFunction.default;
|
binding = new Binding(
|
||||||
|
name,
|
||||||
|
pptrFunction.default as (...args: unknown[]) => unknown
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.#pageBindings.set(name, exposedFunction);
|
this.#bindings.set(name, binding);
|
||||||
|
|
||||||
const expression = pageBindingInitString('exposedFun', name);
|
const expression = pageBindingInitString('exposedFun', name);
|
||||||
await this.#client.send('Runtime.addBinding', {name: name});
|
await this.#client.send('Runtime.addBinding', {name: name});
|
||||||
|
|
@ -747,10 +761,20 @@ export class CDPPage extends Page {
|
||||||
// @see https://github.com/puppeteer/puppeteer/issues/3865
|
// @see https://github.com/puppeteer/puppeteer/issues/3865
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const context = this.#frameManager.executionContextById(
|
const context = this.#frameManager.getExecutionContextById(
|
||||||
event.executionContextId,
|
event.executionContextId,
|
||||||
this.#client
|
this.#client
|
||||||
);
|
);
|
||||||
|
if (!context) {
|
||||||
|
debugError(
|
||||||
|
new Error(
|
||||||
|
`ExecutionContext not found for a console message: ${JSON.stringify(
|
||||||
|
event
|
||||||
|
)}`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
const values = event.args.map(arg => {
|
const values = event.args.map(arg => {
|
||||||
return createJSHandle(context, arg);
|
return createJSHandle(context, arg);
|
||||||
});
|
});
|
||||||
|
|
@ -760,7 +784,7 @@ export class CDPPage extends Page {
|
||||||
async #onBindingCalled(
|
async #onBindingCalled(
|
||||||
event: Protocol.Runtime.BindingCalledEvent
|
event: Protocol.Runtime.BindingCalledEvent
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
let payload: {type: string; name: string; seq: number; args: unknown[]};
|
let payload: BindingPayload;
|
||||||
try {
|
try {
|
||||||
payload = JSON.parse(event.payload);
|
payload = JSON.parse(event.payload);
|
||||||
} catch {
|
} catch {
|
||||||
|
|
@ -768,34 +792,21 @@ export class CDPPage extends Page {
|
||||||
// called before our wrapper was initialized.
|
// called before our wrapper was initialized.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const {type, name, seq, args} = payload;
|
const {type, name, seq, args, isTrivial} = payload;
|
||||||
if (type !== 'exposedFun' || !this.#pageBindings.has(name)) {
|
if (type !== 'exposedFun') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let expression = null;
|
|
||||||
try {
|
const context = this.#frameManager.executionContextById(
|
||||||
const pageBinding = this.#pageBindings.get(name);
|
event.executionContextId,
|
||||||
assert(pageBinding);
|
|
||||||
const result = await pageBinding(...args);
|
|
||||||
expression = pageBindingDeliverResultString(name, seq, result);
|
|
||||||
} catch (error) {
|
|
||||||
if (isErrorLike(error)) {
|
|
||||||
expression = pageBindingDeliverErrorString(
|
|
||||||
name,
|
|
||||||
seq,
|
|
||||||
error.message,
|
|
||||||
error.stack
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
expression = pageBindingDeliverErrorValueString(name, seq, error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.#client
|
this.#client
|
||||||
.send('Runtime.evaluate', {
|
);
|
||||||
expression,
|
if (!context) {
|
||||||
contextId: event.executionContextId,
|
return;
|
||||||
})
|
}
|
||||||
.catch(debugError);
|
|
||||||
|
const binding = this.#bindings.get(name);
|
||||||
|
await binding?.run(context, seq, args, isTrivial);
|
||||||
}
|
}
|
||||||
|
|
||||||
#addConsoleMessage(
|
#addConsoleMessage(
|
||||||
|
|
@ -811,7 +822,7 @@ export class CDPPage extends Page {
|
||||||
}
|
}
|
||||||
const textTokens = [];
|
const textTokens = [];
|
||||||
for (const arg of args) {
|
for (const arg of args) {
|
||||||
const remoteObject = arg.remoteObject();
|
const remoteObject = arg.remoteObject() as Protocol.Runtime.RemoteObject;
|
||||||
if (remoteObject.objectId) {
|
if (remoteObject.objectId) {
|
||||||
textTokens.push(arg.toString());
|
textTokens.push(arg.toString());
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1621,11 +1632,7 @@ export class CDPPage extends Page {
|
||||||
|
|
||||||
override waitForXPath(
|
override waitForXPath(
|
||||||
xpath: string,
|
xpath: string,
|
||||||
options: {
|
options: WaitForSelectorOptions = {}
|
||||||
visible?: boolean;
|
|
||||||
hidden?: boolean;
|
|
||||||
timeout?: number;
|
|
||||||
} = {}
|
|
||||||
): Promise<ElementHandle<Node> | null> {
|
): Promise<ElementHandle<Node> | null> {
|
||||||
return this.mainFrame().waitForXPath(xpath, options);
|
return this.mainFrame().waitForXPath(xpath, options);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2023 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type PuppeteerUtil from '../injected/injected.js';
|
||||||
|
|
||||||
|
import {QueryHandler} from './QueryHandler.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export class PierceQueryHandler extends QueryHandler {
|
||||||
|
static override querySelector = (
|
||||||
|
element: Node,
|
||||||
|
selector: string,
|
||||||
|
{pierceQuerySelector}: PuppeteerUtil
|
||||||
|
): Node | null => {
|
||||||
|
return pierceQuerySelector(element, selector);
|
||||||
|
};
|
||||||
|
static override querySelectorAll = (
|
||||||
|
element: Node,
|
||||||
|
selector: string,
|
||||||
|
{pierceQuerySelectorAll}: PuppeteerUtil
|
||||||
|
): Iterable<Node> => {
|
||||||
|
return pierceQuerySelectorAll(element, selector);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -13,19 +13,15 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Browser} from '../api/Browser.js';
|
import {Browser} from '../api/Browser.js';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
BrowserConnectOptions,
|
BrowserConnectOptions,
|
||||||
_connectToCDPBrowser,
|
_connectToCDPBrowser,
|
||||||
} from './BrowserConnector.js';
|
} from './BrowserConnector.js';
|
||||||
import {ConnectionTransport} from './ConnectionTransport.js';
|
import {ConnectionTransport} from './ConnectionTransport.js';
|
||||||
import {
|
import {CustomQueryHandler, customQueryHandlers} from './CustomQueryHandler.js';
|
||||||
clearCustomQueryHandlers,
|
|
||||||
CustomQueryHandler,
|
|
||||||
customQueryHandlerNames,
|
|
||||||
registerCustomQueryHandler,
|
|
||||||
unregisterCustomQueryHandler,
|
|
||||||
} from './QueryHandler.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Settings that are common to the Puppeteer class, regardless of environment.
|
* Settings that are common to the Puppeteer class, regardless of environment.
|
||||||
|
|
@ -57,9 +53,18 @@ export interface ConnectOptions extends BrowserConnectOptions {
|
||||||
* instance of {@link PuppeteerNode} when you import or require `puppeteer`.
|
* instance of {@link PuppeteerNode} when you import or require `puppeteer`.
|
||||||
* That class extends `Puppeteer`, so has all the methods documented below as
|
* That class extends `Puppeteer`, so has all the methods documented below as
|
||||||
* well as all that are defined on {@link PuppeteerNode}.
|
* well as all that are defined on {@link PuppeteerNode}.
|
||||||
|
*
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export class Puppeteer {
|
export class Puppeteer {
|
||||||
|
/**
|
||||||
|
* Operations for {@link CustomQueryHandler | custom query handlers}. See
|
||||||
|
* {@link CustomQueryHandlerRegistry}.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
static customQueryHandlers = customQueryHandlers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a {@link CustomQueryHandler | custom query handler}.
|
* Registers a {@link CustomQueryHandler | custom query handler}.
|
||||||
*
|
*
|
||||||
|
|
@ -86,28 +91,28 @@ export class Puppeteer {
|
||||||
name: string,
|
name: string,
|
||||||
queryHandler: CustomQueryHandler
|
queryHandler: CustomQueryHandler
|
||||||
): void {
|
): void {
|
||||||
return registerCustomQueryHandler(name, queryHandler);
|
return this.customQueryHandlers.register(name, queryHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unregisters a custom query handler for a given name.
|
* Unregisters a custom query handler for a given name.
|
||||||
*/
|
*/
|
||||||
static unregisterCustomQueryHandler(name: string): void {
|
static unregisterCustomQueryHandler(name: string): void {
|
||||||
return unregisterCustomQueryHandler(name);
|
return this.customQueryHandlers.unregister(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the names of all custom query handlers.
|
* Gets the names of all custom query handlers.
|
||||||
*/
|
*/
|
||||||
static customQueryHandlerNames(): string[] {
|
static customQueryHandlerNames(): string[] {
|
||||||
return customQueryHandlerNames();
|
return this.customQueryHandlers.names();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unregisters all custom query handlers.
|
* Unregisters all custom query handlers.
|
||||||
*/
|
*/
|
||||||
static clearCustomQueryHandlers(): void {
|
static clearCustomQueryHandlers(): void {
|
||||||
return clearCustomQueryHandlers();
|
return this.customQueryHandlers.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* Copyright 2020 Google Inc. All rights reserved.
|
* Copyright 2023 Google Inc. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -14,305 +14,202 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import PuppeteerUtil from '../injected/injected.js';
|
import {ElementHandle} from '../api/ElementHandle.js';
|
||||||
import {ariaHandler} from './AriaQueryHandler.js';
|
import type PuppeteerUtil from '../injected/injected.js';
|
||||||
import {ElementHandle} from './ElementHandle.js';
|
import {assert} from '../util/assert.js';
|
||||||
import {Frame} from './Frame.js';
|
import {isErrorLike} from '../util/ErrorLike.js';
|
||||||
import {WaitForSelectorOptions} from './IsolatedWorld.js';
|
import {interpolateFunction, stringifyFunction} from '../util/Function.js';
|
||||||
|
|
||||||
|
import type {Frame} from './Frame.js';
|
||||||
|
import {transposeIterableHandle} from './HandleIterator.js';
|
||||||
|
import type {WaitForSelectorOptions} from './IsolatedWorld.js';
|
||||||
import {MAIN_WORLD, PUPPETEER_WORLD} from './IsolatedWorlds.js';
|
import {MAIN_WORLD, PUPPETEER_WORLD} from './IsolatedWorlds.js';
|
||||||
|
import {LazyArg} from './LazyArg.js';
|
||||||
/**
|
import type {Awaitable, AwaitableIterable} from './types.js';
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export interface CustomQueryHandler {
|
|
||||||
/**
|
|
||||||
* @returns A {@link Node} matching the given `selector` from {@link node}.
|
|
||||||
*/
|
|
||||||
queryOne?: (node: Node, selector: string) => Node | null;
|
|
||||||
/**
|
|
||||||
* @returns Some {@link Node}s matching the given `selector` from {@link node}.
|
|
||||||
*/
|
|
||||||
queryAll?: (node: Node, selector: string) => Node[];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export interface InternalQueryHandler {
|
export type QuerySelectorAll = (
|
||||||
/**
|
|
||||||
* @returns A {@link Node} matching the given `selector` from {@link node}.
|
|
||||||
*/
|
|
||||||
queryOne?: (
|
|
||||||
node: Node,
|
node: Node,
|
||||||
selector: string,
|
selector: string,
|
||||||
PuppeteerUtil: PuppeteerUtil
|
PuppeteerUtil: PuppeteerUtil
|
||||||
) => Node | null;
|
) => AwaitableIterable<Node>;
|
||||||
/**
|
|
||||||
* @returns Some {@link Node}s matching the given `selector` from {@link node}.
|
|
||||||
*/
|
|
||||||
queryAll?: (
|
|
||||||
node: Node,
|
|
||||||
selector: string,
|
|
||||||
PuppeteerUtil: PuppeteerUtil
|
|
||||||
) => Node[];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export interface PuppeteerQueryHandler {
|
export type QuerySelector = (
|
||||||
/**
|
node: Node,
|
||||||
* Queries for a single node given a selector and {@link ElementHandle}.
|
selector: string,
|
||||||
*
|
PuppeteerUtil: PuppeteerUtil
|
||||||
* Akin to {@link Window.prototype.querySelector}.
|
) => Awaitable<Node | null>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
*/
|
*/
|
||||||
queryOne?: (
|
export class QueryHandler {
|
||||||
element: ElementHandle<Node>,
|
// Either one of these may be implemented, but at least one must be.
|
||||||
selector: string
|
static querySelectorAll?: QuerySelectorAll;
|
||||||
) => Promise<ElementHandle<Node> | null>;
|
static querySelector?: QuerySelector;
|
||||||
|
|
||||||
|
static get _querySelector(): QuerySelector {
|
||||||
|
if (this.querySelector) {
|
||||||
|
return this.querySelector;
|
||||||
|
}
|
||||||
|
if (!this.querySelectorAll) {
|
||||||
|
throw new Error('Cannot create default `querySelector`.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return (this.querySelector = interpolateFunction(
|
||||||
|
async (node, selector, PuppeteerUtil) => {
|
||||||
|
const querySelectorAll: QuerySelectorAll =
|
||||||
|
PLACEHOLDER('querySelectorAll');
|
||||||
|
const results = querySelectorAll(node, selector, PuppeteerUtil);
|
||||||
|
for await (const result of results) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
querySelectorAll: stringifyFunction(this.querySelectorAll),
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
static get _querySelectorAll(): QuerySelectorAll {
|
||||||
|
if (this.querySelectorAll) {
|
||||||
|
return this.querySelectorAll;
|
||||||
|
}
|
||||||
|
if (!this.querySelector) {
|
||||||
|
throw new Error('Cannot create default `querySelectorAll`.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return (this.querySelectorAll = interpolateFunction(
|
||||||
|
async function* (node, selector, PuppeteerUtil) {
|
||||||
|
const querySelector: QuerySelector = PLACEHOLDER('querySelector');
|
||||||
|
const result = await querySelector(node, selector, PuppeteerUtil);
|
||||||
|
if (result) {
|
||||||
|
yield result;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
querySelector: stringifyFunction(this.querySelector),
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queries for multiple nodes given a selector and {@link ElementHandle}.
|
* Queries for multiple nodes given a selector and {@link ElementHandle}.
|
||||||
*
|
*
|
||||||
* Akin to {@link Window.prototype.querySelectorAll}.
|
* Akin to {@link Document.prototype.querySelectorAll}.
|
||||||
*/
|
*/
|
||||||
queryAll?: (
|
static async *queryAll(
|
||||||
element: ElementHandle<Node>,
|
element: ElementHandle<Node>,
|
||||||
selector: string
|
selector: string
|
||||||
) => Promise<Array<ElementHandle<Node>>>;
|
): AwaitableIterable<ElementHandle<Node>> {
|
||||||
|
const world = element.executionContext()._world;
|
||||||
|
assert(world);
|
||||||
|
const handle = await element.evaluateHandle(
|
||||||
|
this._querySelectorAll,
|
||||||
|
selector,
|
||||||
|
LazyArg.create(context => {
|
||||||
|
return context.puppeteerUtil;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
yield* transposeIterableHandle(handle);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Waits until a single node appears for a given selector and
|
* Queries for a single node given a selector and {@link ElementHandle}.
|
||||||
* {@link ElementHandle}.
|
*
|
||||||
|
* Akin to {@link Document.prototype.querySelector}.
|
||||||
*/
|
*/
|
||||||
waitFor?: (
|
static async queryOne(
|
||||||
elementOrFrame: ElementHandle<Node> | Frame,
|
element: ElementHandle<Node>,
|
||||||
selector: string,
|
selector: string
|
||||||
options: WaitForSelectorOptions
|
): Promise<ElementHandle<Node> | null> {
|
||||||
) => Promise<ElementHandle<Node> | null>;
|
const world = element.executionContext()._world;
|
||||||
}
|
assert(world);
|
||||||
|
const result = await element.evaluateHandle(
|
||||||
function createPuppeteerQueryHandler(
|
this._querySelector,
|
||||||
handler: InternalQueryHandler
|
|
||||||
): PuppeteerQueryHandler {
|
|
||||||
const internalHandler: PuppeteerQueryHandler = {};
|
|
||||||
|
|
||||||
if (handler.queryOne) {
|
|
||||||
const queryOne = handler.queryOne;
|
|
||||||
internalHandler.queryOne = async (element, selector) => {
|
|
||||||
const jsHandle = await element.evaluateHandle(
|
|
||||||
queryOne,
|
|
||||||
selector,
|
selector,
|
||||||
await element.executionContext()._world!.puppeteerUtil
|
LazyArg.create(context => {
|
||||||
|
return context.puppeteerUtil;
|
||||||
|
})
|
||||||
);
|
);
|
||||||
const elementHandle = jsHandle.asElement();
|
|
||||||
if (elementHandle) {
|
|
||||||
return elementHandle;
|
|
||||||
}
|
|
||||||
await jsHandle.dispose();
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
internalHandler.waitFor = async (elementOrFrame, selector, options) => {
|
|
||||||
let frame: Frame;
|
|
||||||
let element: ElementHandle<Node> | undefined;
|
|
||||||
if (elementOrFrame instanceof Frame) {
|
|
||||||
frame = elementOrFrame;
|
|
||||||
} else {
|
|
||||||
frame = elementOrFrame.frame;
|
|
||||||
element = await frame.worlds[PUPPETEER_WORLD].adoptHandle(
|
|
||||||
elementOrFrame
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const result = await frame.worlds[PUPPETEER_WORLD]._waitForSelectorInPage(
|
|
||||||
queryOne,
|
|
||||||
element,
|
|
||||||
selector,
|
|
||||||
options
|
|
||||||
);
|
|
||||||
if (element) {
|
|
||||||
await element.dispose();
|
|
||||||
}
|
|
||||||
if (!result) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (!(result instanceof ElementHandle)) {
|
if (!(result instanceof ElementHandle)) {
|
||||||
await result.dispose();
|
await result.dispose();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return frame.worlds[MAIN_WORLD].transferHandle(result);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handler.queryAll) {
|
|
||||||
const queryAll = handler.queryAll;
|
|
||||||
internalHandler.queryAll = async (element, selector) => {
|
|
||||||
const jsHandle = await element.evaluateHandle(
|
|
||||||
queryAll,
|
|
||||||
selector,
|
|
||||||
await element.executionContext()._world!.puppeteerUtil
|
|
||||||
);
|
|
||||||
const properties = await jsHandle.getProperties();
|
|
||||||
await jsHandle.dispose();
|
|
||||||
const result = [];
|
|
||||||
for (const property of properties.values()) {
|
|
||||||
const elementHandle = property.asElement();
|
|
||||||
if (elementHandle) {
|
|
||||||
result.push(elementHandle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
return result;
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return internalHandler;
|
/**
|
||||||
}
|
* Waits until a single node appears for a given selector and
|
||||||
|
* {@link ElementHandle}.
|
||||||
|
*
|
||||||
|
* This will always query the handle in the Puppeteer world and migrate the
|
||||||
|
* result to the main world.
|
||||||
|
*/
|
||||||
|
static async waitFor(
|
||||||
|
elementOrFrame: ElementHandle<Node> | Frame,
|
||||||
|
selector: string,
|
||||||
|
options: WaitForSelectorOptions
|
||||||
|
): Promise<ElementHandle<Node> | null> {
|
||||||
|
let frame: Frame;
|
||||||
|
let element: ElementHandle<Node> | undefined;
|
||||||
|
if (!(elementOrFrame instanceof ElementHandle)) {
|
||||||
|
frame = elementOrFrame;
|
||||||
|
} else {
|
||||||
|
frame = elementOrFrame.frame;
|
||||||
|
element = await frame.worlds[PUPPETEER_WORLD].adoptHandle(elementOrFrame);
|
||||||
|
}
|
||||||
|
|
||||||
const defaultHandler = createPuppeteerQueryHandler({
|
const {visible = false, hidden = false, timeout} = options;
|
||||||
queryOne: (element, selector) => {
|
|
||||||
if (!('querySelector' in element)) {
|
try {
|
||||||
throw new Error(
|
const handle = await frame.worlds[PUPPETEER_WORLD].waitForFunction(
|
||||||
`Could not invoke \`querySelector\` on node of type ${element.nodeName}.`
|
async (PuppeteerUtil, query, selector, root, visible) => {
|
||||||
|
const querySelector = PuppeteerUtil.createFunction(
|
||||||
|
query
|
||||||
|
) as QuerySelector;
|
||||||
|
const node = await querySelector(
|
||||||
|
root ?? document,
|
||||||
|
selector,
|
||||||
|
PuppeteerUtil
|
||||||
);
|
);
|
||||||
}
|
return PuppeteerUtil.checkVisibility(node, visible);
|
||||||
return (
|
|
||||||
element as unknown as {querySelector(selector: string): Element}
|
|
||||||
).querySelector(selector);
|
|
||||||
},
|
},
|
||||||
queryAll: (element, selector) => {
|
{
|
||||||
if (!('querySelectorAll' in element)) {
|
polling: visible || hidden ? 'raf' : 'mutation',
|
||||||
throw new Error(
|
root: element,
|
||||||
`Could not invoke \`querySelectorAll\` on node of type ${element.nodeName}.`
|
timeout,
|
||||||
|
},
|
||||||
|
LazyArg.create(context => {
|
||||||
|
return context.puppeteerUtil;
|
||||||
|
}),
|
||||||
|
stringifyFunction(this._querySelector),
|
||||||
|
selector,
|
||||||
|
element,
|
||||||
|
visible ? true : hidden ? false : undefined
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!(handle instanceof ElementHandle)) {
|
||||||
|
await handle.dispose();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return frame.worlds[MAIN_WORLD].transferHandle(handle);
|
||||||
|
} catch (error) {
|
||||||
|
if (!isErrorLike(error)) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
error.message = `Waiting for selector \`${selector}\` failed: ${error.message}`;
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
if (element) {
|
||||||
|
await element.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return [
|
|
||||||
...(
|
|
||||||
element as unknown as {
|
|
||||||
querySelectorAll(selector: string): NodeList;
|
|
||||||
}
|
}
|
||||||
).querySelectorAll(selector),
|
|
||||||
];
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const pierceHandler = createPuppeteerQueryHandler({
|
|
||||||
queryOne: (element, selector, {pierceQuerySelector}) => {
|
|
||||||
return pierceQuerySelector(element, selector);
|
|
||||||
},
|
|
||||||
queryAll: (element, selector, {pierceQuerySelectorAll}) => {
|
|
||||||
return pierceQuerySelectorAll(element, selector);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const xpathHandler = createPuppeteerQueryHandler({
|
|
||||||
queryOne: (element, selector, {xpathQuerySelector}) => {
|
|
||||||
return xpathQuerySelector(element, selector);
|
|
||||||
},
|
|
||||||
queryAll: (element, selector, {xpathQuerySelectorAll}) => {
|
|
||||||
return xpathQuerySelectorAll(element, selector);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const textQueryHandler = createPuppeteerQueryHandler({
|
|
||||||
queryOne: (element, selector, {textQuerySelector}) => {
|
|
||||||
return textQuerySelector(element, selector);
|
|
||||||
},
|
|
||||||
queryAll: (element, selector, {textQuerySelectorAll}) => {
|
|
||||||
return textQuerySelectorAll(element, selector);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
interface RegisteredQueryHandler {
|
|
||||||
handler: PuppeteerQueryHandler;
|
|
||||||
transformSelector?: (selector: string) => string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const INTERNAL_QUERY_HANDLERS = new Map<string, RegisteredQueryHandler>([
|
|
||||||
['aria', {handler: ariaHandler}],
|
|
||||||
['pierce', {handler: pierceHandler}],
|
|
||||||
['xpath', {handler: xpathHandler}],
|
|
||||||
['text', {handler: textQueryHandler}],
|
|
||||||
]);
|
|
||||||
const QUERY_HANDLERS = new Map<string, RegisteredQueryHandler>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Import {@link Puppeteer} and use the static method
|
|
||||||
* {@link Puppeteer.registerCustomQueryHandler}
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export function registerCustomQueryHandler(
|
|
||||||
name: string,
|
|
||||||
handler: CustomQueryHandler
|
|
||||||
): void {
|
|
||||||
if (INTERNAL_QUERY_HANDLERS.has(name)) {
|
|
||||||
throw new Error(`A query handler named "${name}" already exists`);
|
|
||||||
}
|
|
||||||
if (QUERY_HANDLERS.has(name)) {
|
|
||||||
throw new Error(`A custom query handler named "${name}" already exists`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const isValidName = /^[a-zA-Z]+$/.test(name);
|
|
||||||
if (!isValidName) {
|
|
||||||
throw new Error(`Custom query handler names may only contain [a-zA-Z]`);
|
|
||||||
}
|
|
||||||
|
|
||||||
QUERY_HANDLERS.set(name, {handler: createPuppeteerQueryHandler(handler)});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Import {@link Puppeteer} and use the static method
|
|
||||||
* {@link Puppeteer.unregisterCustomQueryHandler}
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export function unregisterCustomQueryHandler(name: string): void {
|
|
||||||
QUERY_HANDLERS.delete(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Import {@link Puppeteer} and use the static method
|
|
||||||
* {@link Puppeteer.customQueryHandlerNames}
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export function customQueryHandlerNames(): string[] {
|
|
||||||
return [...QUERY_HANDLERS.keys()];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Import {@link Puppeteer} and use the static method
|
|
||||||
* {@link Puppeteer.clearCustomQueryHandlers}
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export function clearCustomQueryHandlers(): void {
|
|
||||||
QUERY_HANDLERS.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
const CUSTOM_QUERY_SEPARATORS = ['=', '/'];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
*/
|
|
||||||
export function getQueryHandlerAndSelector(selector: string): {
|
|
||||||
updatedSelector: string;
|
|
||||||
queryHandler: PuppeteerQueryHandler;
|
|
||||||
} {
|
|
||||||
for (const handlerMap of [QUERY_HANDLERS, INTERNAL_QUERY_HANDLERS]) {
|
|
||||||
for (const [
|
|
||||||
name,
|
|
||||||
{handler: queryHandler, transformSelector},
|
|
||||||
] of handlerMap) {
|
|
||||||
for (const separator of CUSTOM_QUERY_SEPARATORS) {
|
|
||||||
const prefix = `${name}${separator}`;
|
|
||||||
if (selector.startsWith(prefix)) {
|
|
||||||
selector = selector.slice(prefix.length);
|
|
||||||
if (transformSelector) {
|
|
||||||
selector = transformSelector(selector);
|
|
||||||
}
|
|
||||||
return {updatedSelector: selector, queryHandler};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {updatedSelector: selector, queryHandler: defaultHandler};
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
import {source as injectedSource} from '../generated/injected.js';
|
||||||
|
|
||||||
|
class ScriptInjector {
|
||||||
|
#updated = false;
|
||||||
|
#amendments = new Set<string>();
|
||||||
|
|
||||||
|
// Appends a statement of the form `(PuppeteerUtil) => {...}`.
|
||||||
|
append(statement: string): void {
|
||||||
|
this.#update(() => {
|
||||||
|
this.#amendments.add(statement);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pop(statement: string): void {
|
||||||
|
this.#update(() => {
|
||||||
|
this.#amendments.delete(statement);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
inject(inject: (script: string) => void, force = false) {
|
||||||
|
if (this.#updated || force) {
|
||||||
|
inject(this.#get());
|
||||||
|
}
|
||||||
|
this.#updated = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#update(callback: () => void): void {
|
||||||
|
callback();
|
||||||
|
this.#updated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#get(): string {
|
||||||
|
return `(() => {
|
||||||
|
const module = {};
|
||||||
|
${injectedSource}
|
||||||
|
${[...this.#amendments]
|
||||||
|
.map(statement => {
|
||||||
|
return `(${statement})(module.exports.default);`;
|
||||||
|
})
|
||||||
|
.join('')}
|
||||||
|
return module.exports.default;
|
||||||
|
})()`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export const scriptInjector = new ScriptInjector();
|
||||||
|
|
@ -14,16 +14,18 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Page, PageEmittedEvents} from '../api/Page.js';
|
import {Protocol} from 'devtools-protocol';
|
||||||
import {WebWorker} from './WebWorker.js';
|
|
||||||
import {CDPSession} from './Connection.js';
|
|
||||||
import type {Browser, IsPageTargetCallback} from '../api/Browser.js';
|
import type {Browser, IsPageTargetCallback} from '../api/Browser.js';
|
||||||
import type {BrowserContext} from '../api/BrowserContext.js';
|
import type {BrowserContext} from '../api/BrowserContext.js';
|
||||||
import {Viewport} from './PuppeteerViewport.js';
|
import {Page, PageEmittedEvents} from '../api/Page.js';
|
||||||
import {Protocol} from 'devtools-protocol';
|
|
||||||
import {TaskQueue} from './TaskQueue.js';
|
import {CDPSession} from './Connection.js';
|
||||||
import {TargetManager} from './TargetManager.js';
|
|
||||||
import {CDPPage} from './Page.js';
|
import {CDPPage} from './Page.js';
|
||||||
|
import {Viewport} from './PuppeteerViewport.js';
|
||||||
|
import {TargetManager} from './TargetManager.js';
|
||||||
|
import {TaskQueue} from './TaskQueue.js';
|
||||||
|
import {WebWorker} from './WebWorker.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Target represents a
|
* Target represents a
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Protocol} from 'devtools-protocol';
|
import {Protocol} from 'devtools-protocol';
|
||||||
|
|
||||||
import {CDPSession} from './Connection.js';
|
import {CDPSession} from './Connection.js';
|
||||||
import {EventEmitter} from './EventEmitter.js';
|
import {EventEmitter} from './EventEmitter.js';
|
||||||
import {Target} from './Target.js';
|
import {Target} from './Target.js';
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2023 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {QueryHandler, QuerySelectorAll} from './QueryHandler.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export class TextQueryHandler extends QueryHandler {
|
||||||
|
static override querySelectorAll: QuerySelectorAll = (
|
||||||
|
element,
|
||||||
|
selector,
|
||||||
|
{textQuerySelectorAll}
|
||||||
|
) => {
|
||||||
|
return textQuerySelectorAll(element, selector);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -14,9 +14,10 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import {assert} from '../util/assert.js';
|
import {assert} from '../util/assert.js';
|
||||||
import {getReadableAsBuffer, getReadableFromProtocolStream} from './util.js';
|
|
||||||
import {isErrorLike} from '../util/ErrorLike.js';
|
import {isErrorLike} from '../util/ErrorLike.js';
|
||||||
|
|
||||||
import {CDPSession} from './Connection.js';
|
import {CDPSession} from './Connection.js';
|
||||||
|
import {getReadableAsBuffer, getReadableFromProtocolStream} from './util.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
|
|
|
||||||
|
|
@ -14,19 +14,21 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {ElementHandle} from '../api/ElementHandle.js';
|
||||||
|
import {JSHandle} from '../api/JSHandle.js';
|
||||||
import type {Poller} from '../injected/Poller.js';
|
import type {Poller} from '../injected/Poller.js';
|
||||||
import {createDeferredPromise} from '../util/DeferredPromise.js';
|
import {createDeferredPromise} from '../util/DeferredPromise.js';
|
||||||
import {ElementHandle} from './ElementHandle.js';
|
import {stringifyFunction} from '../util/Function.js';
|
||||||
|
|
||||||
import {TimeoutError} from './Errors.js';
|
import {TimeoutError} from './Errors.js';
|
||||||
import {IsolatedWorld} from './IsolatedWorld.js';
|
import {IsolatedWorld} from './IsolatedWorld.js';
|
||||||
import {JSHandle} from './JSHandle.js';
|
import {LazyArg} from './LazyArg.js';
|
||||||
import {HandleFor} from './types.js';
|
import {HandleFor} from './types.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export interface WaitTaskOptions {
|
export interface WaitTaskOptions {
|
||||||
bindings?: Map<string, (...args: never[]) => unknown>;
|
|
||||||
polling: 'raf' | 'mutation' | number;
|
polling: 'raf' | 'mutation' | number;
|
||||||
root?: ElementHandle<Node>;
|
root?: ElementHandle<Node>;
|
||||||
timeout: number;
|
timeout: number;
|
||||||
|
|
@ -37,7 +39,6 @@ export interface WaitTaskOptions {
|
||||||
*/
|
*/
|
||||||
export class WaitTask<T = unknown> {
|
export class WaitTask<T = unknown> {
|
||||||
#world: IsolatedWorld;
|
#world: IsolatedWorld;
|
||||||
#bindings: Map<string, (...args: never[]) => unknown>;
|
|
||||||
#polling: 'raf' | 'mutation' | number;
|
#polling: 'raf' | 'mutation' | number;
|
||||||
#root?: ElementHandle<Node>;
|
#root?: ElementHandle<Node>;
|
||||||
|
|
||||||
|
|
@ -57,7 +58,6 @@ export class WaitTask<T = unknown> {
|
||||||
...args: unknown[]
|
...args: unknown[]
|
||||||
) {
|
) {
|
||||||
this.#world = world;
|
this.#world = world;
|
||||||
this.#bindings = options.bindings ?? new Map();
|
|
||||||
this.#polling = options.polling;
|
this.#polling = options.polling;
|
||||||
this.#root = options.root;
|
this.#root = options.root;
|
||||||
|
|
||||||
|
|
@ -66,7 +66,7 @@ export class WaitTask<T = unknown> {
|
||||||
this.#fn = `() => {return (${fn});}`;
|
this.#fn = `() => {return (${fn});}`;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
this.#fn = fn.toString();
|
this.#fn = stringifyFunction(fn);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this.#args = args;
|
this.#args = args;
|
||||||
|
|
@ -81,12 +81,6 @@ export class WaitTask<T = unknown> {
|
||||||
}, options.timeout);
|
}, options.timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.#bindings.size !== 0) {
|
|
||||||
for (const [name, fn] of this.#bindings) {
|
|
||||||
this.#world._boundFunctions.set(name, fn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.rerun();
|
this.rerun();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -96,15 +90,6 @@ export class WaitTask<T = unknown> {
|
||||||
|
|
||||||
async rerun(): Promise<void> {
|
async rerun(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
if (this.#bindings.size !== 0) {
|
|
||||||
const context = await this.#world.executionContext();
|
|
||||||
await Promise.all(
|
|
||||||
[...this.#bindings].map(async ([name]) => {
|
|
||||||
return await this.#world._addBindingToContext(context, name);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (this.#polling) {
|
switch (this.#polling) {
|
||||||
case 'raf':
|
case 'raf':
|
||||||
this.#poller = await this.#world.evaluateHandle(
|
this.#poller = await this.#world.evaluateHandle(
|
||||||
|
|
@ -114,7 +99,9 @@ export class WaitTask<T = unknown> {
|
||||||
return fun(...args) as Promise<T>;
|
return fun(...args) as Promise<T>;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
await this.#world.puppeteerUtil,
|
LazyArg.create(context => {
|
||||||
|
return context.puppeteerUtil;
|
||||||
|
}),
|
||||||
this.#fn,
|
this.#fn,
|
||||||
...this.#args
|
...this.#args
|
||||||
);
|
);
|
||||||
|
|
@ -127,7 +114,9 @@ export class WaitTask<T = unknown> {
|
||||||
return fun(...args) as Promise<T>;
|
return fun(...args) as Promise<T>;
|
||||||
}, root || document);
|
}, root || document);
|
||||||
},
|
},
|
||||||
await this.#world.puppeteerUtil,
|
LazyArg.create(context => {
|
||||||
|
return context.puppeteerUtil;
|
||||||
|
}),
|
||||||
this.#root,
|
this.#root,
|
||||||
this.#fn,
|
this.#fn,
|
||||||
...this.#args
|
...this.#args
|
||||||
|
|
@ -141,7 +130,9 @@ export class WaitTask<T = unknown> {
|
||||||
return fun(...args) as Promise<T>;
|
return fun(...args) as Promise<T>;
|
||||||
}, ms);
|
}, ms);
|
||||||
},
|
},
|
||||||
await this.#world.puppeteerUtil,
|
LazyArg.create(context => {
|
||||||
|
return context.puppeteerUtil;
|
||||||
|
}),
|
||||||
this.#polling,
|
this.#polling,
|
||||||
this.#fn,
|
this.#fn,
|
||||||
...this.#args
|
...this.#args
|
||||||
|
|
|
||||||
|
|
@ -14,21 +14,23 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import {Protocol} from 'devtools-protocol';
|
import {Protocol} from 'devtools-protocol';
|
||||||
|
|
||||||
|
import {createDeferredPromise} from '../util/DeferredPromise.js';
|
||||||
|
|
||||||
import {CDPSession} from './Connection.js';
|
import {CDPSession} from './Connection.js';
|
||||||
import {ConsoleMessageType} from './ConsoleMessage.js';
|
import {ConsoleMessageType} from './ConsoleMessage.js';
|
||||||
import {EvaluateFunc, HandleFor} from './types.js';
|
|
||||||
import {EventEmitter} from './EventEmitter.js';
|
import {EventEmitter} from './EventEmitter.js';
|
||||||
import {ExecutionContext} from './ExecutionContext.js';
|
import {ExecutionContext} from './ExecutionContext.js';
|
||||||
import {JSHandle} from './JSHandle.js';
|
import {CDPJSHandle} from './JSHandle.js';
|
||||||
|
import {EvaluateFunc, HandleFor} from './types.js';
|
||||||
import {debugError} from './util.js';
|
import {debugError} from './util.js';
|
||||||
import {createDeferredPromise} from '../util/DeferredPromise.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
export type ConsoleAPICalledCallback = (
|
export type ConsoleAPICalledCallback = (
|
||||||
eventType: ConsoleMessageType,
|
eventType: ConsoleMessageType,
|
||||||
handles: JSHandle[],
|
handles: CDPJSHandle[],
|
||||||
trace: Protocol.Runtime.StackTrace
|
trace: Protocol.Runtime.StackTrace
|
||||||
) => void;
|
) => void;
|
||||||
|
|
||||||
|
|
@ -93,7 +95,7 @@ export class WebWorker extends EventEmitter {
|
||||||
return consoleAPICalled(
|
return consoleAPICalled(
|
||||||
event.type,
|
event.type,
|
||||||
event.args.map((object: Protocol.Runtime.RemoteObject) => {
|
event.args.map((object: Protocol.Runtime.RemoteObject) => {
|
||||||
return new JSHandle(context, object);
|
return new CDPJSHandle(context, object);
|
||||||
}),
|
}),
|
||||||
event.stackTrace
|
event.stackTrace
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2023 Google Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {QueryHandler, QuerySelectorAll} from './QueryHandler.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export class XPathQueryHandler extends QueryHandler {
|
||||||
|
static override querySelectorAll: QuerySelectorAll = (
|
||||||
|
element,
|
||||||
|
selector,
|
||||||
|
{xpathQuerySelectorAll}
|
||||||
|
) => {
|
||||||
|
return xpathQuerySelectorAll(element, selector);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,12 @@
|
||||||
import {CDPSession, Connection as CDPPPtrConnection} from '../Connection.js';
|
import * as BidiMapper from 'chromium-bidi/lib/cjs/bidiMapper/bidiMapper.js';
|
||||||
import {Connection as BidiPPtrConnection} from './Connection.js';
|
import * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js';
|
||||||
import {Bidi, BidiMapper} from '../../../third_party/chromium-bidi/index.js';
|
|
||||||
import type {ProtocolMapping} from 'devtools-protocol/types/protocol-mapping.js';
|
import type {ProtocolMapping} from 'devtools-protocol/types/protocol-mapping.js';
|
||||||
|
|
||||||
|
import {CDPSession, Connection as CDPPPtrConnection} from '../Connection.js';
|
||||||
import {Handler} from '../EventEmitter.js';
|
import {Handler} from '../EventEmitter.js';
|
||||||
|
|
||||||
|
import {Connection as BidiPPtrConnection} from './Connection.js';
|
||||||
|
|
||||||
type CdpEvents = {
|
type CdpEvents = {
|
||||||
[Property in keyof ProtocolMapping.Events]: ProtocolMapping.Events[Property][0];
|
[Property in keyof ProtocolMapping.Events]: ProtocolMapping.Events[Property][0];
|
||||||
};
|
};
|
||||||
|
|
@ -99,9 +102,9 @@ class CDPClientAdapter<
|
||||||
this.#client.on('*', this.#forwardMessage as Handler<any>);
|
this.#client.on('*', this.#forwardMessage as Handler<any>);
|
||||||
}
|
}
|
||||||
|
|
||||||
#forwardMessage = (
|
#forwardMessage = <T extends keyof CdpEvents>(
|
||||||
method: keyof ProtocolMapping.Events,
|
method: T,
|
||||||
event: ProtocolMapping.Events[keyof ProtocolMapping.Events]
|
event: CdpEvents[T]
|
||||||
) => {
|
) => {
|
||||||
this.emit(method, event);
|
this.emit(method, event);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -14,15 +14,17 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {ChildProcess} from 'child_process';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Browser as BrowserBase,
|
Browser as BrowserBase,
|
||||||
BrowserCloseCallback,
|
BrowserCloseCallback,
|
||||||
BrowserContextOptions,
|
BrowserContextOptions,
|
||||||
} from '../../api/Browser.js';
|
} from '../../api/Browser.js';
|
||||||
import {BrowserContext as BrowserContextBase} from '../../api/BrowserContext.js';
|
import {BrowserContext as BrowserContextBase} from '../../api/BrowserContext.js';
|
||||||
import {Connection} from './Connection.js';
|
|
||||||
import {ChildProcess} from 'child_process';
|
|
||||||
import {BrowserContext} from './BrowserContext.js';
|
import {BrowserContext} from './BrowserContext.js';
|
||||||
|
import {Connection} from './Connection.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
|
|
@ -34,7 +36,7 @@ export class Browser extends BrowserBase {
|
||||||
static async create(opts: Options): Promise<Browser> {
|
static async create(opts: Options): Promise<Browser> {
|
||||||
// TODO: await until the connection is established.
|
// TODO: await until the connection is established.
|
||||||
try {
|
try {
|
||||||
(await opts.connection.send('session.new', {})) as {sessionId: string};
|
await opts.connection.send('session.new', {});
|
||||||
} catch {}
|
} catch {}
|
||||||
return new Browser(opts);
|
return new Browser(opts);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
import {BrowserContext as BrowserContextBase} from '../../api/BrowserContext.js';
|
import {BrowserContext as BrowserContextBase} from '../../api/BrowserContext.js';
|
||||||
import {Page as PageBase} from '../../api/Page.js';
|
import {Page as PageBase} from '../../api/Page.js';
|
||||||
|
|
||||||
import {Connection} from './Connection.js';
|
import {Connection} from './Connection.js';
|
||||||
import {Page} from './Page.js';
|
import {Page} from './Page.js';
|
||||||
|
|
||||||
|
|
@ -31,10 +32,10 @@ export class BrowserContext extends BrowserContextBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
override async newPage(): Promise<PageBase> {
|
override async newPage(): Promise<PageBase> {
|
||||||
const result = (await this.#connection.send('browsingContext.create', {
|
const response = await this.#connection.send('browsingContext.create', {
|
||||||
type: 'tab',
|
type: 'tab',
|
||||||
})) as {context: string};
|
});
|
||||||
return new Page(this.#connection, result.context);
|
return new Page(this.#connection, response.result.context);
|
||||||
}
|
}
|
||||||
|
|
||||||
override async close(): Promise<void> {}
|
override async close(): Promise<void> {}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue