first commit

This commit is contained in:
Frank John Begornia
2025-12-23 01:51:15 +08:00
commit c926590e1d
4137 changed files with 613038 additions and 0 deletions

1
node_modules/chromium-bidi/.browser generated vendored Normal file
View File

@@ -0,0 +1 @@
chrome@122.0.6249.0

201
node_modules/chromium-bidi/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

474
node_modules/chromium-bidi/README.md generated vendored Normal file
View File

@@ -0,0 +1,474 @@
# WebDriver BiDi for Chromium [![chromium-bidi on npm](https://img.shields.io/npm/v/chromium-bidi)](https://www.npmjs.com/package/chromium-bidi)
## CI status
![E2E Tests](https://github.com/GoogleChromeLabs/chromium-bidi/actions/workflows/e2e.yml/badge.svg)
![Unit Tests](https://github.com/GoogleChromeLabs/chromium-bidi/actions/workflows/unit.yml/badge.svg)
![WPT Tests](https://github.com/GoogleChromeLabs/chromium-bidi/actions/workflows/wpt.yml/badge.svg)
![Pre-commit](https://github.com/GoogleChromeLabs/chromium-bidi/actions/workflows/pre-commit.yml/badge.svg)
[![codecov](https://codecov.io/gh/GoogleChromeLabs/chromium-bidi/branch/main/graph/badge.svg?token=LJSXSC9L09)](https://codecov.io/gh/GoogleChromeLabs/chromium-bidi)
This is an implementation of the
[WebDriver BiDi](https://w3c.github.io/webdriver-bidi/) protocol with some
extensions (**BiDi+**)
for Chromium, implemented as a JavaScript layer translating between BiDi and CDP,
running inside a Chrome tab.
Current status can be checked
at [WPT WebDriver BiDi status](https://wpt.fyi/results/webdriver/tests/bidi).
## BiDi+
**"BiDi+"** is an extension of the WebDriver BiDi protocol. In addition to [WebDriver BiDi](https://w3c.github.io/webdriver-bidi/) it has:
### Command `cdp.sendCommand`
```cddl
CdpSendCommandCommand = {
method: "cdp.sendCommand",
params: ScriptEvaluateParameters,
}
CdpSendCommandParameters = {
method: text,
params: any,
session?: text,
}
CdpSendCommandResult = {
result: any,
session: text,
}
```
The command runs the
described [CDP command](https://chromedevtools.github.io/devtools-protocol)
and returns the result.
### Command `cdp.getSession`
```cddl
CdpGetSessionCommand = {
method: "cdp.sendCommand",
params: ScriptEvaluateParameters,
}
CdpGetSessionParameters = {
context: BrowsingContext,
}
CdpGetSessionResult = {
session: text,
}
```
The command returns the default CDP session for the selected browsing context.
### Events `cdp`
```cddl
CdpEventReceivedEvent = {
method: "cdp.<CDP Event Name>",
params: CdpEventReceivedParameters,
}
CdpEventReceivedParameters = {
event: text,
params: any,
session: text,
}
```
The event contains a CDP event.
### Field `channel`
Each command can be extended with a `channel`:
```cddl
Command = {
id: js-uint,
channel?: text,
CommandData,
Extensible,
}
```
If provided and non-empty string, the very same `channel` is added to the response:
```cddl
CommandResponse = {
id: js-uint,
channel?: text,
result: ResultData,
Extensible,
}
ErrorResponse = {
id: js-uint / null,
channel?: text,
error: ErrorCode,
message: text,
?stacktrace: text,
Extensible
}
```
When client uses
commands [`session.subscribe`](https://w3c.github.io/webdriver-bidi/#command-session-subscribe)
and [`session.unsubscribe`](https://w3c.github.io/webdriver-bidi/#command-session-unsubscribe)
with `channel`, the subscriptions are handled per channel, and the corresponding
`channel` filed is added to the event message:
```cddl
Event = {
channel?: text,
EventData,
Extensible,
}
```
## Dev Setup
### `npm`
This is a Node.js project, so install dependencies as usual:
```sh
npm install
```
### `cargo`
<!-- TODO(jrandolf): Remove after binaries get published -->
We use [cddlconv](https://github.com/google/cddlconv) to generate our WebDriverBidi types before building.
1. Install [Rust](https://rustup.rs/).
2. Run `cargo install --git https://github.com/google/cddlconv.git cddlconv`
### pre-commit.com integration
Refer to the documentation at [.pre-commit-config.yaml](.pre-commit-config.yaml).
```sh
pre-commit install --hook-type pre-push
```
### Starting WebDriver BiDi Server
This will run the server on port `8080`:
```sh
npm run server
```
Use the `PORT=` environment variable or `--port=` argument to run it on another port:
```sh
PORT=8081 npm run server
npm run server -- --port=8081
```
Use the `DEBUG` environment variable to see debug info:
```sh
DEBUG=* npm run server
```
Use the `DEBUG_DEPTH` (default: `10`) environment variable to see debug deeply nested objects:
```sh
DEBUG_DEPTH=100 DEBUG=* npm run server
```
Use the CLI argument `--headless=false` to run browser in headful mode:
```sh
npm run server -- --headless=false
```
Use the `CHANNEL=...` environment variable or `--channel=...` argument with one of
the following values to run the specific Chrome channel: `stable`,
`beta`, `canary`, `dev`.
The requested Chrome version should be installed.
```sh
CHANNEL=dev npm run server
npm run server -- --channel=dev
```
Use the CLI argument `--verbose` to have CDP events printed to the console. Note: you have to enable debugging output `bidi:mapper:debug:*` as well.
```sh
DEBUG=bidi:mapper:debug:* npm run server -- --verbose
```
or
```sh
DEBUG=* npm run server -- --verbose
```
### Starting on Linux and Mac
TODO: verify it works on Windows.
You can also run the server by using `npm run server`. It will write
output to the file `log.txt`:
```sh
npm run server -- --port=8081 --headless=false
```
### Running with in other project
Sometimes it good to verify that a change will not affect thing downstream for other packages.
There is a useful `puppeteer` label you can add to any PR to run Puppeteer test with your changes.
It will bundle `chromium-bidi` and install it in Puppeteer project then run that package test.
## Running
### Unit tests
Running:
```sh
npm run unit
```
### E2E tests
The E2E tests are written using Python, in order to learn how to eventually do
this in web-platform-tests.
#### Installation
Python 3.10+ and some dependencies are required:
```sh
python -m pip install --user pipenv
pipenv install
```
#### Running
The E2E tests require BiDi server running on the same host. By default, tests
try to connect to the port `8080`. The server can be run from the project root:
```sh
npm run e2e # alias to to e2e-headless
npm run e2e-headful
npm run e2e-headless
```
Use the `PORT` environment variable to connect to another port:
```sh
PORT=8081 npm run e2e
```
#### Updating snapshots
```sh
npm run e2e -- --snapshot-update
```
See https://github.com/tophat/syrupy for more information.
### Local http server
E2E tests use local http
server [`pytest-httpserver`](https://pytest-httpserver.readthedocs.io/), which is run
automatically with the tests. However,
sometimes it is useful to run the http server outside the test
case, for example for manual debugging. This can be done by running:
```sh
pipenv run local_http_server
```
...or directly:
```sh
python tests/tools/local_http_server.py
```
### Examples
Refer to [examples/README.md](examples/README.md).
## WPT (Web Platform Tests)
WPT is added as
a [git submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules). To get run
WPT tests:
### Check out and setup WPT
#### 1. Check out WPT
```sh
git submodule update --init
```
#### 2. Go to the WPT folder
```sh
cd wpt
```
#### 3. Set up virtualenv
Follow the [_System
Setup_](https://web-platform-tests.org/running-tests/from-local-system.html#system-setup)
instructions.
#### 4. Setup `hosts` file
Follow
the [`hosts` File Setup](https://web-platform-tests.org/running-tests/from-local-system.html#hosts-file-setup)
instructions.
##### 4.a On Linux, macOS or other UNIX-like system
```sh
./wpt make-hosts-file | sudo tee -a /etc/hosts
```
##### 4.b On **Windows**
This must be run in a PowerShell session with Administrator privileges:
```sh
python wpt make-hosts-file | Out-File $env:SystemRoot\System32\drivers\etc\hosts -Encoding ascii -Append
```
If you are behind a proxy, you also need to make sure the domains above are excluded
from your proxy lookups.
#### 5. Set `BROWSER_BIN`
Set the `BROWSER_BIN` environment variable to a Chrome, Edge or Chromium binary to launch.
For example, on macOS:
```sh
# Chrome
export BROWSER_BIN="/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary"
export BROWSER_BIN="/Applications/Google Chrome Dev.app/Contents/MacOS/Google Chrome Dev"
export BROWSER_BIN="/Applications/Google Chrome Beta.app/Contents/MacOS/Google Chrome Beta"
export BROWSER_BIN="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
export BROWSER_BIN="/Applications/Chromium.app/Contents/MacOS/Chromium"
# Edge
export BROWSER_BIN="/Applications/Microsoft Edge Canary.app/Contents/MacOS/Microsoft Edge Canary"
export BROWSER_BIN="/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge"
```
### Run WPT tests
#### 1. Make sure you have Chrome Dev installed
https://www.google.com/chrome/dev/
#### 2. Build Chromedriver BiDi
Oneshot:
```sh
npm run build
```
Continuously:
```sh
npm run build --watch
```
#### 3. Run
```sh
npm run wpt -- webdriver/tests/bidi/
```
### Update WPT expectations if needed
```sh
UPDATE_EXPECTATIONS=true npm run wpt -- webdriver/tests/bidi/
```
## How does it work?
The architecture is described in the
[WebDriver BiDi in Chrome Context implementation plan](https://docs.google.com/document/d/1VfQ9tv0wPSnb5TI-MOobjoQ5CXLnJJx9F_PxOMQc8kY)
.
There are 2 main modules:
1. backend WS server in `src`. It runs webSocket server, and for each ws connection
runs an instance of browser with BiDi Mapper.
2. front-end BiDi Mapper in `src/bidiMapper`. Gets BiDi commands from the backend,
and map them to CDP commands.
## Contributing
The BiDi commands are processed in the `src/bidiMapper/commandProcessor.ts`. To add a
new command, add it to `_processCommand`, write and call processor for it.
### Publish new `npm` release
#### Automatic release
We use [release-please](https://github.com/googleapis/release-please) to automate releases. When a release should be done, check for the release PR in our [pull requests](https://github.com/GoogleChromeLabs/chromium-bidi/pulls) and merge it.
#### Manual release
1. Dry-run
```sh
npm publish --dry-run
```
1. Open a PR bumping the chromium-bidi version number in `package.json` for review:
```sh
npm version patch -m 'chore: Release v%s' --no-git-tag-version
```
Instead of `patch`, use `minor` or `major` [as needed](https://semver.org/).
1. After the PR is reviewed, [create a GitHub release](https://github.com/GoogleChromeLabs/chromium-bidi/releases/new) specifying the tag name matching the bumped version.
Our CI then automatically publishes the new release to npm based on the tag name.
#### Roll into Chromium
This section assumes you already have a Chromium set-up locally,
and knowledge on [how to submit changes to the repo](https://chromium.googlesource.com/chromium/src/+/refs/heads/main/docs/contributing.md).
Otherwise submit an issue for a project maintainer.
1. Create a new branch in chromium `src/`.
2. Update the mapper version:
```shell
third_party/bidimapper/pull.sh
third_party/bidimapper/build.sh
```
3. Submit a CL with bug `chromedriver:4226`.
4. [Regenerate WPT expectations or baselines](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/testing/run_web_platform_tests.md#test-expectations-and-baselines):
4.1. Trigger a build and test run:
```shell
third_party/blink/tools/blink_tool.py rebaseline-cl --build="linux-blink-rel" --verbose
```
4.2. Once the test completes on the builder, rerun that command to update the
baselines. Update test expectations if there are any crashes or timeouts.
Commit the changes (if any), and upload the new patch to the CL.
5. Add appropriate reviewers or comment the CL link on the PR.

View File

@@ -0,0 +1,28 @@
/**
* Copyright 2022 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @fileoverview The entry point to the BiDi Mapper namespace.
* Other modules should only access exports defined in this file.
* XXX: Add ESlint rule for this (https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-restricted-paths.md)
*/
export { BidiServer, MapperOptions } from './BidiServer.js';
export type { CdpConnection } from '../cdp/CdpConnection.js';
export type { CdpClient } from '../cdp/CdpClient.js';
export { EventEmitter } from '../utils/EventEmitter.js';
export type { BidiTransport } from './BidiTransport.js';
export { OutgoingMessage } from './OutgoingMessage.js';
export type { BidiCommandParameterParser } from './BidiParser.js';

View File

@@ -0,0 +1,31 @@
"use strict";
/**
* Copyright 2022 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.OutgoingMessage = exports.EventEmitter = exports.BidiServer = void 0;
/**
* @fileoverview The entry point to the BiDi Mapper namespace.
* Other modules should only access exports defined in this file.
* XXX: Add ESlint rule for this (https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-restricted-paths.md)
*/
var BidiServer_js_1 = require("./BidiServer.js");
Object.defineProperty(exports, "BidiServer", { enumerable: true, get: function () { return BidiServer_js_1.BidiServer; } });
var EventEmitter_js_1 = require("../utils/EventEmitter.js");
Object.defineProperty(exports, "EventEmitter", { enumerable: true, get: function () { return EventEmitter_js_1.EventEmitter; } });
var OutgoingMessage_js_1 = require("./OutgoingMessage.js");
Object.defineProperty(exports, "OutgoingMessage", { enumerable: true, get: function () { return OutgoingMessage_js_1.OutgoingMessage; } });
//# sourceMappingURL=BidiMapper.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"BidiMapper.js","sourceRoot":"","sources":["../../../src/bidiMapper/BidiMapper.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;GAeG;;;AAEH;;;;GAIG;AACH,iDAA0D;AAAlD,2GAAA,UAAU,OAAA;AAGlB,4DAAsD;AAA9C,+GAAA,YAAY,OAAA;AAEpB,2DAAqD;AAA7C,qHAAA,eAAe,OAAA"}

View File

@@ -0,0 +1,54 @@
/**
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { BrowsingContext, Cdp, Input, Network, Script, Session, Storage, Permissions } from '../protocol/protocol.js';
import type { BidiCommandParameterParser } from './BidiParser.js';
export declare class BidiNoOpParser implements BidiCommandParameterParser {
parseActivateParams(params: unknown): BrowsingContext.ActivateParameters;
parseCaptureScreenshotParams(params: unknown): BrowsingContext.CaptureScreenshotParameters;
parseCloseParams(params: unknown): BrowsingContext.CloseParameters;
parseCreateParams(params: unknown): BrowsingContext.CreateParameters;
parseGetTreeParams(params: unknown): BrowsingContext.GetTreeParameters;
parseHandleUserPromptParams(params: unknown): BrowsingContext.HandleUserPromptParameters;
parseNavigateParams(params: unknown): BrowsingContext.NavigateParameters;
parsePrintParams(params: unknown): BrowsingContext.PrintParameters;
parseReloadParams(params: unknown): BrowsingContext.ReloadParameters;
parseSetViewportParams(params: unknown): BrowsingContext.SetViewportParameters;
parseTraverseHistoryParams(params: unknown): BrowsingContext.TraverseHistoryParameters;
parseGetSessionParams(params: unknown): Cdp.GetSessionParameters;
parseSendCommandParams(params: unknown): Cdp.SendCommandParameters;
parseAddPreloadScriptParams(params: unknown): Script.AddPreloadScriptParameters;
parseCallFunctionParams(params: unknown): Script.CallFunctionParameters;
parseDisownParams(params: unknown): Script.DisownParameters;
parseEvaluateParams(params: unknown): Script.EvaluateParameters;
parseGetRealmsParams(params: unknown): Script.GetRealmsParameters;
parseRemovePreloadScriptParams(params: unknown): Script.RemovePreloadScriptParameters;
parsePerformActionsParams(params: unknown): Input.PerformActionsParameters;
parseReleaseActionsParams(params: unknown): Input.ReleaseActionsParameters;
parseSetFilesParams(params: unknown): Input.SetFilesParameters;
parseAddInterceptParams(params: unknown): Network.AddInterceptParameters;
parseContinueRequestParams(params: unknown): Network.ContinueRequestParameters;
parseContinueResponseParams(params: unknown): Network.ContinueResponseParameters;
parseContinueWithAuthParams(params: unknown): Network.ContinueWithAuthParameters;
parseFailRequestParams(params: unknown): Network.FailRequestParameters;
parseProvideResponseParams(params: unknown): Network.ProvideResponseParameters;
parseRemoveInterceptParams(params: unknown): Network.RemoveInterceptParameters;
parseSetPermissionsParams(params: unknown): Permissions.SetPermissionParameters;
parseSubscribeParams(params: unknown): Session.SubscriptionRequest;
parseDeleteCookiesParams(params: unknown): Storage.DeleteCookiesParameters;
parseGetCookiesParams(params: unknown): Storage.GetCookiesParameters;
parseSetCookieParams(params: unknown): Storage.SetCookieParameters;
}

View File

@@ -0,0 +1,148 @@
"use strict";
/**
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.BidiNoOpParser = void 0;
class BidiNoOpParser {
// Browsing Context domain
// keep-sorted start block=yes
parseActivateParams(params) {
return params;
}
parseCaptureScreenshotParams(params) {
return params;
}
parseCloseParams(params) {
return params;
}
parseCreateParams(params) {
return params;
}
parseGetTreeParams(params) {
return params;
}
parseHandleUserPromptParams(params) {
return params;
}
parseNavigateParams(params) {
return params;
}
parsePrintParams(params) {
return params;
}
parseReloadParams(params) {
return params;
}
parseSetViewportParams(params) {
return params;
}
parseTraverseHistoryParams(params) {
return params;
}
// keep-sorted end
// CDP domain
// keep-sorted start block=yes
parseGetSessionParams(params) {
return params;
}
parseSendCommandParams(params) {
return params;
}
// keep-sorted end
// Script domain
// keep-sorted start block=yes
parseAddPreloadScriptParams(params) {
return params;
}
parseCallFunctionParams(params) {
return params;
}
parseDisownParams(params) {
return params;
}
parseEvaluateParams(params) {
return params;
}
parseGetRealmsParams(params) {
return params;
}
parseRemovePreloadScriptParams(params) {
return params;
}
// keep-sorted end
// Input domain
// keep-sorted start block=yes
parsePerformActionsParams(params) {
return params;
}
parseReleaseActionsParams(params) {
return params;
}
parseSetFilesParams(params) {
return params;
}
// keep-sorted end
// Network domain
// keep-sorted start block=yes
parseAddInterceptParams(params) {
return params;
}
parseContinueRequestParams(params) {
return params;
}
parseContinueResponseParams(params) {
return params;
}
parseContinueWithAuthParams(params) {
return params;
}
parseFailRequestParams(params) {
return params;
}
parseProvideResponseParams(params) {
return params;
}
parseRemoveInterceptParams(params) {
return params;
}
// keep-sorted end
// Permissions domain
// keep-sorted start block=yes
parseSetPermissionsParams(params) {
return params;
}
// keep-sorted end
// Session domain
// keep-sorted start block=yes
parseSubscribeParams(params) {
return params;
}
// keep-sorted end
// Storage domain
// keep-sorted start block=yes
parseDeleteCookiesParams(params) {
return params;
}
parseGetCookiesParams(params) {
return params;
}
parseSetCookieParams(params) {
return params;
}
}
exports.BidiNoOpParser = BidiNoOpParser;
//# sourceMappingURL=BidiNoOpParser.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"BidiNoOpParser.js","sourceRoot":"","sources":["../../../src/bidiMapper/BidiNoOpParser.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,53 @@
/**
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { BrowsingContext, Cdp, Input, Network, Permissions, Script, Session, Storage } from '../protocol/protocol.js';
export interface BidiCommandParameterParser {
parseActivateParams(params: unknown): BrowsingContext.ActivateParameters;
parseCaptureScreenshotParams(params: unknown): BrowsingContext.CaptureScreenshotParameters;
parseCloseParams(params: unknown): BrowsingContext.CloseParameters;
parseCreateParams(params: unknown): BrowsingContext.CreateParameters;
parseGetTreeParams(params: unknown): BrowsingContext.GetTreeParameters;
parseHandleUserPromptParams(params: unknown): BrowsingContext.HandleUserPromptParameters;
parseNavigateParams(params: unknown): BrowsingContext.NavigateParameters;
parsePrintParams(params: unknown): BrowsingContext.PrintParameters;
parseReloadParams(params: unknown): BrowsingContext.ReloadParameters;
parseSetViewportParams(params: unknown): BrowsingContext.SetViewportParameters;
parseTraverseHistoryParams(params: unknown): BrowsingContext.TraverseHistoryParameters;
parseGetSessionParams(params: unknown): Cdp.GetSessionParameters;
parseSendCommandParams(params: unknown): Cdp.SendCommandParameters;
parsePerformActionsParams(params: unknown): Input.PerformActionsParameters;
parseReleaseActionsParams(params: unknown): Input.ReleaseActionsParameters;
parseSetFilesParams(params: unknown): Input.SetFilesParameters;
parseSetPermissionsParams(params: unknown): Permissions.SetPermissionParameters;
parseAddInterceptParams(params: unknown): Network.AddInterceptParameters;
parseContinueRequestParams(params: unknown): Network.ContinueRequestParameters;
parseContinueResponseParams(params: unknown): Network.ContinueResponseParameters;
parseContinueWithAuthParams(params: unknown): Network.ContinueWithAuthParameters;
parseFailRequestParams(params: unknown): Network.FailRequestParameters;
parseProvideResponseParams(params: unknown): Network.ProvideResponseParameters;
parseRemoveInterceptParams(params: unknown): Network.RemoveInterceptParameters;
parseAddPreloadScriptParams(params: unknown): Script.AddPreloadScriptParameters;
parseCallFunctionParams(params: unknown): Script.CallFunctionParameters;
parseDisownParams(params: unknown): Script.DisownParameters;
parseEvaluateParams(params: unknown): Script.EvaluateParameters;
parseGetRealmsParams(params: unknown): Script.GetRealmsParameters;
parseRemovePreloadScriptParams(params: unknown): Script.RemovePreloadScriptParameters;
parseSubscribeParams(params: unknown): Session.SubscriptionRequest;
parseDeleteCookiesParams(params: unknown): Storage.DeleteCookiesParameters;
parseGetCookiesParams(params: unknown): Storage.GetCookiesParameters;
parseSetCookieParams(params: unknown): Storage.SetCookieParameters;
}

View File

@@ -0,0 +1,19 @@
"use strict";
/**
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=BidiParser.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"BidiParser.js","sourceRoot":"","sources":["../../../src/bidiMapper/BidiParser.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;GAeG"}

View File

@@ -0,0 +1,46 @@
/**
* Copyright 2021 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { CdpClient } from '../cdp/CdpClient';
import type { CdpConnection } from '../cdp/CdpConnection.js';
import type { ChromiumBidi } from '../protocol/protocol.js';
import { EventEmitter } from '../utils/EventEmitter.js';
import { type LoggerFn } from '../utils/log.js';
import type { Result } from '../utils/result.js';
import type { BidiCommandParameterParser } from './BidiParser.js';
import type { BidiTransport } from './BidiTransport.js';
import type { OutgoingMessage } from './OutgoingMessage.js';
type BidiServerEvent = {
message: ChromiumBidi.Command;
};
export type MapperOptions = {
acceptInsecureCerts: boolean;
sharedIdWithFrame?: boolean;
};
export declare class BidiServer extends EventEmitter<BidiServerEvent> {
#private;
private constructor();
/**
* Creates and starts BiDi Mapper instance.
*/
static createAndStart(bidiTransport: BidiTransport, cdpConnection: CdpConnection, browserCdpClient: CdpClient, selfTargetId: string, options?: MapperOptions, parser?: BidiCommandParameterParser, logger?: LoggerFn): Promise<BidiServer>;
/**
* Sends BiDi message.
*/
emitOutgoingMessage(messageEntry: Promise<Result<OutgoingMessage>>, event: string): void;
close(): void;
}
export {};

View File

@@ -0,0 +1,111 @@
"use strict";
/**
* Copyright 2021 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.BidiServer = void 0;
const EventEmitter_js_1 = require("../utils/EventEmitter.js");
const log_js_1 = require("../utils/log.js");
const ProcessingQueue_js_1 = require("../utils/ProcessingQueue.js");
const CommandProcessor_js_1 = require("./CommandProcessor.js");
const BrowsingContextStorage_js_1 = require("./domains/context/BrowsingContextStorage.js");
const RealmStorage_js_1 = require("./domains/script/RealmStorage.js");
const EventManager_js_1 = require("./domains/session/EventManager.js");
class BidiServer extends EventEmitter_js_1.EventEmitter {
#messageQueue;
#transport;
#commandProcessor;
#eventManager;
#browsingContextStorage = new BrowsingContextStorage_js_1.BrowsingContextStorage();
#logger;
#handleIncomingMessage = (message) => {
void this.#commandProcessor.processCommand(message).catch((error) => {
this.#logger?.(log_js_1.LogType.debugError, error);
});
};
#processOutgoingMessage = async (messageEntry) => {
const message = messageEntry.message;
if (messageEntry.channel !== null) {
message['channel'] = messageEntry.channel;
}
await this.#transport.sendMessage(message);
};
constructor(bidiTransport, cdpConnection, browserCdpClient, selfTargetId, defaultUserContextId, options, parser, logger) {
super();
this.#logger = logger;
this.#messageQueue = new ProcessingQueue_js_1.ProcessingQueue(this.#processOutgoingMessage, this.#logger);
this.#transport = bidiTransport;
this.#transport.setOnMessage(this.#handleIncomingMessage);
this.#eventManager = new EventManager_js_1.EventManager(this.#browsingContextStorage);
this.#commandProcessor = new CommandProcessor_js_1.CommandProcessor(cdpConnection, browserCdpClient, this.#eventManager, selfTargetId, defaultUserContextId, this.#browsingContextStorage, new RealmStorage_js_1.RealmStorage(), options?.acceptInsecureCerts ?? false, options?.sharedIdWithFrame ?? false, parser, this.#logger);
this.#eventManager.on("event" /* EventManagerEvents.Event */, ({ message, event }) => {
this.emitOutgoingMessage(message, event);
});
this.#commandProcessor.on("response" /* CommandProcessorEvents.Response */, ({ message, event }) => {
this.emitOutgoingMessage(message, event);
});
}
/**
* Creates and starts BiDi Mapper instance.
*/
static async createAndStart(bidiTransport, cdpConnection, browserCdpClient, selfTargetId, options, parser, logger) {
// The default context is not exposed in Target.getBrowserContexts but can
// be observed via Target.getTargets. To determine the default browser
// context, we check which one is mentioned in Target.getTargets and not in
// Target.getBrowserContexts.
const [{ browserContextIds }, { targetInfos }] = await Promise.all([
browserCdpClient.sendCommand('Target.getBrowserContexts'),
browserCdpClient.sendCommand('Target.getTargets'),
]);
let defaultUserContextId = 'default';
for (const info of targetInfos) {
if (info.browserContextId &&
!browserContextIds.includes(info.browserContextId)) {
defaultUserContextId = info.browserContextId;
break;
}
}
const server = new BidiServer(bidiTransport, cdpConnection, browserCdpClient, selfTargetId, defaultUserContextId, options, parser, logger);
// Needed to get events about new targets.
await browserCdpClient.sendCommand('Target.setDiscoverTargets', {
discover: true,
});
// Needed to automatically attach to new targets.
await browserCdpClient.sendCommand('Target.setAutoAttach', {
autoAttach: true,
waitForDebuggerOnStart: true,
flatten: true,
});
await server.#topLevelContextsLoaded();
return server;
}
/**
* Sends BiDi message.
*/
emitOutgoingMessage(messageEntry, event) {
this.#messageQueue.add(messageEntry, event);
}
close() {
this.#transport.close();
}
async #topLevelContextsLoaded() {
await Promise.all(this.#browsingContextStorage
.getTopLevelContexts()
.map((c) => c.lifecycleLoaded()));
}
}
exports.BidiServer = BidiServer;
//# sourceMappingURL=BidiServer.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"BidiServer.js","sourceRoot":"","sources":["../../../src/bidiMapper/BidiServer.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,22 @@
/**
* Copyright 2022 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { ChromiumBidi } from '../protocol/protocol.js';
export interface BidiTransport {
setOnMessage: (handler: (message: ChromiumBidi.Command) => Promise<void> | void) => void;
sendMessage: (message: ChromiumBidi.Message) => Promise<void> | void;
close(): void;
}

View File

@@ -0,0 +1,19 @@
"use strict";
/**
* Copyright 2022 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=BidiTransport.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"BidiTransport.js","sourceRoot":"","sources":["../../../src/bidiMapper/BidiTransport.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;GAeG"}

View File

@@ -0,0 +1,42 @@
/**
* Copyright 2021 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { CdpClient } from '../cdp/CdpClient';
import type { CdpConnection } from '../cdp/CdpConnection.js';
import { type ChromiumBidi, type Browser } from '../protocol/protocol.js';
import { EventEmitter } from '../utils/EventEmitter.js';
import { type LoggerFn } from '../utils/log.js';
import type { Result } from '../utils/result.js';
import type { BidiCommandParameterParser } from './BidiParser.js';
import type { BrowsingContextStorage } from './domains/context/BrowsingContextStorage.js';
import type { RealmStorage } from './domains/script/RealmStorage.js';
import type { EventManager } from './domains/session/EventManager.js';
import { OutgoingMessage } from './OutgoingMessage.js';
export declare const enum CommandProcessorEvents {
Response = "response"
}
type CommandProcessorEventsMap = {
[CommandProcessorEvents.Response]: {
message: Promise<Result<OutgoingMessage>>;
event: string;
};
};
export declare class CommandProcessor extends EventEmitter<CommandProcessorEventsMap> {
#private;
constructor(cdpConnection: CdpConnection, browserCdpClient: CdpClient, eventManager: EventManager, selfTargetId: string, defaultUserContextId: Browser.UserContext, browsingContextStorage: BrowsingContextStorage, realmStorage: RealmStorage, acceptInsecureCerts: boolean, sharedIdWithFrame: boolean, parser?: BidiCommandParameterParser, logger?: LoggerFn);
processCommand(command: ChromiumBidi.Command): Promise<void>;
}
export {};

View File

@@ -0,0 +1,221 @@
"use strict";
/**
* Copyright 2021 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.CommandProcessor = void 0;
const protocol_js_1 = require("../protocol/protocol.js");
const EventEmitter_js_1 = require("../utils/EventEmitter.js");
const log_js_1 = require("../utils/log.js");
const BidiNoOpParser_js_1 = require("./BidiNoOpParser.js");
const BrowserProcessor_js_1 = require("./domains/browser/BrowserProcessor.js");
const CdpProcessor_js_1 = require("./domains/cdp/CdpProcessor.js");
const BrowsingContextProcessor_js_1 = require("./domains/context/BrowsingContextProcessor.js");
const InputProcessor_js_1 = require("./domains/input/InputProcessor.js");
const NetworkProcessor_js_1 = require("./domains/network/NetworkProcessor.js");
const NetworkStorage_js_1 = require("./domains/network/NetworkStorage.js");
const PermissionsProcessor_js_1 = require("./domains/permissions/PermissionsProcessor.js");
const PreloadScriptStorage_js_1 = require("./domains/script/PreloadScriptStorage.js");
const ScriptProcessor_js_1 = require("./domains/script/ScriptProcessor.js");
const SessionProcessor_js_1 = require("./domains/session/SessionProcessor.js");
const StorageProcessor_js_1 = require("./domains/storage/StorageProcessor.js");
const OutgoingMessage_js_1 = require("./OutgoingMessage.js");
class CommandProcessor extends EventEmitter_js_1.EventEmitter {
// keep-sorted start
#browserProcessor;
#browsingContextProcessor;
#cdpProcessor;
#inputProcessor;
#networkProcessor;
#permissionsProcessor;
#scriptProcessor;
#sessionProcessor;
#storageProcessor;
// keep-sorted end
#parser;
#logger;
constructor(cdpConnection, browserCdpClient, eventManager, selfTargetId, defaultUserContextId, browsingContextStorage, realmStorage, acceptInsecureCerts, sharedIdWithFrame, parser = new BidiNoOpParser_js_1.BidiNoOpParser(), logger) {
super();
this.#parser = parser;
this.#logger = logger;
const networkStorage = new NetworkStorage_js_1.NetworkStorage();
const preloadScriptStorage = new PreloadScriptStorage_js_1.PreloadScriptStorage();
// keep-sorted start block=yes
this.#browserProcessor = new BrowserProcessor_js_1.BrowserProcessor(browserCdpClient);
this.#browsingContextProcessor = new BrowsingContextProcessor_js_1.BrowsingContextProcessor(cdpConnection, browserCdpClient, selfTargetId, eventManager, browsingContextStorage, realmStorage, networkStorage, preloadScriptStorage, acceptInsecureCerts, sharedIdWithFrame, defaultUserContextId, logger);
this.#cdpProcessor = new CdpProcessor_js_1.CdpProcessor(browsingContextStorage, cdpConnection, browserCdpClient);
this.#inputProcessor = new InputProcessor_js_1.InputProcessor(browsingContextStorage, realmStorage);
this.#networkProcessor = new NetworkProcessor_js_1.NetworkProcessor(browsingContextStorage, networkStorage);
this.#permissionsProcessor = new PermissionsProcessor_js_1.PermissionsProcessor(browserCdpClient);
this.#scriptProcessor = new ScriptProcessor_js_1.ScriptProcessor(browsingContextStorage, realmStorage, preloadScriptStorage, logger);
this.#sessionProcessor = new SessionProcessor_js_1.SessionProcessor(eventManager);
this.#storageProcessor = new StorageProcessor_js_1.StorageProcessor(browserCdpClient, browsingContextStorage, logger);
// keep-sorted end
}
async #processCommand(command) {
switch (command.method) {
case 'session.end':
case 'session.new':
// TODO: Implement.
break;
// Browser domain
// keep-sorted start block=yes
case 'browser.close':
return this.#browserProcessor.close();
case 'browser.createUserContext':
return await this.#browserProcessor.createUserContext();
case 'browser.getUserContexts':
return await this.#browserProcessor.getUserContexts();
case 'browser.removeUserContext':
return await this.#browserProcessor.removeUserContext(command.params.userContext);
// keep-sorted end
// Browsing Context domain
// keep-sorted start block=yes
case 'browsingContext.activate':
return await this.#browsingContextProcessor.activate(this.#parser.parseActivateParams(command.params));
case 'browsingContext.captureScreenshot':
return await this.#browsingContextProcessor.captureScreenshot(this.#parser.parseCaptureScreenshotParams(command.params));
case 'browsingContext.close':
return await this.#browsingContextProcessor.close(this.#parser.parseCloseParams(command.params));
case 'browsingContext.create':
return await this.#browsingContextProcessor.create(this.#parser.parseCreateParams(command.params));
case 'browsingContext.getTree':
return this.#browsingContextProcessor.getTree(this.#parser.parseGetTreeParams(command.params));
case 'browsingContext.handleUserPrompt':
return await this.#browsingContextProcessor.handleUserPrompt(this.#parser.parseHandleUserPromptParams(command.params));
case 'browsingContext.locateNodes':
throw new protocol_js_1.UnsupportedOperationException(`Command '${command.method}' not yet implemented.`);
case 'browsingContext.navigate':
return await this.#browsingContextProcessor.navigate(this.#parser.parseNavigateParams(command.params));
case 'browsingContext.print':
return await this.#browsingContextProcessor.print(this.#parser.parsePrintParams(command.params));
case 'browsingContext.reload':
return await this.#browsingContextProcessor.reload(this.#parser.parseReloadParams(command.params));
case 'browsingContext.setViewport':
return await this.#browsingContextProcessor.setViewport(this.#parser.parseSetViewportParams(command.params));
case 'browsingContext.traverseHistory':
return await this.#browsingContextProcessor.traverseHistory(this.#parser.parseTraverseHistoryParams(command.params));
// keep-sorted end
// CDP domain
// keep-sorted start block=yes
case 'cdp.getSession':
return this.#cdpProcessor.getSession(this.#parser.parseGetSessionParams(command.params));
case 'cdp.sendCommand':
return await this.#cdpProcessor.sendCommand(this.#parser.parseSendCommandParams(command.params));
// keep-sorted end
// Input domain
// keep-sorted start block=yes
case 'input.performActions':
return await this.#inputProcessor.performActions(this.#parser.parsePerformActionsParams(command.params));
case 'input.releaseActions':
return await this.#inputProcessor.releaseActions(this.#parser.parseReleaseActionsParams(command.params));
case 'input.setFiles':
return await this.#inputProcessor.setFiles(this.#parser.parseSetFilesParams(command.params));
// keep-sorted end
// Network domain
// keep-sorted start block=yes
case 'network.addIntercept':
return await this.#networkProcessor.addIntercept(this.#parser.parseAddInterceptParams(command.params));
case 'network.continueRequest':
return await this.#networkProcessor.continueRequest(this.#parser.parseContinueRequestParams(command.params));
case 'network.continueResponse':
return await this.#networkProcessor.continueResponse(this.#parser.parseContinueResponseParams(command.params));
case 'network.continueWithAuth':
return await this.#networkProcessor.continueWithAuth(this.#parser.parseContinueWithAuthParams(command.params));
case 'network.failRequest':
return await this.#networkProcessor.failRequest(this.#parser.parseFailRequestParams(command.params));
case 'network.provideResponse':
return await this.#networkProcessor.provideResponse(this.#parser.parseProvideResponseParams(command.params));
case 'network.removeIntercept':
return await this.#networkProcessor.removeIntercept(this.#parser.parseRemoveInterceptParams(command.params));
// keep-sorted end
// Permissions domain
// keep-sorted start block=yes
case 'permissions.setPermission':
return await this.#permissionsProcessor.setPermissions(this.#parser.parseSetPermissionsParams(command.params));
// keep-sorted end
// Script domain
// keep-sorted start block=yes
case 'script.addPreloadScript':
return await this.#scriptProcessor.addPreloadScript(this.#parser.parseAddPreloadScriptParams(command.params));
case 'script.callFunction':
return await this.#scriptProcessor.callFunction(this.#parser.parseCallFunctionParams(command.params));
case 'script.disown':
return await this.#scriptProcessor.disown(this.#parser.parseDisownParams(command.params));
case 'script.evaluate':
return await this.#scriptProcessor.evaluate(this.#parser.parseEvaluateParams(command.params));
case 'script.getRealms':
return this.#scriptProcessor.getRealms(this.#parser.parseGetRealmsParams(command.params));
case 'script.removePreloadScript':
return await this.#scriptProcessor.removePreloadScript(this.#parser.parseRemovePreloadScriptParams(command.params));
// keep-sorted end
// Session domain
// keep-sorted start block=yes
case 'session.status':
return this.#sessionProcessor.status();
case 'session.subscribe':
return this.#sessionProcessor.subscribe(this.#parser.parseSubscribeParams(command.params), command.channel);
case 'session.unsubscribe':
return this.#sessionProcessor.unsubscribe(this.#parser.parseSubscribeParams(command.params), command.channel);
// keep-sorted end
// Storage domain
// keep-sorted start block=yes
case 'storage.deleteCookies':
throw new protocol_js_1.UnsupportedOperationException(`${command.method} is not supported yet`);
case 'storage.getCookies':
return await this.#storageProcessor.getCookies(this.#parser.parseGetCookiesParams(command.params));
case 'storage.setCookie':
return await this.#storageProcessor.setCookie(this.#parser.parseSetCookieParams(command.params));
// keep-sorted end
}
// Intentionally kept outside the switch statement to ensure that
// ESLint @typescript-eslint/switch-exhaustiveness-check triggers if a new
// command is added.
throw new protocol_js_1.UnknownCommandException(`Unknown command '${command.method}'.`);
}
async processCommand(command) {
try {
const result = await this.#processCommand(command);
const response = {
type: 'success',
id: command.id,
result,
};
this.emit("response" /* CommandProcessorEvents.Response */, {
message: OutgoingMessage_js_1.OutgoingMessage.createResolved(response, command.channel),
event: command.method,
});
}
catch (e) {
if (e instanceof protocol_js_1.Exception) {
this.emit("response" /* CommandProcessorEvents.Response */, {
message: OutgoingMessage_js_1.OutgoingMessage.createResolved(e.toErrorResponse(command.id), command.channel),
event: command.method,
});
}
else {
const error = e;
this.#logger?.(log_js_1.LogType.bidi, error);
this.emit("response" /* CommandProcessorEvents.Response */, {
message: OutgoingMessage_js_1.OutgoingMessage.createResolved(new protocol_js_1.UnknownErrorException(error.message, error.stack).toErrorResponse(command.id), command.channel),
event: command.method,
});
}
}
}
}
exports.CommandProcessor = CommandProcessor;
//# sourceMappingURL=CommandProcessor.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,27 @@
/**
* Copyright 2021 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { BidiPlusChannel } from '../protocol/chromium-bidi.js';
import type { ChromiumBidi } from '../protocol/protocol.js';
import type { Result } from '../utils/result.js';
export declare class OutgoingMessage {
#private;
private constructor();
static createFromPromise(messagePromise: Promise<Result<ChromiumBidi.Message>>, channel: BidiPlusChannel): Promise<Result<OutgoingMessage>>;
static createResolved(message: ChromiumBidi.Message, channel?: BidiPlusChannel): Promise<Result<OutgoingMessage>>;
get message(): ChromiumBidi.Message;
get channel(): BidiPlusChannel;
}

View File

@@ -0,0 +1,52 @@
"use strict";
/**
* Copyright 2021 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.OutgoingMessage = void 0;
class OutgoingMessage {
#message;
#channel;
constructor(message, channel = null) {
this.#message = message;
this.#channel = channel;
}
static createFromPromise(messagePromise, channel) {
return messagePromise.then((message) => {
if (message.kind === 'success') {
return {
kind: 'success',
value: new OutgoingMessage(message.value, channel),
};
}
return message;
});
}
static createResolved(message, channel) {
return Promise.resolve({
kind: 'success',
value: new OutgoingMessage(message, channel),
});
}
get message() {
return this.#message;
}
get channel() {
return this.#channel;
}
}
exports.OutgoingMessage = OutgoingMessage;
//# sourceMappingURL=OutgoingMessage.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"OutgoingMessage.js","sourceRoot":"","sources":["../../../src/bidiMapper/OutgoingMessage.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;GAeG;;;AAMH,MAAa,eAAe;IACjB,QAAQ,CAAuB;IAC/B,QAAQ,CAAkB;IAEnC,YACE,OAA6B,EAC7B,UAA2B,IAAI;QAE/B,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;QACxB,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;IAC1B,CAAC;IAED,MAAM,CAAC,iBAAiB,CACtB,cAAqD,EACrD,OAAwB;QAExB,OAAO,cAAc,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE;YACrC,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;gBAC/B,OAAO;oBACL,IAAI,EAAE,SAAS;oBACf,KAAK,EAAE,IAAI,eAAe,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC;iBACnD,CAAC;YACJ,CAAC;YACD,OAAO,OAAO,CAAC;QACjB,CAAC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,CAAC,cAAc,CACnB,OAA6B,EAC7B,OAAyB;QAEzB,OAAO,OAAO,CAAC,OAAO,CAAC;YACrB,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,IAAI,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC;SAC7C,CAAC,CAAC;IACL,CAAC;IAED,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;CACF;AA5CD,0CA4CC"}

View File

@@ -0,0 +1,26 @@
/**
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type EmptyResult, type Browser } from '../../../protocol/protocol.js';
import type { CdpClient } from '../../BidiMapper.js';
export declare class BrowserProcessor {
#private;
constructor(browserCdpClient: CdpClient);
close(): EmptyResult;
createUserContext(): Promise<Browser.CreateUserContextResult>;
removeUserContext(userContext: Browser.UserContext): Promise<EmptyResult>;
getUserContexts(): Promise<Browser.GetUserContextsResult>;
}

View File

@@ -0,0 +1,73 @@
"use strict";
/**
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.BrowserProcessor = void 0;
const protocol_js_1 = require("../../../protocol/protocol.js");
class BrowserProcessor {
#browserCdpClient;
constructor(browserCdpClient) {
this.#browserCdpClient = browserCdpClient;
}
close() {
// Ensure that it is put at the end of the event loop.
// This way we send back the response before closing the tab.
setTimeout(() => this.#browserCdpClient.sendCommand('Browser.close'), 0);
return {};
}
async createUserContext() {
const context = await this.#browserCdpClient.sendCommand('Target.createBrowserContext');
return {
userContext: context.browserContextId,
};
}
async removeUserContext(userContext) {
if (userContext === 'default') {
throw new protocol_js_1.InvalidArgumentException('`default` user context cannot be removed');
}
try {
await this.#browserCdpClient.sendCommand('Target.disposeBrowserContext', {
browserContextId: userContext,
});
}
catch (err) {
// https://source.chromium.org/chromium/chromium/src/+/main:content/browser/devtools/protocol/target_handler.cc;l=1424;drc=c686e8f4fd379312469fe018f5c390e9c8f20d0d
if (err.message.startsWith('Failed to find context with id')) {
throw new protocol_js_1.NoSuchUserContextException(err.message);
}
throw err;
}
return {};
}
async getUserContexts() {
const result = await this.#browserCdpClient.sendCommand('Target.getBrowserContexts');
return {
userContexts: [
{
userContext: 'default',
},
...result.browserContextIds.map((id) => {
return {
userContext: id,
};
}),
],
};
}
}
exports.BrowserProcessor = BrowserProcessor;
//# sourceMappingURL=BrowserProcessor.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"BrowserProcessor.js","sourceRoot":"","sources":["../../../../../src/bidiMapper/domains/browser/BrowserProcessor.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;GAeG;;;AAEH,+DAKuC;AAGvC,MAAa,gBAAgB;IAClB,iBAAiB,CAAY;IAEtC,YAAY,gBAA2B;QACrC,IAAI,CAAC,iBAAiB,GAAG,gBAAgB,CAAC;IAC5C,CAAC;IAED,KAAK;QACH,sDAAsD;QACtD,6DAA6D;QAC7D,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,WAAW,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC,CAAC;QAEzE,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,KAAK,CAAC,iBAAiB;QACrB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,WAAW,CACtD,6BAA6B,CAC9B,CAAC;QACF,OAAO;YACL,WAAW,EAAE,OAAO,CAAC,gBAAgB;SACtC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,iBAAiB,CACrB,WAAgC;QAEhC,IAAI,WAAW,KAAK,SAAS,EAAE,CAAC;YAC9B,MAAM,IAAI,sCAAwB,CAChC,0CAA0C,CAC3C,CAAC;QACJ,CAAC;QACD,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,iBAAiB,CAAC,WAAW,CAAC,8BAA8B,EAAE;gBACvE,gBAAgB,EAAE,WAAW;aAC9B,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,mKAAmK;YACnK,IAAK,GAAa,CAAC,OAAO,CAAC,UAAU,CAAC,gCAAgC,CAAC,EAAE,CAAC;gBACxE,MAAM,IAAI,wCAA0B,CAAE,GAAa,CAAC,OAAO,CAAC,CAAC;YAC/D,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,KAAK,CAAC,eAAe;QACnB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,WAAW,CACrD,2BAA2B,CAC5B,CAAC;QACF,OAAO;YACL,YAAY,EAAE;gBACZ;oBACE,WAAW,EAAE,SAAS;iBACvB;gBACD,GAAG,MAAM,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE;oBACrC,OAAO;wBACL,WAAW,EAAE,EAAE;qBAChB,CAAC;gBACJ,CAAC,CAAC;aACH;SACF,CAAC;IACJ,CAAC;CACF;AA/DD,4CA+DC"}

View File

@@ -0,0 +1,25 @@
/**
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Cdp } from '../../../protocol/protocol.js';
import type { CdpClient, CdpConnection } from '../../BidiMapper.js';
import type { BrowsingContextStorage } from '../context/BrowsingContextStorage.js';
export declare class CdpProcessor {
#private;
constructor(browsingContextStorage: BrowsingContextStorage, cdpConnection: CdpConnection, browserCdpClient: CdpClient);
getSession(params: Cdp.GetSessionParameters): Cdp.GetSessionResult;
sendCommand(params: Cdp.SendCommandParameters): Promise<Cdp.SendCommandResult>;
}

View File

@@ -0,0 +1,49 @@
"use strict";
/**
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.CdpProcessor = void 0;
class CdpProcessor {
#browsingContextStorage;
#cdpConnection;
#browserCdpClient;
constructor(browsingContextStorage, cdpConnection, browserCdpClient) {
this.#browsingContextStorage = browsingContextStorage;
this.#cdpConnection = cdpConnection;
this.#browserCdpClient = browserCdpClient;
}
getSession(params) {
const context = params.context;
const sessionId = this.#browsingContextStorage.getContext(context).cdpTarget.cdpSessionId;
if (sessionId === undefined) {
return {};
}
return { session: sessionId };
}
async sendCommand(params) {
const client = params.session
? this.#cdpConnection.getCdpClient(params.session)
: this.#browserCdpClient;
const result = await client.sendCommand(params.method, params.params);
return {
result,
session: params.session,
};
}
}
exports.CdpProcessor = CdpProcessor;
//# sourceMappingURL=CdpProcessor.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"CdpProcessor.js","sourceRoot":"","sources":["../../../../../src/bidiMapper/domains/cdp/CdpProcessor.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;GAeG;;;AAMH,MAAa,YAAY;IACd,uBAAuB,CAAyB;IAChD,cAAc,CAAgB;IAC9B,iBAAiB,CAAY;IAEtC,YACE,sBAA8C,EAC9C,aAA4B,EAC5B,gBAA2B;QAE3B,IAAI,CAAC,uBAAuB,GAAG,sBAAsB,CAAC;QACtD,IAAI,CAAC,cAAc,GAAG,aAAa,CAAC;QACpC,IAAI,CAAC,iBAAiB,GAAG,gBAAgB,CAAC;IAC5C,CAAC;IAED,UAAU,CAAC,MAAgC;QACzC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC;QAC/B,MAAM,SAAS,GACb,IAAI,CAAC,uBAAuB,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC;QAC1E,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,OAAO,EAAC,OAAO,EAAE,SAAS,EAAC,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,WAAW,CACf,MAAiC;QAEjC,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO;YAC3B,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC;YAClD,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC;QAC3B,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;QACtE,OAAO;YACL,MAAM;YACN,OAAO,EAAE,MAAM,CAAC,OAAO;SACxB,CAAC;IACJ,CAAC;CACF;AArCD,oCAqCC"}

View File

@@ -0,0 +1,72 @@
/**
* Copyright 2022 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Protocol } from 'devtools-protocol';
import { BrowsingContext } from '../../../protocol/protocol.js';
import { type LoggerFn } from '../../../utils/log.js';
import type { Realm } from '../script/Realm.js';
import type { RealmStorage } from '../script/RealmStorage.js';
import type { EventManager } from '../session/EventManager.js';
import type { BrowsingContextStorage } from './BrowsingContextStorage.js';
import type { CdpTarget } from './CdpTarget.js';
export declare class BrowsingContextImpl {
#private;
static readonly LOGGER_PREFIX: "debug:browsingContext";
readonly userContext: string;
private constructor();
static create(cdpTarget: CdpTarget, realmStorage: RealmStorage, id: BrowsingContext.BrowsingContext, parentId: BrowsingContext.BrowsingContext | null, userContext: string, eventManager: EventManager, browsingContextStorage: BrowsingContextStorage, sharedIdWithFrame: boolean, logger?: LoggerFn): BrowsingContextImpl;
static getTimestamp(): number;
/**
* @see https://html.spec.whatwg.org/multipage/document-sequences.html#navigable
*/
get navigableId(): string | undefined;
dispose(): void;
/** Returns the ID of this context. */
get id(): BrowsingContext.BrowsingContext;
/** Returns the parent context ID. */
get parentId(): BrowsingContext.BrowsingContext | null;
/** Returns the parent context. */
get parent(): BrowsingContextImpl | null;
/** Returns all direct children contexts. */
get directChildren(): BrowsingContextImpl[];
/** Returns all children contexts, flattened. */
get allChildren(): BrowsingContextImpl[];
/**
* Returns true if this is a top-level context.
* This is the case whenever the parent context ID is null.
*/
isTopLevelContext(): boolean;
get top(): BrowsingContextImpl;
addChild(childId: BrowsingContext.BrowsingContext): void;
get cdpTarget(): CdpTarget;
updateCdpTarget(cdpTarget: CdpTarget): void;
get url(): string;
lifecycleLoaded(): Promise<void>;
targetUnblockedOrThrow(): Promise<void>;
getOrCreateSandbox(sandbox: string | undefined): Promise<Realm>;
serializeToBidiValue(maxDepth?: number, addParentField?: boolean): BrowsingContext.Info;
onTargetInfoChanged(params: Protocol.Target.TargetInfoChangedEvent): void;
navigate(url: string, wait: BrowsingContext.ReadinessState): Promise<BrowsingContext.NavigateResult>;
reload(ignoreCache: boolean, wait: BrowsingContext.ReadinessState): Promise<BrowsingContext.NavigateResult>;
setViewport(viewport?: BrowsingContext.Viewport | null, devicePixelRatio?: number | null): Promise<void>;
handleUserPrompt(params: BrowsingContext.HandleUserPromptParameters): Promise<void>;
activate(): Promise<void>;
captureScreenshot(params: BrowsingContext.CaptureScreenshotParameters): Promise<BrowsingContext.CaptureScreenshotResult>;
print(params: BrowsingContext.PrintParameters): Promise<BrowsingContext.PrintResult>;
close(): Promise<void>;
traverseHistory(delta: number): Promise<void>;
}
export declare function serializeOrigin(origin: string): string;

View File

@@ -0,0 +1,817 @@
"use strict";
/**
* Copyright 2022 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.serializeOrigin = exports.BrowsingContextImpl = void 0;
const protocol_js_1 = require("../../../protocol/protocol.js");
const assert_js_1 = require("../../../utils/assert.js");
const Deferred_js_1 = require("../../../utils/Deferred.js");
const log_js_1 = require("../../../utils/log.js");
const unitConversions_js_1 = require("../../../utils/unitConversions.js");
const WindowRealm_js_1 = require("../script/WindowRealm.js");
class BrowsingContextImpl {
static LOGGER_PREFIX = `${log_js_1.LogType.debug}:browsingContext`;
/** The ID of this browsing context. */
#id;
userContext;
/**
* The ID of the parent browsing context.
* If null, this is a top-level context.
*/
#parentId;
/** Direct children browsing contexts. */
#children = new Set();
#browsingContextStorage;
#lifecycle = {
DOMContentLoaded: new Deferred_js_1.Deferred(),
load: new Deferred_js_1.Deferred(),
};
#navigation = {
withinDocument: new Deferred_js_1.Deferred(),
};
#url = 'about:blank';
#eventManager;
#realmStorage;
#loaderId;
#cdpTarget;
#maybeDefaultRealm;
#sharedIdWithFrame;
#logger;
constructor(cdpTarget, realmStorage, id, parentId, userContext, eventManager, browsingContextStorage, sharedIdWithFrame, logger) {
this.#cdpTarget = cdpTarget;
this.#realmStorage = realmStorage;
this.#id = id;
this.#parentId = parentId;
this.userContext = userContext;
this.#eventManager = eventManager;
this.#browsingContextStorage = browsingContextStorage;
this.#sharedIdWithFrame = sharedIdWithFrame;
this.#logger = logger;
}
static create(cdpTarget, realmStorage, id, parentId, userContext, eventManager, browsingContextStorage, sharedIdWithFrame, logger) {
const context = new BrowsingContextImpl(cdpTarget, realmStorage, id, parentId, userContext, eventManager, browsingContextStorage, sharedIdWithFrame, logger);
context.#initListeners();
browsingContextStorage.addContext(context);
if (!context.isTopLevelContext()) {
context.parent.addChild(context.id);
}
eventManager.registerEvent({
type: 'event',
method: protocol_js_1.ChromiumBidi.BrowsingContext.EventNames.ContextCreated,
params: context.serializeToBidiValue(),
}, context.id);
return context;
}
static getTimestamp() {
// `timestamp` from the event is MonotonicTime, not real time, so
// the best Mapper can do is to set the timestamp to the epoch time
// of the event arrived.
// https://chromedevtools.github.io/devtools-protocol/tot/Network/#type-MonotonicTime
return new Date().getTime();
}
/**
* @see https://html.spec.whatwg.org/multipage/document-sequences.html#navigable
*/
get navigableId() {
return this.#loaderId;
}
dispose() {
this.#deleteAllChildren();
this.#realmStorage.deleteRealms({
browsingContextId: this.id,
});
// Remove context from the parent.
if (!this.isTopLevelContext()) {
this.parent.#children.delete(this.id);
}
// Fail all ongoing navigations.
this.#failLifecycleIfNotFinished();
this.#eventManager.registerEvent({
type: 'event',
method: protocol_js_1.ChromiumBidi.BrowsingContext.EventNames.ContextDestroyed,
params: this.serializeToBidiValue(),
}, this.id);
this.#browsingContextStorage.deleteContextById(this.id);
}
/** Returns the ID of this context. */
get id() {
return this.#id;
}
/** Returns the parent context ID. */
get parentId() {
return this.#parentId;
}
/** Returns the parent context. */
get parent() {
if (this.parentId === null) {
return null;
}
return this.#browsingContextStorage.getContext(this.parentId);
}
/** Returns all direct children contexts. */
get directChildren() {
return [...this.#children].map((id) => this.#browsingContextStorage.getContext(id));
}
/** Returns all children contexts, flattened. */
get allChildren() {
const children = this.directChildren;
return children.concat(...children.map((child) => child.allChildren));
}
/**
* Returns true if this is a top-level context.
* This is the case whenever the parent context ID is null.
*/
isTopLevelContext() {
return this.#parentId === null;
}
get top() {
// eslint-disable-next-line @typescript-eslint/no-this-alias
let topContext = this;
let parent = topContext.parent;
while (parent) {
topContext = parent;
parent = topContext.parent;
}
return topContext;
}
addChild(childId) {
this.#children.add(childId);
}
#deleteAllChildren() {
this.directChildren.map((child) => child.dispose());
}
get #defaultRealm() {
(0, assert_js_1.assert)(this.#maybeDefaultRealm, `No default realm for browsing context ${this.#id}`);
return this.#maybeDefaultRealm;
}
get cdpTarget() {
return this.#cdpTarget;
}
updateCdpTarget(cdpTarget) {
this.#cdpTarget = cdpTarget;
this.#initListeners();
}
get url() {
return this.#url;
}
async lifecycleLoaded() {
await this.#lifecycle.load;
}
async targetUnblockedOrThrow() {
const result = await this.#cdpTarget.unblocked;
if (result.kind === 'error') {
throw result.error;
}
}
async getOrCreateSandbox(sandbox) {
if (sandbox === undefined || sandbox === '') {
return this.#defaultRealm;
}
let maybeSandboxes = this.#realmStorage.findRealms({
browsingContextId: this.id,
sandbox,
});
if (maybeSandboxes.length === 0) {
await this.#cdpTarget.cdpClient.sendCommand('Page.createIsolatedWorld', {
frameId: this.id,
worldName: sandbox,
});
// `Runtime.executionContextCreated` should be emitted by the time the
// previous command is done.
maybeSandboxes = this.#realmStorage.findRealms({
browsingContextId: this.id,
sandbox,
});
(0, assert_js_1.assert)(maybeSandboxes.length !== 0);
}
// It's possible for more than one sandbox to be created due to provisional
// frames. In this case, it's always the first one (i.e. the oldest one)
// that is more relevant since the user may have set that one up already
// through evaluation.
return maybeSandboxes[0];
}
serializeToBidiValue(maxDepth = 0, addParentField = true) {
return {
context: this.#id,
url: this.url,
userContext: this.userContext,
children: maxDepth > 0
? this.directChildren.map((c) => c.serializeToBidiValue(maxDepth - 1, false))
: null,
...(addParentField ? { parent: this.#parentId } : {}),
};
}
onTargetInfoChanged(params) {
this.#url = params.targetInfo.url;
}
#initListeners() {
this.#cdpTarget.cdpClient.on('Page.frameNavigated', (params) => {
if (this.id !== params.frame.id) {
return;
}
this.#url = params.frame.url + (params.frame.urlFragment ?? '');
// At the point the page is initialized, all the nested iframes from the
// previous page are detached and realms are destroyed.
// Remove children from context.
this.#deleteAllChildren();
});
this.#cdpTarget.cdpClient.on('Page.navigatedWithinDocument', (params) => {
if (this.id !== params.frameId) {
return;
}
const timestamp = BrowsingContextImpl.getTimestamp();
this.#url = params.url;
this.#navigation.withinDocument.resolve(params);
this.#eventManager.registerEvent({
type: 'event',
method: protocol_js_1.ChromiumBidi.BrowsingContext.EventNames.FragmentNavigated,
params: {
context: this.id,
navigation: null,
timestamp,
url: this.#url,
},
}, this.id);
});
this.#cdpTarget.cdpClient.on('Page.frameStartedLoading', (params) => {
if (this.id !== params.frameId) {
return;
}
this.#eventManager.registerEvent({
type: 'event',
method: protocol_js_1.ChromiumBidi.BrowsingContext.EventNames.NavigationStarted,
params: {
context: this.id,
navigation: null,
timestamp: BrowsingContextImpl.getTimestamp(),
url: '',
},
}, this.id);
});
this.#cdpTarget.cdpClient.on('Page.lifecycleEvent', (params) => {
if (this.id !== params.frameId) {
return;
}
if (params.name === 'init') {
this.#documentChanged(params.loaderId);
return;
}
if (params.name === 'commit') {
this.#loaderId = params.loaderId;
return;
}
// Ignore event from not current navigation.
if (params.loaderId !== this.#loaderId) {
return;
}
const timestamp = BrowsingContextImpl.getTimestamp();
switch (params.name) {
case 'DOMContentLoaded':
this.#eventManager.registerEvent({
type: 'event',
method: protocol_js_1.ChromiumBidi.BrowsingContext.EventNames.DomContentLoaded,
params: {
context: this.id,
navigation: this.#loaderId ?? null,
timestamp,
url: this.#url,
},
}, this.id);
this.#lifecycle.DOMContentLoaded.resolve(params);
break;
case 'load':
this.#eventManager.registerEvent({
type: 'event',
method: protocol_js_1.ChromiumBidi.BrowsingContext.EventNames.Load,
params: {
context: this.id,
navigation: this.#loaderId ?? null,
timestamp,
url: this.#url,
},
}, this.id);
this.#lifecycle.load.resolve(params);
break;
}
});
this.#cdpTarget.cdpClient.on('Runtime.executionContextCreated', (params) => {
const { auxData, name, uniqueId, id } = params.context;
if (!auxData || auxData.frameId !== this.id) {
return;
}
let origin;
let sandbox;
// Only these execution contexts are supported for now.
switch (auxData.type) {
case 'isolated':
sandbox = name;
// Sandbox should have the same origin as the context itself, but in CDP
// it has an empty one.
origin = this.#defaultRealm.origin;
break;
case 'default':
origin = serializeOrigin(params.context.origin);
break;
default:
return;
}
const realm = new WindowRealm_js_1.WindowRealm(this.id, this.#browsingContextStorage, this.#cdpTarget.cdpClient, this.#eventManager, id, this.#logger, origin, uniqueId, this.#realmStorage, sandbox, this.#sharedIdWithFrame);
if (auxData.isDefault) {
this.#maybeDefaultRealm = realm;
// Initialize ChannelProxy listeners for all the channels of all the
// preload scripts related to this BrowsingContext.
// TODO: extend for not default realms by the sandbox name.
void Promise.all(this.#cdpTarget
.getChannels()
.map((channel) => channel.startListenerFromWindow(realm, this.#eventManager)));
}
});
this.#cdpTarget.cdpClient.on('Runtime.executionContextDestroyed', (params) => {
this.#realmStorage.deleteRealms({
cdpSessionId: this.#cdpTarget.cdpSessionId,
executionContextId: params.executionContextId,
});
});
this.#cdpTarget.cdpClient.on('Runtime.executionContextsCleared', () => {
this.#realmStorage.deleteRealms({
cdpSessionId: this.#cdpTarget.cdpSessionId,
});
});
this.#cdpTarget.cdpClient.on('Page.javascriptDialogClosed', (params) => {
const accepted = params.result;
this.#eventManager.registerEvent({
type: 'event',
method: protocol_js_1.ChromiumBidi.BrowsingContext.EventNames.UserPromptClosed,
params: {
context: this.id,
accepted,
userText: accepted && params.userInput ? params.userInput : undefined,
},
}, this.id);
});
this.#cdpTarget.cdpClient.on('Page.javascriptDialogOpening', (params) => {
this.#eventManager.registerEvent({
type: 'event',
method: protocol_js_1.ChromiumBidi.BrowsingContext.EventNames.UserPromptOpened,
params: {
context: this.id,
type: params.type,
message: params.message,
// Don't set the value if empty string
defaultValue: params.defaultPrompt || undefined,
},
}, this.id);
});
}
#documentChanged(loaderId) {
// Same document navigation.
if (loaderId === undefined || this.#loaderId === loaderId) {
if (this.#navigation.withinDocument.isFinished) {
this.#navigation.withinDocument =
new Deferred_js_1.Deferred();
}
else {
this.#logger?.(BrowsingContextImpl.LOGGER_PREFIX, 'Document changed (navigatedWithinDocument)');
}
return;
}
this.#resetLifecycleIfFinished();
this.#loaderId = loaderId;
}
#resetLifecycleIfFinished() {
if (this.#lifecycle.DOMContentLoaded.isFinished) {
this.#lifecycle.DOMContentLoaded =
new Deferred_js_1.Deferred();
}
else {
this.#logger?.(BrowsingContextImpl.LOGGER_PREFIX, 'Document changed (DOMContentLoaded)');
}
if (this.#lifecycle.load.isFinished) {
this.#lifecycle.load = new Deferred_js_1.Deferred();
}
else {
this.#logger?.(BrowsingContextImpl.LOGGER_PREFIX, 'Document changed (load)');
}
}
#failLifecycleIfNotFinished() {
if (!this.#lifecycle.DOMContentLoaded.isFinished) {
this.#lifecycle.DOMContentLoaded.reject(new protocol_js_1.UnknownErrorException('navigation canceled'));
}
if (!this.#lifecycle.load.isFinished) {
this.#lifecycle.load.reject(new protocol_js_1.UnknownErrorException('navigation canceled'));
}
}
async navigate(url, wait) {
try {
new URL(url);
}
catch {
throw new protocol_js_1.InvalidArgumentException(`Invalid URL: ${url}`);
}
await this.targetUnblockedOrThrow();
// TODO: handle loading errors.
const cdpNavigateResult = await this.#cdpTarget.cdpClient.sendCommand('Page.navigate', {
url,
frameId: this.id,
});
if (cdpNavigateResult.errorText) {
throw new protocol_js_1.UnknownErrorException(cdpNavigateResult.errorText);
}
this.#documentChanged(cdpNavigateResult.loaderId);
switch (wait) {
case "none" /* BrowsingContext.ReadinessState.None */:
break;
case "interactive" /* BrowsingContext.ReadinessState.Interactive */:
// No `loaderId` means same-document navigation.
if (cdpNavigateResult.loaderId === undefined) {
await this.#navigation.withinDocument;
}
else {
await this.#lifecycle.DOMContentLoaded;
}
break;
case "complete" /* BrowsingContext.ReadinessState.Complete */:
// No `loaderId` means same-document navigation.
if (cdpNavigateResult.loaderId === undefined) {
await this.#navigation.withinDocument;
}
else {
await this.#lifecycle.load;
}
break;
}
return {
navigation: cdpNavigateResult.loaderId ?? null,
// Url can change due to redirect get the latest one.
url: wait === "none" /* BrowsingContext.ReadinessState.None */ ? url : this.#url,
};
}
async reload(ignoreCache, wait) {
await this.targetUnblockedOrThrow();
await this.#cdpTarget.cdpClient.sendCommand('Page.reload', {
ignoreCache,
});
this.#resetLifecycleIfFinished();
switch (wait) {
case "none" /* BrowsingContext.ReadinessState.None */:
break;
case "interactive" /* BrowsingContext.ReadinessState.Interactive */:
await this.#lifecycle.DOMContentLoaded;
break;
case "complete" /* BrowsingContext.ReadinessState.Complete */:
await this.#lifecycle.load;
break;
}
return {
navigation: wait === "none" /* BrowsingContext.ReadinessState.None */
? null
: this.navigableId ?? null,
url: this.url,
};
}
async setViewport(viewport, devicePixelRatio) {
if (viewport === null && devicePixelRatio === null) {
await this.#cdpTarget.cdpClient.sendCommand('Emulation.clearDeviceMetricsOverride');
}
else {
try {
await this.#cdpTarget.cdpClient.sendCommand('Emulation.setDeviceMetricsOverride', {
width: viewport ? viewport.width : 0,
height: viewport ? viewport.height : 0,
deviceScaleFactor: devicePixelRatio ? devicePixelRatio : 0,
mobile: false,
dontSetVisibleSize: true,
});
}
catch (err) {
if (err.message.startsWith(
// https://crsrc.org/c/content/browser/devtools/protocol/emulation_handler.cc;l=257;drc=2f6eee84cf98d4227e7c41718dd71b82f26d90ff
'Width and height values must be positive')) {
throw new protocol_js_1.UnsupportedOperationException('Provided viewport dimensions are not supported');
}
throw err;
}
}
}
async handleUserPrompt(params) {
await this.#cdpTarget.cdpClient.sendCommand('Page.handleJavaScriptDialog', {
accept: params.accept ?? true,
promptText: params.userText,
});
}
async activate() {
await this.#cdpTarget.cdpClient.sendCommand('Page.bringToFront');
}
async captureScreenshot(params) {
if (!this.isTopLevelContext()) {
throw new protocol_js_1.UnsupportedOperationException(`Non-top-level 'context' (${params.context}) is currently not supported`);
}
const formatParameters = getImageFormatParameters(params);
// XXX: Focus the original tab after the screenshot is taken.
// This is needed because the screenshot gets blocked until the active tab gets focus.
await this.#cdpTarget.cdpClient.sendCommand('Page.bringToFront');
let captureBeyondViewport = false;
let script;
params.origin ??= 'viewport';
switch (params.origin) {
case 'document': {
script = String(() => {
const element = document.documentElement;
return {
x: 0,
y: 0,
width: element.scrollWidth,
height: element.scrollHeight,
};
});
captureBeyondViewport = true;
break;
}
case 'viewport': {
script = String(() => {
const viewport = window.visualViewport;
return {
x: viewport.pageLeft,
y: viewport.pageTop,
width: viewport.width,
height: viewport.height,
};
});
break;
}
}
const realm = await this.getOrCreateSandbox(undefined);
const originResult = await realm.callFunction(script, { type: 'undefined' }, [], false, "none" /* Script.ResultOwnership.None */, {}, false);
(0, assert_js_1.assert)(originResult.type === 'success');
const origin = deserializeDOMRect(originResult.result);
(0, assert_js_1.assert)(origin);
const rect = params.clip
? getIntersectionRect(await this.#parseRect(params.clip), origin)
: origin;
if (rect.width === 0 || rect.height === 0) {
throw new protocol_js_1.UnableToCaptureScreenException(`Unable to capture screenshot with zero dimensions: width=${rect.width}, height=${rect.height}`);
}
return await this.#cdpTarget.cdpClient.sendCommand('Page.captureScreenshot', {
clip: { ...rect, scale: 1.0 },
...formatParameters,
captureBeyondViewport,
});
}
async print(params) {
const cdpParams = {};
if (params.background !== undefined) {
cdpParams.printBackground = params.background;
}
if (params.margin?.bottom !== undefined) {
cdpParams.marginBottom = (0, unitConversions_js_1.inchesFromCm)(params.margin.bottom);
}
if (params.margin?.left !== undefined) {
cdpParams.marginLeft = (0, unitConversions_js_1.inchesFromCm)(params.margin.left);
}
if (params.margin?.right !== undefined) {
cdpParams.marginRight = (0, unitConversions_js_1.inchesFromCm)(params.margin.right);
}
if (params.margin?.top !== undefined) {
cdpParams.marginTop = (0, unitConversions_js_1.inchesFromCm)(params.margin.top);
}
if (params.orientation !== undefined) {
cdpParams.landscape = params.orientation === 'landscape';
}
if (params.page?.height !== undefined) {
cdpParams.paperHeight = (0, unitConversions_js_1.inchesFromCm)(params.page.height);
}
if (params.page?.width !== undefined) {
cdpParams.paperWidth = (0, unitConversions_js_1.inchesFromCm)(params.page.width);
}
if (params.pageRanges !== undefined) {
for (const range of params.pageRanges) {
if (typeof range === 'number') {
continue;
}
const rangeParts = range.split('-');
if (rangeParts.length < 1 || rangeParts.length > 2) {
throw new protocol_js_1.InvalidArgumentException(`Invalid page range: ${range} is not a valid integer range.`);
}
if (rangeParts.length === 1) {
void parseInteger(rangeParts[0] ?? '');
continue;
}
let lowerBound;
let upperBound;
const [rangeLowerPart = '', rangeUpperPart = ''] = rangeParts;
if (rangeLowerPart === '') {
lowerBound = 1;
}
else {
lowerBound = parseInteger(rangeLowerPart);
}
if (rangeUpperPart === '') {
upperBound = Number.MAX_SAFE_INTEGER;
}
else {
upperBound = parseInteger(rangeUpperPart);
}
if (lowerBound > upperBound) {
throw new protocol_js_1.InvalidArgumentException(`Invalid page range: ${rangeLowerPart} > ${rangeUpperPart}`);
}
}
cdpParams.pageRanges = params.pageRanges.join(',');
}
if (params.scale !== undefined) {
cdpParams.scale = params.scale;
}
if (params.shrinkToFit !== undefined) {
cdpParams.preferCSSPageSize = !params.shrinkToFit;
}
try {
const result = await this.#cdpTarget.cdpClient.sendCommand('Page.printToPDF', cdpParams);
return {
data: result.data,
};
}
catch (error) {
// Effectively zero dimensions.
if (error.message ===
'invalid print parameters: content area is empty') {
throw new protocol_js_1.UnsupportedOperationException(error.message);
}
throw error;
}
}
/**
* See
* https://w3c.github.io/webdriver-bidi/#:~:text=If%20command%20parameters%20contains%20%22clip%22%3A
*/
async #parseRect(clip) {
switch (clip.type) {
case 'box':
return { x: clip.x, y: clip.y, width: clip.width, height: clip.height };
case 'element': {
// TODO: #1213: Use custom sandbox specifically for Chromium BiDi
const sandbox = await this.getOrCreateSandbox(undefined);
const result = await sandbox.callFunction(String((element) => {
return element instanceof Element;
}), { type: 'undefined' }, [clip.element], false, "none" /* Script.ResultOwnership.None */, {});
if (result.type === 'exception') {
throw new protocol_js_1.NoSuchElementException(`Element '${clip.element.sharedId}' was not found`);
}
(0, assert_js_1.assert)(result.result.type === 'boolean');
if (!result.result.value) {
throw new protocol_js_1.NoSuchElementException(`Node '${clip.element.sharedId}' is not an Element`);
}
{
const result = await sandbox.callFunction(String((element) => {
const rect = element.getBoundingClientRect();
return {
x: rect.x,
y: rect.y,
height: rect.height,
width: rect.width,
};
}), { type: 'undefined' }, [clip.element], false, "none" /* Script.ResultOwnership.None */, {});
(0, assert_js_1.assert)(result.type === 'success');
const rect = deserializeDOMRect(result.result);
if (!rect) {
throw new protocol_js_1.UnableToCaptureScreenException(`Could not get bounding box for Element '${clip.element.sharedId}'`);
}
return rect;
}
}
}
}
async close() {
await this.#cdpTarget.cdpClient.sendCommand('Page.close');
}
async traverseHistory(delta) {
if (delta === 0) {
return;
}
const history = await this.#cdpTarget.cdpClient.sendCommand('Page.getNavigationHistory');
const entry = history.entries[history.currentIndex + delta];
if (!entry) {
throw new protocol_js_1.NoSuchHistoryEntryException(`No history entry at delta ${delta}`);
}
await this.#cdpTarget.cdpClient.sendCommand('Page.navigateToHistoryEntry', {
entryId: entry.id,
});
}
}
exports.BrowsingContextImpl = BrowsingContextImpl;
function serializeOrigin(origin) {
// https://html.spec.whatwg.org/multipage/origin.html#ascii-serialisation-of-an-origin
if (['://', ''].includes(origin)) {
origin = 'null';
}
return origin;
}
exports.serializeOrigin = serializeOrigin;
function getImageFormatParameters(params) {
const { quality, type } = params.format ?? {
type: 'image/png',
};
switch (type) {
case 'image/png': {
return { format: 'png' };
}
case 'image/jpeg': {
return {
format: 'jpeg',
...(quality === undefined ? {} : { quality: Math.round(quality * 100) }),
};
}
case 'image/webp': {
return {
format: 'webp',
...(quality === undefined ? {} : { quality: Math.round(quality * 100) }),
};
}
}
throw new protocol_js_1.InvalidArgumentException(`Image format '${type}' is not a supported format`);
}
function deserializeDOMRect(result) {
if (result.type !== 'object' || result.value === undefined) {
return;
}
const x = result.value.find(([key]) => {
return key === 'x';
})?.[1];
const y = result.value.find(([key]) => {
return key === 'y';
})?.[1];
const height = result.value.find(([key]) => {
return key === 'height';
})?.[1];
const width = result.value.find(([key]) => {
return key === 'width';
})?.[1];
if (x?.type !== 'number' ||
y?.type !== 'number' ||
height?.type !== 'number' ||
width?.type !== 'number') {
return;
}
return {
x: x.value,
y: y.value,
width: width.value,
height: height.value,
};
}
/** @see https://w3c.github.io/webdriver-bidi/#normalize-rect */
function normalizeRect(box) {
return {
...(box.width < 0
? {
x: box.x + box.width,
width: -box.width,
}
: {
x: box.x,
width: box.width,
}),
...(box.height < 0
? {
y: box.y + box.height,
height: -box.height,
}
: {
y: box.y,
height: box.height,
}),
};
}
/** @see https://w3c.github.io/webdriver-bidi/#rectangle-intersection */
function getIntersectionRect(first, second) {
first = normalizeRect(first);
second = normalizeRect(second);
const x = Math.max(first.x, second.x);
const y = Math.max(first.y, second.y);
return {
x,
y,
width: Math.max(Math.min(first.x + first.width, second.x + second.width) - x, 0),
height: Math.max(Math.min(first.y + first.height, second.y + second.height) - y, 0),
};
}
function parseInteger(value) {
value = value.trim();
if (!/^[0-9]+$/.test(value)) {
throw new protocol_js_1.InvalidArgumentException(`Invalid integer: ${value}`);
}
return parseInt(value);
}
//# sourceMappingURL=BrowsingContextImpl.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,24 @@
import type { CdpClient } from '../../../cdp/CdpClient.js';
import type { CdpConnection } from '../../../cdp/CdpConnection.js';
import { BrowsingContext, type EmptyResult, type Browser } from '../../../protocol/protocol.js';
import { type LoggerFn } from '../../../utils/log.js';
import type { NetworkStorage } from '../network/NetworkStorage.js';
import type { PreloadScriptStorage } from '../script/PreloadScriptStorage.js';
import type { RealmStorage } from '../script/RealmStorage.js';
import type { EventManager } from '../session/EventManager.js';
import type { BrowsingContextStorage } from './BrowsingContextStorage.js';
export declare class BrowsingContextProcessor {
#private;
constructor(cdpConnection: CdpConnection, browserCdpClient: CdpClient, selfTargetId: string, eventManager: EventManager, browsingContextStorage: BrowsingContextStorage, realmStorage: RealmStorage, networkStorage: NetworkStorage, preloadScriptStorage: PreloadScriptStorage, acceptInsecureCerts: boolean, sharedIdWithFrame: boolean, defaultUserContextId: Browser.UserContext, logger?: LoggerFn);
getTree(params: BrowsingContext.GetTreeParameters): BrowsingContext.GetTreeResult;
create(params: BrowsingContext.CreateParameters): Promise<BrowsingContext.CreateResult>;
navigate(params: BrowsingContext.NavigateParameters): Promise<BrowsingContext.NavigateResult>;
reload(params: BrowsingContext.ReloadParameters): Promise<EmptyResult>;
activate(params: BrowsingContext.ActivateParameters): Promise<EmptyResult>;
captureScreenshot(params: BrowsingContext.CaptureScreenshotParameters): Promise<BrowsingContext.CaptureScreenshotResult>;
print(params: BrowsingContext.PrintParameters): Promise<BrowsingContext.PrintResult>;
setViewport(params: BrowsingContext.SetViewportParameters): Promise<EmptyResult>;
traverseHistory(params: BrowsingContext.TraverseHistoryParameters): Promise<BrowsingContext.TraverseHistoryResult>;
handleUserPrompt(params: BrowsingContext.HandleUserPromptParameters): Promise<EmptyResult>;
close(params: BrowsingContext.CloseParameters): Promise<EmptyResult>;
}

View File

@@ -0,0 +1,303 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BrowsingContextProcessor = void 0;
const protocol_js_1 = require("../../../protocol/protocol.js");
const log_js_1 = require("../../../utils/log.js");
const DedicatedWorkerRealm_js_1 = require("../script/DedicatedWorkerRealm.js");
const BrowsingContextImpl_js_1 = require("./BrowsingContextImpl.js");
const CdpTarget_js_1 = require("./CdpTarget.js");
class BrowsingContextProcessor {
#browserCdpClient;
#cdpConnection;
#selfTargetId;
#eventManager;
#browsingContextStorage;
#networkStorage;
#acceptInsecureCerts;
#sharedIdWithFrame;
#preloadScriptStorage;
#realmStorage;
#defaultUserContextId;
#logger;
constructor(cdpConnection, browserCdpClient, selfTargetId, eventManager, browsingContextStorage, realmStorage, networkStorage, preloadScriptStorage, acceptInsecureCerts, sharedIdWithFrame, defaultUserContextId, logger) {
this.#acceptInsecureCerts = acceptInsecureCerts;
this.#cdpConnection = cdpConnection;
this.#browserCdpClient = browserCdpClient;
this.#selfTargetId = selfTargetId;
this.#eventManager = eventManager;
this.#browsingContextStorage = browsingContextStorage;
this.#preloadScriptStorage = preloadScriptStorage;
this.#networkStorage = networkStorage;
this.#realmStorage = realmStorage;
this.#sharedIdWithFrame = sharedIdWithFrame;
this.#defaultUserContextId = defaultUserContextId;
this.#logger = logger;
this.#setEventListeners(browserCdpClient);
}
getTree(params) {
const resultContexts = params.root === undefined
? this.#browsingContextStorage.getTopLevelContexts()
: [this.#browsingContextStorage.getContext(params.root)];
return {
contexts: resultContexts.map((c) => c.serializeToBidiValue(params.maxDepth ?? Number.MAX_VALUE)),
};
}
async create(params) {
let referenceContext;
let userContext = params.userContext ?? 'default';
if (params.referenceContext !== undefined) {
referenceContext = this.#browsingContextStorage.getContext(params.referenceContext);
if (!referenceContext.isTopLevelContext()) {
throw new protocol_js_1.InvalidArgumentException(`referenceContext should be a top-level context`);
}
userContext = referenceContext.userContext;
}
let newWindow = false;
switch (params.type) {
case "tab" /* BrowsingContext.CreateType.Tab */:
newWindow = false;
break;
case "window" /* BrowsingContext.CreateType.Window */:
newWindow = true;
break;
}
if (userContext !== 'default') {
const existingContexts = this.#browsingContextStorage
.getAllContexts()
.filter((context) => context.userContext === userContext);
if (!existingContexts.length) {
// If there are no contexts in the given user context, we need to set
// newWindow to true as newWindow=false will be rejected.
newWindow = true;
}
}
let result;
try {
result = await this.#browserCdpClient.sendCommand('Target.createTarget', {
url: 'about:blank',
newWindow,
browserContextId: userContext === 'default' ? undefined : userContext,
});
}
catch (err) {
if (
// See https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/devtools/protocol/target_handler.cc;l=90;drc=e80392ac11e48a691f4309964cab83a3a59e01c8
err.message.startsWith('Failed to find browser context with id') ||
// See https://source.chromium.org/chromium/chromium/src/+/main:headless/lib/browser/protocol/target_handler.cc;l=49;drc=e80392ac11e48a691f4309964cab83a3a59e01c8
err.message === 'browserContextId') {
throw new protocol_js_1.NoSuchUserContextException(`The context ${userContext} was not found`);
}
throw err;
}
// Wait for the new tab to be loaded to avoid race conditions in the
// `browsingContext` events, when the `browsingContext.domContentLoaded` and
// `browsingContext.load` events from the initial `about:blank` navigation
// are emitted after the next navigation is started.
// Details: https://github.com/web-platform-tests/wpt/issues/35846
const contextId = result.targetId;
const context = this.#browsingContextStorage.getContext(contextId);
await context.lifecycleLoaded();
return { context: context.id };
}
navigate(params) {
const context = this.#browsingContextStorage.getContext(params.context);
return context.navigate(params.url, params.wait ?? "none" /* BrowsingContext.ReadinessState.None */);
}
reload(params) {
const context = this.#browsingContextStorage.getContext(params.context);
return context.reload(params.ignoreCache ?? false, params.wait ?? "none" /* BrowsingContext.ReadinessState.None */);
}
async activate(params) {
const context = this.#browsingContextStorage.getContext(params.context);
if (!context.isTopLevelContext()) {
throw new protocol_js_1.InvalidArgumentException('Activation is only supported on the top-level context');
}
await context.activate();
return {};
}
async captureScreenshot(params) {
const context = this.#browsingContextStorage.getContext(params.context);
return await context.captureScreenshot(params);
}
async print(params) {
const context = this.#browsingContextStorage.getContext(params.context);
return await context.print(params);
}
async setViewport(params) {
const context = this.#browsingContextStorage.getContext(params.context);
if (!context.isTopLevelContext()) {
throw new protocol_js_1.InvalidArgumentException('Emulating viewport is only supported on the top-level context');
}
await context.setViewport(params.viewport, params.devicePixelRatio);
return {};
}
async traverseHistory(params) {
const context = this.#browsingContextStorage.getContext(params.context);
if (!context) {
throw new protocol_js_1.InvalidArgumentException(`No browsing context with id ${params.context}`);
}
await context.traverseHistory(params.delta);
return {};
}
async handleUserPrompt(params) {
const context = this.#browsingContextStorage.getContext(params.context);
await context.handleUserPrompt(params);
return {};
}
async close(params) {
const context = this.#browsingContextStorage.getContext(params.context);
if (!context.isTopLevelContext()) {
throw new protocol_js_1.InvalidArgumentException(`Non top-level browsing context ${context.id} cannot be closed.`);
}
try {
const detachedFromTargetPromise = new Promise((resolve) => {
const onContextDestroyed = (event) => {
if (event.targetId === params.context) {
this.#browserCdpClient.off('Target.detachedFromTarget', onContextDestroyed);
resolve();
}
};
this.#browserCdpClient.on('Target.detachedFromTarget', onContextDestroyed);
});
if (params.promptUnload) {
await context.close();
}
else {
await this.#browserCdpClient.sendCommand('Target.closeTarget', {
targetId: params.context,
});
}
// Sometimes CDP command finishes before `detachedFromTarget` event,
// sometimes after. Wait for the CDP command to be finished, and then wait
// for `detachedFromTarget` if it hasn't emitted.
await detachedFromTargetPromise;
}
catch (error) {
// Swallow error that arise from the page being destroyed
// Example is navigating to faulty SSL certificate
if (!(error.code === -32000 /* CdpErrorConstants.GENERIC_ERROR */ &&
error.message === 'Not attached to an active page')) {
throw error;
}
}
return {};
}
/**
* This method is called for each CDP session, since this class is responsible
* for creating and destroying all targets and browsing contexts.
*/
#setEventListeners(cdpClient) {
cdpClient.on('Target.attachedToTarget', (params) => {
this.#handleAttachedToTargetEvent(params, cdpClient);
});
cdpClient.on('Target.detachedFromTarget', (params) => {
this.#handleDetachedFromTargetEvent(params);
});
cdpClient.on('Target.targetInfoChanged', (params) => {
this.#handleTargetInfoChangedEvent(params);
});
cdpClient.on('Page.frameAttached', (params) => {
this.#handleFrameAttachedEvent(params);
});
cdpClient.on('Page.frameDetached', (params) => {
this.#handleFrameDetachedEvent(params);
});
}
#handleFrameAttachedEvent(params) {
const parentBrowsingContext = this.#browsingContextStorage.findContext(params.parentFrameId);
if (parentBrowsingContext !== undefined) {
BrowsingContextImpl_js_1.BrowsingContextImpl.create(parentBrowsingContext.cdpTarget, this.#realmStorage, params.frameId, params.parentFrameId, parentBrowsingContext.userContext, this.#eventManager, this.#browsingContextStorage, this.#sharedIdWithFrame, this.#logger);
}
}
#handleFrameDetachedEvent(params) {
// In case of OOPiF no need in deleting BrowsingContext.
if (params.reason === 'swap') {
return;
}
this.#browsingContextStorage.findContext(params.frameId)?.dispose();
}
#handleAttachedToTargetEvent(params, parentSessionCdpClient) {
const { sessionId, targetInfo } = params;
const targetCdpClient = this.#cdpConnection.getCdpClient(sessionId);
this.#logger?.(log_js_1.LogType.debugInfo, 'AttachedToTarget event received:', params);
switch (targetInfo.type) {
case 'page':
case 'iframe': {
if (targetInfo.targetId === this.#selfTargetId) {
break;
}
const cdpTarget = this.#createCdpTarget(targetCdpClient, targetInfo);
const maybeContext = this.#browsingContextStorage.findContext(targetInfo.targetId);
if (maybeContext) {
// OOPiF.
maybeContext.updateCdpTarget(cdpTarget);
}
else {
// New context.
BrowsingContextImpl_js_1.BrowsingContextImpl.create(cdpTarget, this.#realmStorage, targetInfo.targetId, null, targetInfo.browserContextId &&
targetInfo.browserContextId !== this.#defaultUserContextId
? targetInfo.browserContextId
: 'default', this.#eventManager, this.#browsingContextStorage, this.#sharedIdWithFrame, this.#logger);
}
return;
}
case 'worker': {
const browsingContext = parentSessionCdpClient.sessionId &&
this.#browsingContextStorage.findContextBySession(parentSessionCdpClient.sessionId);
// If there is no browsing context, this worker is already terminated.
if (!browsingContext) {
break;
}
const cdpTarget = this.#createCdpTarget(targetCdpClient, targetInfo);
this.#handleWorkerTarget(cdpTarget, this.#realmStorage.getRealm({
browsingContextId: browsingContext.id,
type: 'window',
sandbox: undefined,
}));
return;
}
}
// DevTools or some other not supported by BiDi target. Just release
// debugger and ignore them.
targetCdpClient
.sendCommand('Runtime.runIfWaitingForDebugger')
.then(() => parentSessionCdpClient.sendCommand('Target.detachFromTarget', params))
.catch((error) => this.#logger?.(log_js_1.LogType.debugError, error));
}
#createCdpTarget(targetCdpClient, targetInfo) {
this.#setEventListeners(targetCdpClient);
return CdpTarget_js_1.CdpTarget.create(targetInfo.targetId, targetCdpClient, this.#browserCdpClient, this.#realmStorage, this.#eventManager, this.#preloadScriptStorage, this.#networkStorage, this.#acceptInsecureCerts, this.#logger);
}
#workers = new Map();
#handleWorkerTarget(cdpTarget, ownerRealm) {
cdpTarget.cdpClient.on('Runtime.executionContextCreated', (params) => {
const { uniqueId, id, origin } = params.context;
const workerRealm = new DedicatedWorkerRealm_js_1.DedicatedWorkerRealm(cdpTarget.cdpClient, this.#eventManager, id, this.#logger, (0, BrowsingContextImpl_js_1.serializeOrigin)(origin), ownerRealm, uniqueId, this.#realmStorage);
this.#workers.set(cdpTarget.cdpSessionId, workerRealm);
});
}
#handleDetachedFromTargetEvent(params) {
const context = this.#browsingContextStorage.findContextBySession(params.sessionId);
if (context) {
context.dispose();
this.#preloadScriptStorage
.find({ targetId: context.id })
.map((preloadScript) => preloadScript.dispose(context.id));
return;
}
const worker = this.#workers.get(params.sessionId);
if (worker) {
this.#realmStorage.deleteRealms({
cdpSessionId: worker.cdpClient.sessionId,
});
}
}
#handleTargetInfoChangedEvent(params) {
const context = this.#browsingContextStorage.findContext(params.targetInfo.targetId);
if (context) {
context.onTargetInfoChanged(params);
}
}
}
exports.BrowsingContextProcessor = BrowsingContextProcessor;
//# sourceMappingURL=BrowsingContextProcessor.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,41 @@
/**
* Copyright 2022 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { type BrowsingContext } from '../../../protocol/protocol.js';
import type { BrowsingContextImpl } from './BrowsingContextImpl.js';
/** Container class for browsing contexts. */
export declare class BrowsingContextStorage {
#private;
/** Gets all top-level contexts, i.e. those with no parent. */
getTopLevelContexts(): BrowsingContextImpl[];
/** Gets all contexts. */
getAllContexts(): BrowsingContextImpl[];
/** Deletes the context with the given ID. */
deleteContextById(id: BrowsingContext.BrowsingContext): void;
/** Deletes the given context. */
deleteContext(context: BrowsingContextImpl): void;
/** Tracks the given context. */
addContext(context: BrowsingContextImpl): void;
/** Returns true whether there is an existing context with the given ID. */
hasContext(id: BrowsingContext.BrowsingContext): boolean;
/** Gets the context with the given ID, if any. */
findContext(id: BrowsingContext.BrowsingContext): BrowsingContextImpl | undefined;
/** Returns the top-level context ID of the given context, if any. */
findTopLevelContextId(id: BrowsingContext.BrowsingContext | null): BrowsingContext.BrowsingContext | null;
findContextBySession(sessionId: string): BrowsingContextImpl | undefined;
/** Gets the context with the given ID, if any, otherwise throws. */
getContext(id: BrowsingContext.BrowsingContext): BrowsingContextImpl;
}

View File

@@ -0,0 +1,83 @@
"use strict";
/**
* Copyright 2022 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.BrowsingContextStorage = void 0;
const protocol_js_1 = require("../../../protocol/protocol.js");
/** Container class for browsing contexts. */
class BrowsingContextStorage {
/** Map from context ID to context implementation. */
#contexts = new Map();
/** Gets all top-level contexts, i.e. those with no parent. */
getTopLevelContexts() {
return this.getAllContexts().filter((context) => context.isTopLevelContext());
}
/** Gets all contexts. */
getAllContexts() {
return Array.from(this.#contexts.values());
}
/** Deletes the context with the given ID. */
deleteContextById(id) {
this.#contexts.delete(id);
}
/** Deletes the given context. */
deleteContext(context) {
this.#contexts.delete(context.id);
}
/** Tracks the given context. */
addContext(context) {
this.#contexts.set(context.id, context);
}
/** Returns true whether there is an existing context with the given ID. */
hasContext(id) {
return this.#contexts.has(id);
}
/** Gets the context with the given ID, if any. */
findContext(id) {
return this.#contexts.get(id);
}
/** Returns the top-level context ID of the given context, if any. */
findTopLevelContextId(id) {
if (id === null) {
return null;
}
const maybeContext = this.findContext(id);
const parentId = maybeContext?.parentId ?? null;
if (parentId === null) {
return id;
}
return this.findTopLevelContextId(parentId);
}
findContextBySession(sessionId) {
for (const context of this.#contexts.values()) {
if (context.cdpTarget.cdpSessionId === sessionId) {
return context;
}
}
return;
}
/** Gets the context with the given ID, if any, otherwise throws. */
getContext(id) {
const result = this.findContext(id);
if (result === undefined) {
throw new protocol_js_1.NoSuchFrameException(`Context ${id} not found`);
}
return result;
}
}
exports.BrowsingContextStorage = BrowsingContextStorage;
//# sourceMappingURL=BrowsingContextStorage.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"BrowsingContextStorage.js","sourceRoot":"","sources":["../../../../../src/bidiMapper/domains/context/BrowsingContextStorage.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;GAeG;;;AAEH,+DAGuC;AAIvC,6CAA6C;AAC7C,MAAa,sBAAsB;IACjC,qDAAqD;IAC5C,SAAS,GAAG,IAAI,GAAG,EAGzB,CAAC;IAEJ,8DAA8D;IAC9D,mBAAmB;QACjB,OAAO,IAAI,CAAC,cAAc,EAAE,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAC9C,OAAO,CAAC,iBAAiB,EAAE,CAC5B,CAAC;IACJ,CAAC;IAED,yBAAyB;IACzB,cAAc;QACZ,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7C,CAAC;IAED,6CAA6C;IAC7C,iBAAiB,CAAC,EAAmC;QACnD,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IAC5B,CAAC;IAED,iCAAiC;IACjC,aAAa,CAAC,OAA4B;QACxC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACpC,CAAC;IAED,gCAAgC;IAChC,UAAU,CAAC,OAA4B;QACrC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC;IAED,2EAA2E;IAC3E,UAAU,CAAC,EAAmC;QAC5C,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChC,CAAC;IAED,kDAAkD;IAClD,WAAW,CACT,EAAmC;QAEnC,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChC,CAAC;IAED,qEAAqE;IACrE,qBAAqB,CACnB,EAA0C;QAE1C,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;YAChB,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,YAAY,GAAG,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;QAC1C,MAAM,QAAQ,GAAG,YAAY,EAAE,QAAQ,IAAI,IAAI,CAAC;QAChD,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtB,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,OAAO,IAAI,CAAC,qBAAqB,CAAC,QAAQ,CAAC,CAAC;IAC9C,CAAC;IAED,oBAAoB,CAAC,SAAiB;QACpC,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC;YAC9C,IAAI,OAAO,CAAC,SAAS,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;gBACjD,OAAO,OAAO,CAAC;YACjB,CAAC;QACH,CAAC;QACD,OAAO;IACT,CAAC;IAED,oEAAoE;IACpE,UAAU,CAAC,EAAmC;QAC5C,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;QACpC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,IAAI,kCAAoB,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;QAC5D,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;CACF;AA9ED,wDA8EC"}

View File

@@ -0,0 +1,31 @@
import type { Protocol } from 'devtools-protocol';
import type { CdpClient } from '../../../cdp/CdpClient.js';
import { Deferred } from '../../../utils/Deferred.js';
import type { LoggerFn } from '../../../utils/log.js';
import type { Result } from '../../../utils/result.js';
import type { NetworkStorage } from '../network/NetworkStorage.js';
import type { ChannelProxy } from '../script/ChannelProxy.js';
import type { PreloadScriptStorage } from '../script/PreloadScriptStorage.js';
import type { RealmStorage } from '../script/RealmStorage.js';
import type { EventManager } from '../session/EventManager.js';
export declare class CdpTarget {
#private;
static create(targetId: Protocol.Target.TargetID, cdpClient: CdpClient, browserCdpClient: CdpClient, realmStorage: RealmStorage, eventManager: EventManager, preloadScriptStorage: PreloadScriptStorage, networkStorage: NetworkStorage, acceptInsecureCerts: boolean, logger?: LoggerFn): CdpTarget;
constructor(targetId: Protocol.Target.TargetID, cdpClient: CdpClient, browserCdpClient: CdpClient, eventManager: EventManager, preloadScriptStorage: PreloadScriptStorage, networkStorage: NetworkStorage, acceptInsecureCerts: boolean);
/** Returns a deferred that resolves when the target is unblocked. */
get unblocked(): Deferred<Result<void>>;
get id(): Protocol.Target.TargetID;
get cdpClient(): CdpClient;
get browserCdpClient(): CdpClient;
/** Needed for CDP escape path. */
get cdpSessionId(): Protocol.Target.SessionID;
/** Calls `Fetch.enable` with the added network intercepts. */
fetchEnable(): Promise<void>;
/** Calls `Fetch.disable`. */
fetchDisable(): Promise<void>;
/**
* All the ProxyChannels from all the preload scripts of the given
* BrowsingContext.
*/
getChannels(): ChannelProxy[];
}

View File

@@ -0,0 +1,142 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CdpTarget = void 0;
const Deferred_js_1 = require("../../../utils/Deferred.js");
const LogManager_js_1 = require("../log/LogManager.js");
const NetworkManager_js_1 = require("../network/NetworkManager.js");
class CdpTarget {
#id;
#cdpClient;
#browserCdpClient;
#eventManager;
#preloadScriptStorage;
#networkStorage;
#targetUnblocked = new Deferred_js_1.Deferred();
#acceptInsecureCerts;
static create(targetId, cdpClient, browserCdpClient, realmStorage, eventManager, preloadScriptStorage, networkStorage, acceptInsecureCerts, logger) {
const cdpTarget = new CdpTarget(targetId, cdpClient, browserCdpClient, eventManager, preloadScriptStorage, networkStorage, acceptInsecureCerts);
LogManager_js_1.LogManager.create(cdpTarget, realmStorage, eventManager, logger);
NetworkManager_js_1.NetworkManager.create(cdpTarget, eventManager, networkStorage);
cdpTarget.#setEventListeners();
// No need to await.
// Deferred will be resolved when the target is unblocked.
void cdpTarget.#unblock();
return cdpTarget;
}
constructor(targetId, cdpClient, browserCdpClient, eventManager, preloadScriptStorage, networkStorage, acceptInsecureCerts) {
this.#id = targetId;
this.#cdpClient = cdpClient;
this.#eventManager = eventManager;
this.#preloadScriptStorage = preloadScriptStorage;
this.#networkStorage = networkStorage;
this.#browserCdpClient = browserCdpClient;
this.#acceptInsecureCerts = acceptInsecureCerts;
}
/** Returns a deferred that resolves when the target is unblocked. */
get unblocked() {
return this.#targetUnblocked;
}
get id() {
return this.#id;
}
get cdpClient() {
return this.#cdpClient;
}
get browserCdpClient() {
return this.#browserCdpClient;
}
/** Needed for CDP escape path. */
get cdpSessionId() {
// SAFETY we got the client by it's id for creating
return this.#cdpClient.sessionId;
}
/** Calls `Fetch.enable` with the added network intercepts. */
async fetchEnable() {
await this.#cdpClient.sendCommand('Fetch.enable', this.#networkStorage.getFetchEnableParams());
}
/** Calls `Fetch.disable`. */
async fetchDisable() {
await this.#cdpClient.sendCommand('Fetch.disable');
}
/**
* Enables all the required CDP domains and unblocks the target.
*/
async #unblock() {
try {
await Promise.all([
this.#cdpClient.sendCommand('Runtime.enable'),
this.#cdpClient.sendCommand('Page.enable'),
this.#cdpClient.sendCommand('Page.setLifecycleEventsEnabled', {
enabled: true,
}),
// Set ignore certificate errors for each target.
this.#cdpClient.sendCommand('Security.setIgnoreCertificateErrors', {
ignore: this.#acceptInsecureCerts,
}),
// XXX: #1080: Do not always enable the network domain globally.
// TODO: enable Network domain for OOPiF targets.
this.#cdpClient.sendCommand('Network.enable'),
// XXX: #1080: Do not always enable the fetch domain globally.
this.fetchEnable(),
this.#cdpClient.sendCommand('Target.setAutoAttach', {
autoAttach: true,
waitForDebuggerOnStart: true,
flatten: true,
}),
this.#initAndEvaluatePreloadScripts(),
this.#cdpClient.sendCommand('Runtime.runIfWaitingForDebugger'),
]);
}
catch (error) {
// The target might have been closed before the initialization finished.
if (!this.#cdpClient.isCloseError(error)) {
this.#targetUnblocked.resolve({
kind: 'error',
error,
});
return;
}
}
this.#targetUnblocked.resolve({
kind: 'success',
value: undefined,
});
}
#setEventListeners() {
this.#cdpClient.on('*', (event, params) => {
// We may encounter uses for EventEmitter other than CDP events,
// which we want to skip.
if (typeof event !== 'string') {
return;
}
this.#eventManager.registerEvent({
type: 'event',
method: `cdp.${event}`,
params: {
event,
params,
session: this.cdpSessionId,
},
}, null);
});
}
/**
* All the ProxyChannels from all the preload scripts of the given
* BrowsingContext.
*/
getChannels() {
return this.#preloadScriptStorage
.find()
.flatMap((script) => script.channels);
}
/** Loads all top-level preload scripts. */
async #initAndEvaluatePreloadScripts() {
for (const script of this.#preloadScriptStorage.find({
global: true,
})) {
await script.initInTarget(this, true);
}
}
}
exports.CdpTarget = CdpTarget;
//# sourceMappingURL=CdpTarget.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"CdpTarget.js","sourceRoot":"","sources":["../../../../../src/bidiMapper/domains/context/CdpTarget.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,26 @@
/**
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { BrowsingContextImpl } from '../context/BrowsingContextImpl.js';
import type { ActionOption } from './ActionOption.js';
import type { InputState } from './InputState.js';
export declare class ActionDispatcher {
#private;
static isMacOS: (context: BrowsingContextImpl) => Promise<boolean>;
constructor(inputState: InputState, context: BrowsingContextImpl, isMacOS: boolean);
dispatchActions(optionsByTick: readonly (readonly Readonly<ActionOption>[])[]): Promise<void>;
dispatchTickActions(options: readonly Readonly<ActionOption>[]): Promise<void>;
}

View File

@@ -0,0 +1,681 @@
"use strict";
/**
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ActionDispatcher = void 0;
const protocol_js_1 = require("../../../protocol/protocol.js");
const assert_js_1 = require("../../../utils/assert.js");
const InputSource_js_1 = require("./InputSource.js");
const keyUtils_js_1 = require("./keyUtils.js");
const USKeyboardLayout_js_1 = require("./USKeyboardLayout.js");
/** https://w3c.github.io/webdriver/#dfn-center-point */
const CALCULATE_IN_VIEW_CENTER_PT_DECL = ((i) => {
const t = i.getClientRects()[0], e = Math.max(0, Math.min(t.x, t.x + t.width)), n = Math.min(window.innerWidth, Math.max(t.x, t.x + t.width)), h = Math.max(0, Math.min(t.y, t.y + t.height)), m = Math.min(window.innerHeight, Math.max(t.y, t.y + t.height));
return [e + ((n - e) >> 1), h + ((m - h) >> 1)];
}).toString();
const IS_MAC_DECL = (() => {
return navigator.platform.toLowerCase().includes('mac');
}).toString();
async function getElementCenter(context, element) {
const sandbox = await context.getOrCreateSandbox(undefined);
const result = await sandbox.callFunction(CALCULATE_IN_VIEW_CENTER_PT_DECL, { type: 'undefined' }, [element], false, "none" /* Script.ResultOwnership.None */, {});
if (result.type === 'exception') {
throw new protocol_js_1.NoSuchElementException(`Origin element ${element.sharedId} was not found`);
}
(0, assert_js_1.assert)(result.result.type === 'array');
(0, assert_js_1.assert)(result.result.value?.[0]?.type === 'number');
(0, assert_js_1.assert)(result.result.value?.[1]?.type === 'number');
const { result: { value: [{ value: x }, { value: y }], }, } = result;
return { x: x, y: y };
}
class ActionDispatcher {
static isMacOS = async (context) => {
const result = await (await context.getOrCreateSandbox(undefined)).callFunction(IS_MAC_DECL, { type: 'undefined' }, [], false, "none" /* Script.ResultOwnership.None */, {});
(0, assert_js_1.assert)(result.type !== 'exception');
(0, assert_js_1.assert)(result.result.type === 'boolean');
return result.result.value;
};
#tickStart = 0;
#tickDuration = 0;
#inputState;
#context;
#isMacOS;
constructor(inputState, context, isMacOS) {
this.#inputState = inputState;
this.#context = context;
this.#isMacOS = isMacOS;
}
async dispatchActions(optionsByTick) {
await this.#inputState.queue.run(async () => {
for (const options of optionsByTick) {
await this.dispatchTickActions(options);
}
});
}
async dispatchTickActions(options) {
this.#tickStart = performance.now();
this.#tickDuration = 0;
for (const { action } of options) {
if ('duration' in action && action.duration !== undefined) {
this.#tickDuration = Math.max(this.#tickDuration, action.duration);
}
}
const promises = [
new Promise((resolve) => setTimeout(resolve, this.#tickDuration)),
];
for (const option of options) {
// In theory we have to wait for each action to happen, but CDP is serial,
// so as an optimization, we queue all CDP commands at once and await all
// of them.
promises.push(this.#dispatchAction(option));
}
await Promise.all(promises);
}
async #dispatchAction({ id, action }) {
const source = this.#inputState.get(id);
const keyState = this.#inputState.getGlobalKeyState();
switch (action.type) {
case 'keyDown': {
// SAFETY: The source is validated before.
await this.#dispatchKeyDownAction(source, action);
this.#inputState.cancelList.push({
id,
action: {
...action,
type: 'keyUp',
},
});
break;
}
case 'keyUp': {
// SAFETY: The source is validated before.
await this.#dispatchKeyUpAction(source, action);
break;
}
case 'pause': {
// TODO: Implement waiting on the input source.
break;
}
case 'pointerDown': {
// SAFETY: The source is validated before.
await this.#dispatchPointerDownAction(source, keyState, action);
this.#inputState.cancelList.push({
id,
action: {
...action,
type: 'pointerUp',
},
});
break;
}
case 'pointerMove': {
// SAFETY: The source is validated before.
await this.#dispatchPointerMoveAction(source, keyState, action);
break;
}
case 'pointerUp': {
// SAFETY: The source is validated before.
await this.#dispatchPointerUpAction(source, keyState, action);
break;
}
case 'scroll': {
// SAFETY: The source is validated before.
await this.#dispatchScrollAction(source, keyState, action);
break;
}
}
}
#dispatchPointerDownAction(source, keyState, action) {
const { button } = action;
if (source.pressed.has(button)) {
return;
}
source.pressed.add(button);
const { x, y, subtype: pointerType } = source;
const { width, height, pressure, twist, tangentialPressure } = action;
const { tiltX, tiltY } = getTilt(action);
// --- Platform-specific code begins here ---
const { modifiers } = keyState;
switch (pointerType) {
case "mouse" /* Input.PointerType.Mouse */:
case "pen" /* Input.PointerType.Pen */:
// TODO: Implement width and height when available.
return this.#context.cdpTarget.cdpClient.sendCommand('Input.dispatchMouseEvent', {
type: 'mousePressed',
x,
y,
modifiers,
button: getCdpButton(button),
buttons: source.buttons,
clickCount: source.setClickCount(button, new InputSource_js_1.PointerSource.ClickContext(x, y, performance.now())),
pointerType,
tangentialPressure,
tiltX,
tiltY,
twist,
force: pressure,
});
case "touch" /* Input.PointerType.Touch */:
return this.#context.cdpTarget.cdpClient.sendCommand('Input.dispatchTouchEvent', {
type: 'touchStart',
touchPoints: [
{
x,
y,
...getRadii(width ?? 1, height ?? 1),
tangentialPressure,
tiltX,
tiltY,
twist,
force: pressure,
id: source.pointerId,
},
],
modifiers,
});
}
// --- Platform-specific code ends here ---
}
#dispatchPointerUpAction(source, keyState, action) {
const { button } = action;
if (!source.pressed.has(button)) {
return;
}
source.pressed.delete(button);
const { x, y, subtype: pointerType } = source;
// --- Platform-specific code begins here ---
const { modifiers } = keyState;
switch (pointerType) {
case "mouse" /* Input.PointerType.Mouse */:
case "pen" /* Input.PointerType.Pen */:
// TODO: Implement width and height when available.
return this.#context.cdpTarget.cdpClient.sendCommand('Input.dispatchMouseEvent', {
type: 'mouseReleased',
x,
y,
modifiers,
button: getCdpButton(button),
buttons: source.buttons,
clickCount: source.getClickCount(button),
pointerType,
});
case "touch" /* Input.PointerType.Touch */:
return this.#context.cdpTarget.cdpClient.sendCommand('Input.dispatchTouchEvent', {
type: 'touchEnd',
touchPoints: [
{
x,
y,
id: source.pointerId,
},
],
modifiers,
});
}
// --- Platform-specific code ends here ---
}
async #dispatchPointerMoveAction(source, keyState, action) {
const { x: startX, y: startY, subtype: pointerType } = source;
const { width, height, pressure, twist, tangentialPressure, x: offsetX, y: offsetY, origin = 'viewport', duration = this.#tickDuration, } = action;
const { tiltX, tiltY } = getTilt(action);
const { targetX, targetY } = await this.#getCoordinateFromOrigin(origin, offsetX, offsetY, startX, startY);
if (targetX < 0 || targetY < 0) {
throw new protocol_js_1.MoveTargetOutOfBoundsException(`Cannot move beyond viewport (x: ${targetX}, y: ${targetY})`);
}
let last;
do {
const ratio = duration > 0 ? (performance.now() - this.#tickStart) / duration : 1;
last = ratio >= 1;
let x;
let y;
if (last) {
x = targetX;
y = targetY;
}
else {
x = Math.round(ratio * (targetX - startX) + startX);
y = Math.round(ratio * (targetY - startY) + startY);
}
if (source.x !== x || source.y !== y) {
// --- Platform-specific code begins here ---
const { modifiers } = keyState;
switch (pointerType) {
case "mouse" /* Input.PointerType.Mouse */:
// TODO: Implement width and height when available.
await this.#context.cdpTarget.cdpClient.sendCommand('Input.dispatchMouseEvent', {
type: 'mouseMoved',
x,
y,
modifiers,
clickCount: 0,
button: getCdpButton(source.pressed.values().next().value ?? 5),
buttons: source.buttons,
pointerType,
tangentialPressure,
tiltX,
tiltY,
twist,
force: pressure,
});
break;
case "pen" /* Input.PointerType.Pen */:
if (source.pressed.size !== 0) {
// TODO: Implement width and height when available.
await this.#context.cdpTarget.cdpClient.sendCommand('Input.dispatchMouseEvent', {
type: 'mouseMoved',
x,
y,
modifiers,
clickCount: 0,
button: getCdpButton(source.pressed.values().next().value ?? 5),
buttons: source.buttons,
pointerType,
tangentialPressure,
tiltX,
tiltY,
twist,
force: pressure,
});
}
break;
case "touch" /* Input.PointerType.Touch */:
if (source.pressed.size !== 0) {
await this.#context.cdpTarget.cdpClient.sendCommand('Input.dispatchTouchEvent', {
type: 'touchMove',
touchPoints: [
{
x,
y,
...getRadii(width ?? 1, height ?? 1),
tangentialPressure,
tiltX,
tiltY,
twist,
force: pressure,
id: source.pointerId,
},
],
modifiers,
});
}
break;
}
// --- Platform-specific code ends here ---
source.x = x;
source.y = y;
}
} while (!last);
}
async #getCoordinateFromOrigin(origin, offsetX, offsetY, startX, startY) {
let targetX;
let targetY;
switch (origin) {
case 'viewport':
targetX = offsetX;
targetY = offsetY;
break;
case 'pointer':
targetX = startX + offsetX;
targetY = startY + offsetY;
break;
default: {
const { x: posX, y: posY } = await getElementCenter(this.#context, origin.element);
// SAFETY: These can never be special numbers.
targetX = posX + offsetX;
targetY = posY + offsetY;
break;
}
}
return { targetX, targetY };
}
async #dispatchScrollAction(_source, keyState, action) {
const { deltaX: targetDeltaX, deltaY: targetDeltaY, x: offsetX, y: offsetY, origin = 'viewport', duration = this.#tickDuration, } = action;
if (origin === 'pointer') {
throw new protocol_js_1.InvalidArgumentException('"pointer" origin is invalid for scrolling.');
}
const { targetX, targetY } = await this.#getCoordinateFromOrigin(origin, offsetX, offsetY, 0, 0);
if (targetX < 0 || targetY < 0) {
throw new protocol_js_1.MoveTargetOutOfBoundsException(`Cannot move beyond viewport (x: ${targetX}, y: ${targetY})`);
}
let currentDeltaX = 0;
let currentDeltaY = 0;
let last;
do {
const ratio = duration > 0 ? (performance.now() - this.#tickStart) / duration : 1;
last = ratio >= 1;
let deltaX;
let deltaY;
if (last) {
deltaX = targetDeltaX - currentDeltaX;
deltaY = targetDeltaY - currentDeltaY;
}
else {
deltaX = Math.round(ratio * targetDeltaX - currentDeltaX);
deltaY = Math.round(ratio * targetDeltaY - currentDeltaY);
}
if (deltaX !== 0 || deltaY !== 0) {
// --- Platform-specific code begins here ---
const { modifiers } = keyState;
await this.#context.cdpTarget.cdpClient.sendCommand('Input.dispatchMouseEvent', {
type: 'mouseWheel',
deltaX,
deltaY,
x: targetX,
y: targetY,
modifiers,
});
// --- Platform-specific code ends here ---
currentDeltaX += deltaX;
currentDeltaY += deltaY;
}
} while (!last);
}
async #dispatchKeyDownAction(source, action) {
if ([...action.value].length > 1) {
throw new protocol_js_1.InvalidArgumentException(`Invalid key value: ${action.value}`);
}
const rawKey = action.value;
const key = (0, keyUtils_js_1.getNormalizedKey)(rawKey);
const repeat = source.pressed.has(key);
const code = (0, keyUtils_js_1.getKeyCode)(rawKey);
const location = (0, keyUtils_js_1.getKeyLocation)(rawKey);
switch (key) {
case 'Alt':
source.alt = true;
break;
case 'Shift':
source.shift = true;
break;
case 'Control':
source.ctrl = true;
break;
case 'Meta':
source.meta = true;
break;
}
source.pressed.add(key);
const { modifiers } = source;
// --- Platform-specific code begins here ---
// The spread is a little hack so JS gives us an array of unicode characters
// to measure.
const unmodifiedText = getKeyEventUnmodifiedText(key, source);
const text = getKeyEventText(code ?? '', source) ?? unmodifiedText;
let command;
// The following commands need to be declared because Chromium doesn't
// handle them. See
// https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:third_party/blink/renderer/core/editing/editing_behavior.cc;l=169;drc=b8143cf1dfd24842890fcd831c4f5d909bef4fc4;bpv=0;bpt=1.
if (this.#isMacOS && source.meta) {
switch (code) {
case 'KeyA':
command = 'SelectAll';
break;
case 'KeyC':
command = 'Copy';
break;
case 'KeyV':
command = source.shift ? 'PasteAndMatchStyle' : 'Paste';
break;
case 'KeyX':
command = 'Cut';
break;
case 'KeyZ':
command = source.shift ? 'Redo' : 'Undo';
break;
default:
// Intentionally empty.
}
}
const promises = [
this.#context.cdpTarget.cdpClient.sendCommand('Input.dispatchKeyEvent', {
type: text ? 'keyDown' : 'rawKeyDown',
windowsVirtualKeyCode: USKeyboardLayout_js_1.KeyToKeyCode[key],
key,
code,
text,
unmodifiedText,
autoRepeat: repeat,
isSystemKey: source.alt || undefined,
location: location < 3 ? location : undefined,
isKeypad: location === 3,
modifiers,
commands: command ? [command] : undefined,
}),
];
// Drag cancelling happens on escape.
if (key === 'Escape') {
if (!source.alt &&
((this.#isMacOS && !source.ctrl && !source.meta) || !this.#isMacOS)) {
promises.push(this.#context.cdpTarget.cdpClient.sendCommand('Input.cancelDragging'));
}
}
await Promise.all(promises);
// --- Platform-specific code ends here ---
}
#dispatchKeyUpAction(source, action) {
if ([...action.value].length > 1) {
throw new protocol_js_1.InvalidArgumentException(`Invalid key value: ${action.value}`);
}
const rawKey = action.value;
const key = (0, keyUtils_js_1.getNormalizedKey)(rawKey);
if (!source.pressed.has(key)) {
return;
}
const code = (0, keyUtils_js_1.getKeyCode)(rawKey);
const location = (0, keyUtils_js_1.getKeyLocation)(rawKey);
switch (key) {
case 'Alt':
source.alt = false;
break;
case 'Shift':
source.shift = false;
break;
case 'Control':
source.ctrl = false;
break;
case 'Meta':
source.meta = false;
break;
}
source.pressed.delete(key);
const { modifiers } = source;
// --- Platform-specific code begins here ---
// The spread is a little hack so JS gives us an array of unicode characters
// to measure.
const unmodifiedText = getKeyEventUnmodifiedText(key, source);
const text = getKeyEventText(code ?? '', source) ?? unmodifiedText;
return this.#context.cdpTarget.cdpClient.sendCommand('Input.dispatchKeyEvent', {
type: 'keyUp',
windowsVirtualKeyCode: USKeyboardLayout_js_1.KeyToKeyCode[key],
key,
code,
text,
unmodifiedText,
location: location < 3 ? location : undefined,
isSystemKey: source.alt || undefined,
isKeypad: location === 3,
modifiers,
});
// --- Platform-specific code ends here ---
}
}
exports.ActionDispatcher = ActionDispatcher;
const getKeyEventUnmodifiedText = (key, source) => {
if (key === 'Enter') {
return '\r';
}
return [...key].length === 1
? source.shift
? key.toLocaleUpperCase('en-US')
: key
: undefined;
};
const getKeyEventText = (code, source) => {
if (source.ctrl) {
switch (code) {
case 'Digit2':
if (source.shift) {
return '\x00';
}
break;
case 'KeyA':
return '\x01';
case 'KeyB':
return '\x02';
case 'KeyC':
return '\x03';
case 'KeyD':
return '\x04';
case 'KeyE':
return '\x05';
case 'KeyF':
return '\x06';
case 'KeyG':
return '\x07';
case 'KeyH':
return '\x08';
case 'KeyI':
return '\x09';
case 'KeyJ':
return '\x0A';
case 'KeyK':
return '\x0B';
case 'KeyL':
return '\x0C';
case 'KeyM':
return '\x0D';
case 'KeyN':
return '\x0E';
case 'KeyO':
return '\x0F';
case 'KeyP':
return '\x10';
case 'KeyQ':
return '\x11';
case 'KeyR':
return '\x12';
case 'KeyS':
return '\x13';
case 'KeyT':
return '\x14';
case 'KeyU':
return '\x15';
case 'KeyV':
return '\x16';
case 'KeyW':
return '\x17';
case 'KeyX':
return '\x18';
case 'KeyY':
return '\x19';
case 'KeyZ':
return '\x1A';
case 'BracketLeft':
return '\x1B';
case 'Backslash':
return '\x1C';
case 'BracketRight':
return '\x1D';
case 'Digit6':
if (source.shift) {
return '\x1E';
}
break;
case 'Minus':
return '\x1F';
}
return '';
}
if (source.alt) {
return '';
}
return;
};
function getCdpButton(button) {
switch (button) {
case 0:
return 'left';
case 1:
return 'middle';
case 2:
return 'right';
case 3:
return 'back';
case 4:
return 'forward';
default:
return 'none';
}
}
function getTilt(action) {
// https://w3c.github.io/pointerevents/#converting-between-tiltx-tilty-and-altitudeangle-azimuthangle
const altitudeAngle = action.altitudeAngle ?? 0;
const azimuthAngle = action.azimuthAngle ?? 0;
let tiltXRadians = 0;
let tiltYRadians = 0;
if (altitudeAngle === 0) {
// the pen is in the X-Y plane
if (azimuthAngle === 0 || azimuthAngle === 2 * Math.PI) {
// pen is on positive X axis
tiltXRadians = Math.PI / 2;
}
if (azimuthAngle === Math.PI / 2) {
// pen is on positive Y axis
tiltYRadians = Math.PI / 2;
}
if (azimuthAngle === Math.PI) {
// pen is on negative X axis
tiltXRadians = -Math.PI / 2;
}
if (azimuthAngle === (3 * Math.PI) / 2) {
// pen is on negative Y axis
tiltYRadians = -Math.PI / 2;
}
if (azimuthAngle > 0 && azimuthAngle < Math.PI / 2) {
tiltXRadians = Math.PI / 2;
tiltYRadians = Math.PI / 2;
}
if (azimuthAngle > Math.PI / 2 && azimuthAngle < Math.PI) {
tiltXRadians = -Math.PI / 2;
tiltYRadians = Math.PI / 2;
}
if (azimuthAngle > Math.PI && azimuthAngle < (3 * Math.PI) / 2) {
tiltXRadians = -Math.PI / 2;
tiltYRadians = -Math.PI / 2;
}
if (azimuthAngle > (3 * Math.PI) / 2 && azimuthAngle < 2 * Math.PI) {
tiltXRadians = Math.PI / 2;
tiltYRadians = -Math.PI / 2;
}
}
if (altitudeAngle !== 0) {
const tanAlt = Math.tan(altitudeAngle);
tiltXRadians = Math.atan(Math.cos(azimuthAngle) / tanAlt);
tiltYRadians = Math.atan(Math.sin(azimuthAngle) / tanAlt);
}
const factor = 180 / Math.PI;
return {
tiltX: Math.round(tiltXRadians * factor),
tiltY: Math.round(tiltYRadians * factor),
};
}
function getRadii(width, height) {
return {
radiusX: width ? width / 2 : 0.5,
radiusY: height ? height / 2 : 0.5,
};
}
//# sourceMappingURL=ActionDispatcher.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,22 @@
/**
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Input } from '../../../protocol/protocol.js';
export type ActionOption = ActionOptionFor<Input.NoneSourceAction | Input.KeySourceAction | Input.PointerSourceAction | Input.WheelSourceAction>;
export interface ActionOptionFor<A> {
id: string;
action: A;
}

View File

@@ -0,0 +1,19 @@
"use strict";
/**
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=ActionOption.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ActionOption.js","sourceRoot":"","sources":["../../../../../src/bidiMapper/domains/input/ActionOption.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;GAeG"}

View File

@@ -0,0 +1,10 @@
import { Input, type EmptyResult } from '../../../protocol/protocol.js';
import type { BrowsingContextStorage } from '../context/BrowsingContextStorage.js';
import type { RealmStorage } from '../script/RealmStorage.js';
export declare class InputProcessor {
#private;
constructor(browsingContextStorage: BrowsingContextStorage, realmStorage: RealmStorage);
performActions(params: Input.PerformActionsParameters): Promise<EmptyResult>;
releaseActions(params: Input.ReleaseActionsParameters): Promise<EmptyResult>;
setFiles(params: Input.SetFilesParameters): Promise<EmptyResult>;
}

View File

@@ -0,0 +1,153 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.InputProcessor = void 0;
/*
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const protocol_js_1 = require("../../../protocol/protocol.js");
const assert_js_1 = require("../../../utils/assert.js");
const ActionDispatcher_js_1 = require("../input/ActionDispatcher.js");
const InputStateManager_js_1 = require("../input/InputStateManager.js");
class InputProcessor {
#browsingContextStorage;
#realmStorage;
#inputStateManager = new InputStateManager_js_1.InputStateManager();
constructor(browsingContextStorage, realmStorage) {
this.#browsingContextStorage = browsingContextStorage;
this.#realmStorage = realmStorage;
}
async performActions(params) {
const context = this.#browsingContextStorage.getContext(params.context);
const inputState = this.#inputStateManager.get(context.top);
const actionsByTick = this.#getActionsByTick(params, inputState);
const dispatcher = new ActionDispatcher_js_1.ActionDispatcher(inputState, context, await ActionDispatcher_js_1.ActionDispatcher.isMacOS(context).catch(() => false));
await dispatcher.dispatchActions(actionsByTick);
return {};
}
async releaseActions(params) {
const context = this.#browsingContextStorage.getContext(params.context);
const topContext = context.top;
const inputState = this.#inputStateManager.get(topContext);
const dispatcher = new ActionDispatcher_js_1.ActionDispatcher(inputState, context, await ActionDispatcher_js_1.ActionDispatcher.isMacOS(context).catch(() => false));
await dispatcher.dispatchTickActions(inputState.cancelList.reverse());
this.#inputStateManager.delete(topContext);
return {};
}
async setFiles(params) {
const realm = this.#realmStorage.findRealm({
browsingContextId: params.context,
});
if (realm === undefined) {
throw new protocol_js_1.NoSuchFrameException(`Could not find browsingContext ${params.context}`);
}
let isFileInput;
try {
const result = await realm.callFunction(String(function getFiles() {
return (this instanceof HTMLInputElement &&
this.type === 'file' &&
!this.disabled);
}), params.element, [], false, "none" /* Script.ResultOwnership.None */, {}, false);
(0, assert_js_1.assert)(result.type === 'success');
(0, assert_js_1.assert)(result.result.type === 'boolean');
isFileInput = result.result.value;
}
catch {
throw new protocol_js_1.NoSuchElementException(`Could not find element ${params.element.sharedId}`);
}
if (!isFileInput) {
throw new protocol_js_1.UnableToSetFileInputException(`Element ${params.element.sharedId} is not a mutable file input.`);
}
// Our goal here is to iterate over the input element files and get their
// file paths.
const paths = [];
for (let i = 0; i < params.files.length; ++i) {
const result = await realm.callFunction(String(function getFiles(index) {
if (!this.files) {
// We use `null` because `item` also returns null.
return null;
}
return this.files.item(index);
}), params.element, [{ type: 'number', value: 0 }], false, "root" /* Script.ResultOwnership.Root */, {}, false);
(0, assert_js_1.assert)(result.type === 'success');
if (result.result.type !== 'object') {
break;
}
const { handle } = result.result;
(0, assert_js_1.assert)(handle !== undefined);
const { path } = await realm.cdpClient.sendCommand('DOM.getFileInfo', {
objectId: handle,
});
paths.push(path);
// Cleanup the handle.
void realm.disown(handle).catch(undefined);
}
paths.sort();
// We create a new array so we preserve the order of the original files.
const sortedFiles = [...params.files].sort();
if (paths.length !== params.files.length ||
sortedFiles.some((path, index) => {
return paths[index] !== path;
})) {
const { objectId } = await realm.deserializeForCdp(params.element);
// This cannot throw since this was just used in `callFunction` above.
(0, assert_js_1.assert)(objectId !== undefined);
await realm.cdpClient.sendCommand('DOM.setFileInputFiles', {
files: params.files,
objectId,
});
}
else {
// XXX: We should dispatch a trusted event.
await realm.callFunction(String(function dispatchEvent() {
this.dispatchEvent(new Event('cancel', {
bubbles: true,
}));
}), params.element, [], false, "none" /* Script.ResultOwnership.None */, {}, false);
}
return {};
}
#getActionsByTick(params, inputState) {
const actionsByTick = [];
for (const action of params.actions) {
switch (action.type) {
case "pointer" /* SourceType.Pointer */: {
action.parameters ??= { pointerType: "mouse" /* Input.PointerType.Mouse */ };
action.parameters.pointerType ??= "mouse" /* Input.PointerType.Mouse */;
const source = inputState.getOrCreate(action.id, "pointer" /* SourceType.Pointer */, action.parameters.pointerType);
if (source.subtype !== action.parameters.pointerType) {
throw new protocol_js_1.InvalidArgumentException(`Expected input source ${action.id} to be ${source.subtype}; got ${action.parameters.pointerType}.`);
}
break;
}
default:
inputState.getOrCreate(action.id, action.type);
}
const actions = action.actions.map((item) => ({
id: action.id,
action: item,
}));
for (let i = 0; i < actions.length; i++) {
if (actionsByTick.length === i) {
actionsByTick.push([]);
}
actionsByTick[i].push(actions[i]);
}
}
return actionsByTick;
}
}
exports.InputProcessor = InputProcessor;
//# sourceMappingURL=InputProcessor.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,69 @@
/**
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Input } from '../../../protocol/protocol.js';
export declare const enum SourceType {
Key = "key",
Pointer = "pointer",
Wheel = "wheel",
None = "none"
}
export declare class NoneSource {
type: SourceType.None;
}
export declare class KeySource {
#private;
type: SourceType.Key;
pressed: Set<string>;
get modifiers(): number;
get alt(): boolean;
set alt(value: boolean);
get ctrl(): boolean;
set ctrl(value: boolean);
get meta(): boolean;
set meta(value: boolean);
get shift(): boolean;
set shift(value: boolean);
}
export declare class PointerSource {
#private;
type: SourceType.Pointer;
subtype: Input.PointerType;
pointerId: number;
pressed: Set<number>;
x: number;
y: number;
constructor(id: number, subtype: Input.PointerType);
get buttons(): number;
static ClickContext: {
new (x: number, y: number, time: number): {
count: number;
"__#89102@#x": number;
"__#89102@#y": number;
"__#89102@#time": number;
compare(context: any): boolean;
};
"__#89102@#DOUBLE_CLICK_TIME_MS": number;
"__#89102@#MAX_DOUBLE_CLICK_RADIUS": number;
};
setClickCount(button: number, context: InstanceType<typeof PointerSource.ClickContext>): number;
getClickCount(button: number): number;
}
export declare class WheelSource {
type: SourceType.Wheel;
}
export type InputSource = NoneSource | KeySource | PointerSource | WheelSource;
export type InputSourceFor<Type extends SourceType> = Type extends SourceType.Key ? KeySource : Type extends SourceType.Pointer ? PointerSource : Type extends SourceType.Wheel ? WheelSource : NoneSource;

View File

@@ -0,0 +1,148 @@
"use strict";
/**
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.WheelSource = exports.PointerSource = exports.KeySource = exports.NoneSource = void 0;
class NoneSource {
type = "none" /* SourceType.None */;
}
exports.NoneSource = NoneSource;
class KeySource {
type = "key" /* SourceType.Key */;
pressed = new Set();
// This is a bitfield that matches the modifiers parameter of
// https://chromedevtools.github.io/devtools-protocol/tot/Input/#method-dispatchKeyEvent
#modifiers = 0;
get modifiers() {
return this.#modifiers;
}
get alt() {
return (this.#modifiers & 1) === 1;
}
set alt(value) {
this.#setModifier(value, 1);
}
get ctrl() {
return (this.#modifiers & 2) === 2;
}
set ctrl(value) {
this.#setModifier(value, 2);
}
get meta() {
return (this.#modifiers & 4) === 4;
}
set meta(value) {
this.#setModifier(value, 4);
}
get shift() {
return (this.#modifiers & 8) === 8;
}
set shift(value) {
this.#setModifier(value, 8);
}
#setModifier(value, bit) {
if (value) {
this.#modifiers |= bit;
}
else {
this.#modifiers &= ~bit;
}
}
}
exports.KeySource = KeySource;
class PointerSource {
type = "pointer" /* SourceType.Pointer */;
subtype;
pointerId;
pressed = new Set();
x = 0;
y = 0;
constructor(id, subtype) {
this.pointerId = id;
this.subtype = subtype;
}
// This is a bitfield that matches the buttons parameter of
// https://chromedevtools.github.io/devtools-protocol/tot/Input/#method-dispatchMouseEvent
get buttons() {
let buttons = 0;
for (const button of this.pressed) {
switch (button) {
case 0:
buttons |= 1;
break;
case 1:
buttons |= 4;
break;
case 2:
buttons |= 2;
break;
case 3:
buttons |= 8;
break;
case 4:
buttons |= 16;
break;
}
}
return buttons;
}
// --- Platform-specific code starts here ---
// Input.dispatchMouseEvent doesn't know the concept of double click, so we
// need to create the logic, similar to how it's done for OSes:
// https://source.chromium.org/chromium/chromium/src/+/refs/heads/main:ui/events/event.cc;l=479
static ClickContext = class ClickContext {
static #DOUBLE_CLICK_TIME_MS = 500;
static #MAX_DOUBLE_CLICK_RADIUS = 2;
count = 0;
#x;
#y;
#time;
constructor(x, y, time) {
this.#x = x;
this.#y = y;
this.#time = time;
}
compare(context) {
return (
// The click needs to be within a certain amount of ms.
context.#time - this.#time > ClickContext.#DOUBLE_CLICK_TIME_MS ||
// The click needs to be within a certain square radius.
Math.abs(context.#x - this.#x) >
ClickContext.#MAX_DOUBLE_CLICK_RADIUS ||
Math.abs(context.#y - this.#y) > ClickContext.#MAX_DOUBLE_CLICK_RADIUS);
}
};
#clickContexts = new Map();
setClickCount(button, context) {
let storedContext = this.#clickContexts.get(button);
if (!storedContext || storedContext.compare(context)) {
storedContext = context;
}
++storedContext.count;
this.#clickContexts.set(button, storedContext);
return storedContext.count;
}
getClickCount(button) {
return this.#clickContexts.get(button)?.count ?? 0;
}
}
exports.PointerSource = PointerSource;
class WheelSource {
type = "wheel" /* SourceType.Wheel */;
}
exports.WheelSource = WheelSource;
//# sourceMappingURL=InputSource.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"InputSource.js","sourceRoot":"","sources":["../../../../../src/bidiMapper/domains/input/InputSource.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,29 @@
/**
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Input } from '../../../protocol/protocol.js';
import { Mutex } from '../../../utils/Mutex.js';
import type { ActionOption } from './ActionOption.js';
import { KeySource, PointerSource, SourceType, type InputSource, type InputSourceFor } from './InputSource.js';
export declare class InputState {
#private;
cancelList: ActionOption[];
getOrCreate(id: string, type: SourceType.Pointer, subtype: Input.PointerType): PointerSource;
getOrCreate<Type extends SourceType>(id: string, type: Type): InputSourceFor<Type>;
get(id: string): InputSource;
getGlobalKeyState(): KeySource;
get queue(): Mutex;
}

View File

@@ -0,0 +1,93 @@
"use strict";
/**
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.InputState = void 0;
const protocol_js_1 = require("../../../protocol/protocol.js");
const Mutex_js_1 = require("../../../utils/Mutex.js");
const InputSource_js_1 = require("./InputSource.js");
class InputState {
cancelList = [];
#sources = new Map();
#mutex = new Mutex_js_1.Mutex();
getOrCreate(id, type, subtype) {
let source = this.#sources.get(id);
if (!source) {
switch (type) {
case "none" /* SourceType.None */:
source = new InputSource_js_1.NoneSource();
break;
case "key" /* SourceType.Key */:
source = new InputSource_js_1.KeySource();
break;
case "pointer" /* SourceType.Pointer */: {
let pointerId = subtype === "mouse" /* Input.PointerType.Mouse */ ? 0 : 2;
const pointerIds = new Set();
for (const [, source] of this.#sources) {
if (source.type === "pointer" /* SourceType.Pointer */) {
pointerIds.add(source.pointerId);
}
}
while (pointerIds.has(pointerId)) {
++pointerId;
}
source = new InputSource_js_1.PointerSource(pointerId, subtype);
break;
}
case "wheel" /* SourceType.Wheel */:
source = new InputSource_js_1.WheelSource();
break;
default:
throw new protocol_js_1.InvalidArgumentException(`Expected "${"none" /* SourceType.None */}", "${"key" /* SourceType.Key */}", "${"pointer" /* SourceType.Pointer */}", or "${"wheel" /* SourceType.Wheel */}". Found unknown source type ${type}.`);
}
this.#sources.set(id, source);
return source;
}
if (source.type !== type) {
throw new protocol_js_1.InvalidArgumentException(`Input source type of ${id} is ${source.type}, but received ${type}.`);
}
return source;
}
get(id) {
const source = this.#sources.get(id);
if (!source) {
throw new protocol_js_1.UnknownErrorException(`Internal error.`);
}
return source;
}
getGlobalKeyState() {
const state = new InputSource_js_1.KeySource();
for (const [, source] of this.#sources) {
if (source.type !== "key" /* SourceType.Key */) {
continue;
}
for (const pressed of source.pressed) {
state.pressed.add(pressed);
}
state.alt ||= source.alt;
state.ctrl ||= source.ctrl;
state.meta ||= source.meta;
state.shift ||= source.shift;
}
return state;
}
get queue() {
return this.#mutex;
}
}
exports.InputState = InputState;
//# sourceMappingURL=InputState.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"InputState.js","sourceRoot":"","sources":["../../../../../src/bidiMapper/domains/input/InputState.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,21 @@
/**
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { BrowsingContextImpl } from '../context/BrowsingContextImpl.js';
import { InputState } from './InputState.js';
export declare class InputStateManager extends WeakMap<BrowsingContextImpl, InputState> {
get(context: BrowsingContextImpl): InputState;
}

View File

@@ -0,0 +1,34 @@
"use strict";
/**
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.InputStateManager = void 0;
const assert_js_1 = require("../../../utils/assert.js");
const InputState_js_1 = require("./InputState.js");
// We use a weak map here as specified here:
// https://www.w3.org/TR/webdriver/#dfn-browsing-context-input-state-map
class InputStateManager extends WeakMap {
get(context) {
(0, assert_js_1.assert)(context.isTopLevelContext());
if (!this.has(context)) {
this.set(context, new InputState_js_1.InputState());
}
return super.get(context);
}
}
exports.InputStateManager = InputStateManager;
//# sourceMappingURL=InputStateManager.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"InputStateManager.js","sourceRoot":"","sources":["../../../../../src/bidiMapper/domains/input/InputStateManager.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;GAeG;;;AAEH,wDAAgD;AAGhD,mDAA2C;AAE3C,4CAA4C;AAC5C,wEAAwE;AACxE,MAAa,iBAAkB,SAAQ,OAGtC;IACU,GAAG,CAAC,OAA4B;QACvC,IAAA,kBAAM,EAAC,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC;QAEpC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YACvB,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,0BAAU,EAAE,CAAC,CAAC;QACtC,CAAC;QAED,OAAO,KAAK,CAAC,GAAG,CAAC,OAAO,CAAE,CAAC;IAC7B,CAAC;CACF;AAbD,8CAaC"}

View File

@@ -0,0 +1,17 @@
/**
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export declare const KeyToKeyCode: Record<string, number | undefined>;

View File

@@ -0,0 +1,274 @@
"use strict";
/**
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.KeyToKeyCode = void 0;
// TODO: Remove this once https://crrev.com/c/4548290 is stably in Chromium.
// `Input.dispatchKeyboardEvent` will automatically handle these conversions.
exports.KeyToKeyCode = {
'0': 48,
'1': 49,
'2': 50,
'3': 51,
'4': 52,
'5': 53,
'6': 54,
'7': 55,
'8': 56,
'9': 57,
Abort: 3,
Help: 6,
Backspace: 8,
Tab: 9,
Numpad5: 12,
NumpadEnter: 13,
Enter: 13,
'\\r': 13,
'\\n': 13,
ShiftLeft: 16,
ShiftRight: 16,
ControlLeft: 17,
ControlRight: 17,
AltLeft: 18,
AltRight: 18,
Pause: 19,
CapsLock: 20,
Escape: 27,
Convert: 28,
NonConvert: 29,
Space: 32,
Numpad9: 33,
PageUp: 33,
Numpad3: 34,
PageDown: 34,
End: 35,
Numpad1: 35,
Home: 36,
Numpad7: 36,
ArrowLeft: 37,
Numpad4: 37,
Numpad8: 38,
ArrowUp: 38,
ArrowRight: 39,
Numpad6: 39,
Numpad2: 40,
ArrowDown: 40,
Select: 41,
Open: 43,
PrintScreen: 44,
Insert: 45,
Numpad0: 45,
Delete: 46,
NumpadDecimal: 46,
Digit0: 48,
Digit1: 49,
Digit2: 50,
Digit3: 51,
Digit4: 52,
Digit5: 53,
Digit6: 54,
Digit7: 55,
Digit8: 56,
Digit9: 57,
KeyA: 65,
KeyB: 66,
KeyC: 67,
KeyD: 68,
KeyE: 69,
KeyF: 70,
KeyG: 71,
KeyH: 72,
KeyI: 73,
KeyJ: 74,
KeyK: 75,
KeyL: 76,
KeyM: 77,
KeyN: 78,
KeyO: 79,
KeyP: 80,
KeyQ: 81,
KeyR: 82,
KeyS: 83,
KeyT: 84,
KeyU: 85,
KeyV: 86,
KeyW: 87,
KeyX: 88,
KeyY: 89,
KeyZ: 90,
MetaLeft: 91,
MetaRight: 92,
ContextMenu: 93,
NumpadMultiply: 106,
NumpadAdd: 107,
NumpadSubtract: 109,
NumpadDivide: 111,
F1: 112,
F2: 113,
F3: 114,
F4: 115,
F5: 116,
F6: 117,
F7: 118,
F8: 119,
F9: 120,
F10: 121,
F11: 122,
F12: 123,
F13: 124,
F14: 125,
F15: 126,
F16: 127,
F17: 128,
F18: 129,
F19: 130,
F20: 131,
F21: 132,
F22: 133,
F23: 134,
F24: 135,
NumLock: 144,
ScrollLock: 145,
AudioVolumeMute: 173,
AudioVolumeDown: 174,
AudioVolumeUp: 175,
MediaTrackNext: 176,
MediaTrackPrevious: 177,
MediaStop: 178,
MediaPlayPause: 179,
Semicolon: 186,
Equal: 187,
NumpadEqual: 187,
Comma: 188,
Minus: 189,
Period: 190,
Slash: 191,
Backquote: 192,
BracketLeft: 219,
Backslash: 220,
BracketRight: 221,
Quote: 222,
AltGraph: 225,
Props: 247,
Cancel: 3,
Clear: 12,
Shift: 16,
Control: 17,
Alt: 18,
Accept: 30,
ModeChange: 31,
' ': 32,
Print: 42,
Execute: 43,
'\\u0000': 46,
a: 65,
b: 66,
c: 67,
d: 68,
e: 69,
f: 70,
g: 71,
h: 72,
i: 73,
j: 74,
k: 75,
l: 76,
m: 77,
n: 78,
o: 79,
p: 80,
q: 81,
r: 82,
s: 83,
t: 84,
u: 85,
v: 86,
w: 87,
x: 88,
y: 89,
z: 90,
Meta: 91,
'*': 106,
'+': 107,
'-': 109,
'/': 111,
';': 186,
'=': 187,
',': 188,
'.': 190,
'`': 192,
'[': 219,
'\\\\': 220,
']': 221,
"'": 222,
Attn: 246,
CrSel: 247,
ExSel: 248,
EraseEof: 249,
Play: 250,
ZoomOut: 251,
')': 48,
'!': 49,
'@': 50,
'#': 51,
$: 52,
'%': 53,
'^': 54,
'&': 55,
'(': 57,
A: 65,
B: 66,
C: 67,
D: 68,
E: 69,
F: 70,
G: 71,
H: 72,
I: 73,
J: 74,
K: 75,
L: 76,
M: 77,
N: 78,
O: 79,
P: 80,
Q: 81,
R: 82,
S: 83,
T: 84,
U: 85,
V: 86,
W: 87,
X: 88,
Y: 89,
Z: 90,
':': 186,
'<': 188,
_: 189,
'>': 190,
'?': 191,
'~': 192,
'{': 219,
'|': 220,
'}': 221,
'"': 222,
Camera: 44,
EndCall: 95,
VolumeDown: 182,
VolumeUp: 183,
};
//# sourceMappingURL=USKeyboardLayout.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,19 @@
/**
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export declare function getNormalizedKey(value: string): string;
export declare function getKeyCode(key: string): string | undefined;
export declare function getKeyLocation(key: string): 0 | 1 | 2 | 3;

View File

@@ -0,0 +1,476 @@
"use strict";
/**
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.getKeyLocation = exports.getKeyCode = exports.getNormalizedKey = void 0;
function getNormalizedKey(value) {
switch (value) {
case '\uE000':
return 'Unidentified';
case '\uE001':
return 'Cancel';
case '\uE002':
return 'Help';
case '\uE003':
return 'Backspace';
case '\uE004':
return 'Tab';
case '\uE005':
return 'Clear';
case '\uE006':
return 'Return';
case '\uE007':
return 'Enter';
case '\uE008':
return 'Shift';
case '\uE009':
return 'Control';
case '\uE00A':
return 'Alt';
case '\uE00B':
return 'Pause';
case '\uE00C':
return 'Escape';
case '\uE00D':
return ' ';
case '\uE00E':
return 'PageUp';
case '\uE00F':
return 'PageDown';
case '\uE010':
return 'End';
case '\uE011':
return 'Home';
case '\uE012':
return 'ArrowLeft';
case '\uE013':
return 'ArrowUp';
case '\uE014':
return 'ArrowRight';
case '\uE015':
return 'ArrowDown';
case '\uE016':
return 'Insert';
case '\uE017':
return 'Delete';
case '\uE018':
return ';';
case '\uE019':
return '=';
case '\uE01A':
return '0';
case '\uE01B':
return '1';
case '\uE01C':
return '2';
case '\uE01D':
return '3';
case '\uE01E':
return '4';
case '\uE01F':
return '5';
case '\uE020':
return '6';
case '\uE021':
return '7';
case '\uE022':
return '8';
case '\uE023':
return '9';
case '\uE024':
return '*';
case '\uE025':
return '+';
case '\uE026':
return ',';
case '\uE027':
return '-';
case '\uE028':
return '.';
case '\uE029':
return '/';
case '\uE031':
return 'F1';
case '\uE032':
return 'F2';
case '\uE033':
return 'F3';
case '\uE034':
return 'F4';
case '\uE035':
return 'F5';
case '\uE036':
return 'F6';
case '\uE037':
return 'F7';
case '\uE038':
return 'F8';
case '\uE039':
return 'F9';
case '\uE03A':
return 'F10';
case '\uE03B':
return 'F11';
case '\uE03C':
return 'F12';
case '\uE03D':
return 'Meta';
case '\uE040':
return 'ZenkakuHankaku';
case '\uE050':
return 'Shift';
case '\uE051':
return 'Control';
case '\uE052':
return 'Alt';
case '\uE053':
return 'Meta';
case '\uE054':
return 'PageUp';
case '\uE055':
return 'PageDown';
case '\uE056':
return 'End';
case '\uE057':
return 'Home';
case '\uE058':
return 'ArrowLeft';
case '\uE059':
return 'ArrowUp';
case '\uE05A':
return 'ArrowRight';
case '\uE05B':
return 'ArrowDown';
case '\uE05C':
return 'Insert';
case '\uE05D':
return 'Delete';
default:
return value;
}
}
exports.getNormalizedKey = getNormalizedKey;
function getKeyCode(key) {
switch (key) {
case '`':
case '~':
return 'Backquote';
case '\\':
case '|':
return 'Backslash';
case '\uE003':
return 'Backspace';
case '[':
case '{':
return 'BracketLeft';
case ']':
case '}':
return 'BracketRight';
case ',':
case '<':
return 'Comma';
case '0':
case ')':
return 'Digit0';
case '1':
case '!':
return 'Digit1';
case '2':
case '@':
return 'Digit2';
case '3':
case '#':
return 'Digit3';
case '4':
case '$':
return 'Digit4';
case '5':
case '%':
return 'Digit5';
case '6':
case '^':
return 'Digit6';
case '7':
case '&':
return 'Digit7';
case '8':
case '*':
return 'Digit8';
case '9':
case '(':
return 'Digit9';
case '=':
case '+':
return 'Equal';
case 'a':
case 'A':
return 'KeyA';
case 'b':
case 'B':
return 'KeyB';
case 'c':
case 'C':
return 'KeyC';
case 'd':
case 'D':
return 'KeyD';
case 'e':
case 'E':
return 'KeyE';
case 'f':
case 'F':
return 'KeyF';
case 'g':
case 'G':
return 'KeyG';
case 'h':
case 'H':
return 'KeyH';
case 'i':
case 'I':
return 'KeyI';
case 'j':
case 'J':
return 'KeyJ';
case 'k':
case 'K':
return 'KeyK';
case 'l':
case 'L':
return 'KeyL';
case 'm':
case 'M':
return 'KeyM';
case 'n':
case 'N':
return 'KeyN';
case 'o':
case 'O':
return 'KeyO';
case 'p':
case 'P':
return 'KeyP';
case 'q':
case 'Q':
return 'KeyQ';
case 'r':
case 'R':
return 'KeyR';
case 's':
case 'S':
return 'KeyS';
case 't':
case 'T':
return 'KeyT';
case 'u':
case 'U':
return 'KeyU';
case 'v':
case 'V':
return 'KeyV';
case 'w':
case 'W':
return 'KeyW';
case 'x':
case 'X':
return 'KeyX';
case 'y':
case 'Y':
return 'KeyY';
case 'z':
case 'Z':
return 'KeyZ';
case '-':
case '_':
return 'Minus';
case '.':
return 'Period';
case "'":
case '"':
return 'Quote';
case ';':
case ':':
return 'Semicolon';
case '/':
case '?':
return 'Slash';
case '\uE00A':
return 'AltLeft';
case '\uE052':
return 'AltRight';
case '\uE009':
return 'ControlLeft';
case '\uE051':
return 'ControlRight';
case '\uE006':
return 'Enter';
case '\uE03D':
return 'MetaLeft';
case '\uE053':
return 'MetaRight';
case '\uE008':
return 'ShiftLeft';
case '\uE050':
return 'ShiftRight';
case ' ':
case '\uE00D':
return 'Space';
case '\uE004':
return 'Tab';
case '\uE017':
return 'Delete';
case '\uE010':
return 'End';
case '\uE002':
return 'Help';
case '\uE011':
return 'Home';
case '\uE016':
return 'Insert';
case '\uE00F':
return 'PageDown';
case '\uE00E':
return 'PageUp';
case '\uE015':
return 'ArrowDown';
case '\uE012':
return 'ArrowLeft';
case '\uE014':
return 'ArrowRight';
case '\uE013':
return 'ArrowUp';
case '\uE00C':
return 'Escape';
case '\uE031':
return 'F1';
case '\uE032':
return 'F2';
case '\uE033':
return 'F3';
case '\uE034':
return 'F4';
case '\uE035':
return 'F5';
case '\uE036':
return 'F6';
case '\uE037':
return 'F7';
case '\uE038':
return 'F8';
case '\uE039':
return 'F9';
case '\uE03A':
return 'F10';
case '\uE03B':
return 'F11';
case '\uE03C':
return 'F12';
case '\uE01A':
case '\uE05C':
return 'Numpad0';
case '\uE01B':
case '\uE056':
return 'Numpad1';
case '\uE01C':
case '\uE05B':
return 'Numpad2';
case '\uE01D':
case '\uE055':
return 'Numpad3';
case '\uE01E':
case '\uE058':
return 'Numpad4';
case '\uE01F':
return 'Numpad5';
case '\uE020':
case '\uE05A':
return 'Numpad6';
case '\uE021':
case '\uE057':
return 'Numpad7';
case '\uE022':
case '\uE059':
return 'Numpad8';
case '\uE023':
case '\uE054':
return 'Numpad9';
case '\uE025':
return 'NumpadAdd';
case '\uE026':
return 'NumpadComma';
case '\uE028':
case '\uE05D':
return 'NumpadDecimal';
case '\uE029':
return 'NumpadDivide';
case '\uE007':
return 'NumpadEnter';
case '\uE024':
return 'NumpadMultiply';
case '\uE027':
return 'NumpadSubtract';
default:
return;
}
}
exports.getKeyCode = getKeyCode;
function getKeyLocation(key) {
switch (key) {
case '\uE007':
case '\uE008':
case '\uE009':
case '\uE00A':
case '\uE03D':
return 1;
case '\uE01A':
case '\uE01B':
case '\uE01C':
case '\uE01D':
case '\uE01E':
case '\uE01F':
case '\uE020':
case '\uE021':
case '\uE022':
case '\uE023':
case '\uE024':
case '\uE025':
case '\uE026':
case '\uE027':
case '\uE028':
case '\uE029':
case '\uE054':
case '\uE055':
case '\uE056':
case '\uE057':
case '\uE058':
case '\uE059':
case '\uE05A':
case '\uE05B':
case '\uE05C':
case '\uE05D':
return 3;
case '\uE050':
case '\uE051':
case '\uE052':
case '\uE053':
return 2;
default:
return 0;
}
}
exports.getKeyLocation = getKeyLocation;
//# sourceMappingURL=keyUtils.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,9 @@
import { type LoggerFn } from '../../../utils/log.js';
import type { CdpTarget } from '../context/CdpTarget.js';
import type { RealmStorage } from '../script/RealmStorage.js';
import type { EventManager } from '../session/EventManager.js';
export declare class LogManager {
#private;
private constructor();
static create(cdpTarget: CdpTarget, realmStorage: RealmStorage, eventManager: EventManager, logger?: LoggerFn): LogManager;
}

View File

@@ -0,0 +1,132 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LogManager = void 0;
const protocol_js_1 = require("../../../protocol/protocol.js");
const log_js_1 = require("../../../utils/log.js");
const logHelper_js_1 = require("./logHelper.js");
/** Converts CDP StackTrace object to BiDi StackTrace object. */
function getBidiStackTrace(cdpStackTrace) {
const stackFrames = cdpStackTrace?.callFrames.map((callFrame) => {
return {
columnNumber: callFrame.columnNumber,
functionName: callFrame.functionName,
lineNumber: callFrame.lineNumber,
url: callFrame.url,
};
});
return stackFrames ? { callFrames: stackFrames } : undefined;
}
function getLogLevel(consoleApiType) {
if (["error" /* Log.Level.Error */, 'assert'].includes(consoleApiType)) {
return "error" /* Log.Level.Error */;
}
if (["debug" /* Log.Level.Debug */, 'trace'].includes(consoleApiType)) {
return "debug" /* Log.Level.Debug */;
}
if (["warn" /* Log.Level.Warn */, 'warning'].includes(consoleApiType)) {
return "warn" /* Log.Level.Warn */;
}
return "info" /* Log.Level.Info */;
}
class LogManager {
#eventManager;
#realmStorage;
#cdpTarget;
#logger;
constructor(cdpTarget, realmStorage, eventManager, logger) {
this.#cdpTarget = cdpTarget;
this.#realmStorage = realmStorage;
this.#eventManager = eventManager;
this.#logger = logger;
}
static create(cdpTarget, realmStorage, eventManager, logger) {
const logManager = new LogManager(cdpTarget, realmStorage, eventManager, logger);
logManager.#initializeEntryAddedEventListener();
return logManager;
}
#initializeEntryAddedEventListener() {
this.#cdpTarget.cdpClient.on('Runtime.consoleAPICalled', (params) => {
// Try to find realm by `cdpSessionId` and `executionContextId`,
// if provided.
const realm = this.#realmStorage.findRealm({
cdpSessionId: this.#cdpTarget.cdpSessionId,
executionContextId: params.executionContextId,
});
if (realm === undefined) {
// Ignore exceptions not attached to any realm.
this.#logger?.(log_js_1.LogType.cdp, params);
return;
}
const argsPromise = realm === undefined
? Promise.resolve(params.args)
: // Properly serialize arguments if possible.
Promise.all(params.args.map((arg) => {
return realm.serializeCdpObject(arg, "none" /* Script.ResultOwnership.None */);
}));
for (const browsingContext of realm.associatedBrowsingContexts) {
this.#eventManager.registerPromiseEvent(argsPromise.then((args) => ({
kind: 'success',
value: {
type: 'event',
method: protocol_js_1.ChromiumBidi.Log.EventNames.LogEntryAdded,
params: {
level: getLogLevel(params.type),
source: realm.source,
text: (0, logHelper_js_1.getRemoteValuesText)(args, true),
timestamp: Math.round(params.timestamp),
stackTrace: getBidiStackTrace(params.stackTrace),
type: 'console',
// Console method is `warn`, not `warning`.
method: params.type === 'warning' ? 'warn' : params.type,
args,
},
},
})), browsingContext.id, protocol_js_1.ChromiumBidi.Log.EventNames.LogEntryAdded);
}
});
this.#cdpTarget.cdpClient.on('Runtime.exceptionThrown', (params) => {
// Try to find realm by `cdpSessionId` and `executionContextId`,
// if provided.
const realm = this.#realmStorage.findRealm({
cdpSessionId: this.#cdpTarget.cdpSessionId,
executionContextId: params.exceptionDetails.executionContextId,
});
if (realm === undefined) {
// Ignore exceptions not attached to any realm.
this.#logger?.(log_js_1.LogType.cdp, params);
return;
}
for (const browsingContext of realm.associatedBrowsingContexts) {
this.#eventManager.registerPromiseEvent(LogManager.#getExceptionText(params, realm).then((text) => ({
kind: 'success',
value: {
type: 'event',
method: protocol_js_1.ChromiumBidi.Log.EventNames.LogEntryAdded,
params: {
level: "error" /* Log.Level.Error */,
source: realm.source,
text,
timestamp: Math.round(params.timestamp),
stackTrace: getBidiStackTrace(params.exceptionDetails.stackTrace),
type: 'javascript',
},
},
})), browsingContext.id, protocol_js_1.ChromiumBidi.Log.EventNames.LogEntryAdded);
}
});
}
/**
* Try the best to get the exception text.
*/
static async #getExceptionText(params, realm) {
if (!params.exceptionDetails.exception) {
return params.exceptionDetails.text;
}
if (realm === undefined) {
return JSON.stringify(params.exceptionDetails.exception);
}
return await realm.stringifyObject(params.exceptionDetails.exception);
}
}
exports.LogManager = LogManager;
//# sourceMappingURL=LogManager.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"LogManager.js","sourceRoot":"","sources":["../../../../../src/bidiMapper/domains/log/LogManager.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,23 @@
/**
* Copyright 2022 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Script } from '../../../protocol/protocol.js';
/**
* @param args input remote values to be format printed
* @return parsed text of the remote values in specific format
*/
export declare function logMessageFormatter(args: Script.RemoteValue[]): string;
export declare function getRemoteValuesText(args: Script.RemoteValue[], formatText: boolean): string;

View File

@@ -0,0 +1,174 @@
"use strict";
/**
* Copyright 2022 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.getRemoteValuesText = exports.logMessageFormatter = void 0;
const assert_js_1 = require("../../../utils/assert.js");
const specifiers = ['%s', '%d', '%i', '%f', '%o', '%O', '%c'];
function isFormatSpecifier(str) {
return specifiers.some((spec) => str.includes(spec));
}
/**
* @param args input remote values to be format printed
* @return parsed text of the remote values in specific format
*/
function logMessageFormatter(args) {
let output = '';
const argFormat = args[0].value.toString();
const argValues = args.slice(1, undefined);
const tokens = argFormat.split(new RegExp(specifiers.map((spec) => `(${spec})`).join('|'), 'g'));
for (const token of tokens) {
if (token === undefined || token === '') {
continue;
}
if (isFormatSpecifier(token)) {
const arg = argValues.shift();
// raise an exception when less value is provided
(0, assert_js_1.assert)(arg, `Less value is provided: "${getRemoteValuesText(args, false)}"`);
if (token === '%s') {
output += stringFromArg(arg);
}
else if (token === '%d' || token === '%i') {
if (arg.type === 'bigint' ||
arg.type === 'number' ||
arg.type === 'string') {
output += parseInt(arg.value.toString(), 10);
}
else {
output += 'NaN';
}
}
else if (token === '%f') {
if (arg.type === 'bigint' ||
arg.type === 'number' ||
arg.type === 'string') {
output += parseFloat(arg.value.toString());
}
else {
output += 'NaN';
}
}
else {
// %o, %O, %c
output += toJson(arg);
}
}
else {
output += token;
}
}
// raise an exception when more value is provided
if (argValues.length > 0) {
throw new Error(`More value is provided: "${getRemoteValuesText(args, false)}"`);
}
return output;
}
exports.logMessageFormatter = logMessageFormatter;
/**
* @param arg input remote value to be parsed
* @return parsed text of the remote value
*
* input: {"type": "number", "value": 1}
* output: 1
*
* input: {"type": "string", "value": "abc"}
* output: "abc"
*
* input: {"type": "object", "value": [["id", {"type": "number", "value": 1}]]}
* output: '{"id": 1}'
*
* input: {"type": "object", "value": [["font-size", {"type": "string", "value": "20px"}]]}
* output: '{"font-size": "20px"}'
*/
function toJson(arg) {
// arg type validation
if (arg.type !== 'array' &&
arg.type !== 'bigint' &&
arg.type !== 'date' &&
arg.type !== 'number' &&
arg.type !== 'object' &&
arg.type !== 'string') {
return stringFromArg(arg);
}
if (arg.type === 'bigint') {
return `${arg.value.toString()}n`;
}
if (arg.type === 'number') {
return arg.value.toString();
}
if (['date', 'string'].includes(arg.type)) {
return JSON.stringify(arg.value);
}
if (arg.type === 'object') {
return `{${arg.value
.map((pair) => {
return `${JSON.stringify(pair[0])}:${toJson(pair[1])}`;
})
.join(',')}}`;
}
if (arg.type === 'array') {
return `[${arg.value?.map((val) => toJson(val)).join(',') ?? ''}]`;
}
// eslint-disable-next-line @typescript-eslint/no-base-to-string
throw Error(`Invalid value type: ${arg}`);
}
function stringFromArg(arg) {
if (!Object.hasOwn(arg, 'value')) {
return arg.type;
}
switch (arg.type) {
case 'string':
case 'number':
case 'boolean':
case 'bigint':
return String(arg.value);
case 'regexp':
return `/${arg.value.pattern}/${arg.value.flags ?? ''}`;
case 'date':
return new Date(arg.value).toString();
case 'object':
return `Object(${arg.value?.length ?? ''})`;
case 'array':
return `Array(${arg.value?.length ?? ''})`;
case 'map':
return `Map(${arg.value?.length})`;
case 'set':
return `Set(${arg.value?.length})`;
default:
return arg.type;
}
}
function getRemoteValuesText(args, formatText) {
const arg = args[0];
if (!arg) {
return '';
}
// if args[0] is a format specifier, format the args as output
if (arg.type === 'string' &&
isFormatSpecifier(arg.value.toString()) &&
formatText) {
return logMessageFormatter(args);
}
// if args[0] is not a format specifier, just join the args with \u0020 (unicode 'SPACE')
return args
.map((arg) => {
return stringFromArg(arg);
})
.join('\u0020');
}
exports.getRemoteValuesText = getRemoteValuesText;
//# sourceMappingURL=logHelper.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"logHelper.js","sourceRoot":"","sources":["../../../../../src/bidiMapper/domains/log/logHelper.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,11 @@
import type { CdpTarget } from '../context/CdpTarget.js';
import type { EventManager } from '../session/EventManager.js';
import type { NetworkStorage } from './NetworkStorage.js';
/** Maps 1:1 to CdpTarget. */
export declare class NetworkManager {
#private;
private constructor();
/** Returns the CDP Target associated with this NetworkManager instance. */
get cdpTarget(): CdpTarget;
static create(cdpTarget: CdpTarget, eventManager: EventManager, networkStorage: NetworkStorage): NetworkManager;
}

View File

@@ -0,0 +1,110 @@
"use strict";
/*
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.NetworkManager = void 0;
const NetworkRequest_js_1 = require("./NetworkRequest.js");
/** Maps 1:1 to CdpTarget. */
class NetworkManager {
#cdpTarget;
#eventManager;
#networkStorage;
constructor(cdpTarget, eventManager, networkStorage) {
this.#cdpTarget = cdpTarget;
this.#eventManager = eventManager;
this.#networkStorage = networkStorage;
}
/** Returns the CDP Target associated with this NetworkManager instance. */
get cdpTarget() {
return this.#cdpTarget;
}
/**
* Gets the network request with the given ID, if any.
* Otherwise, creates a new network request with the given ID and cdp target.
*/
#getOrCreateNetworkRequest(id, redirectCount) {
let request = this.#networkStorage.getRequest(id);
if (request) {
return request;
}
request = new NetworkRequest_js_1.NetworkRequest(id, this.#eventManager, this.#networkStorage, this.#cdpTarget, redirectCount);
this.#networkStorage.addRequest(request);
return request;
}
static create(cdpTarget, eventManager, networkStorage) {
const networkManager = new NetworkManager(cdpTarget, eventManager, networkStorage);
cdpTarget.browserCdpClient.on('Target.detachedFromTarget', (params) => {
if (cdpTarget.cdpClient.sessionId === params.sessionId) {
networkManager.#networkStorage.disposeRequestMap();
}
});
cdpTarget.cdpClient.on('Network.requestWillBeSent', (params) => {
const request = networkManager.#networkStorage.getRequest(params.requestId);
if (request && request.isRedirecting()) {
request.handleRedirect(params);
networkManager.#networkStorage.deleteRequest(params.requestId);
networkManager
.#getOrCreateNetworkRequest(params.requestId, request.redirectCount + 1)
.onRequestWillBeSentEvent(params);
}
else if (request) {
request.onRequestWillBeSentEvent(params);
}
else {
networkManager
.#getOrCreateNetworkRequest(params.requestId)
.onRequestWillBeSentEvent(params);
}
});
cdpTarget.cdpClient.on('Network.requestWillBeSentExtraInfo', (params) => {
networkManager
.#getOrCreateNetworkRequest(params.requestId)
.onRequestWillBeSentExtraInfoEvent(params);
});
cdpTarget.cdpClient.on('Network.responseReceived', (params) => {
networkManager
.#getOrCreateNetworkRequest(params.requestId)
.onResponseReceivedEvent(params);
});
cdpTarget.cdpClient.on('Network.responseReceivedExtraInfo', (params) => {
networkManager
.#getOrCreateNetworkRequest(params.requestId)
.onResponseReceivedExtraInfoEvent(params);
});
cdpTarget.cdpClient.on('Network.requestServedFromCache', (params) => {
networkManager
.#getOrCreateNetworkRequest(params.requestId)
.onServedFromCache();
});
cdpTarget.cdpClient.on('Network.loadingFailed', (params) => {
networkManager
.#getOrCreateNetworkRequest(params.requestId)
.onLoadingFailedEvent(params);
});
// https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#event-requestPaused
cdpTarget.cdpClient.on('Fetch.requestPaused', (params) => {
if (params.networkId) {
networkManager
.#getOrCreateNetworkRequest(params.networkId)
.onRequestPaused(params);
}
});
return networkManager;
}
}
exports.NetworkManager = NetworkManager;
//# sourceMappingURL=NetworkManager.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"NetworkManager.js","sourceRoot":"","sources":["../../../../../src/bidiMapper/domains/network/NetworkManager.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,21 @@
import { Network, type EmptyResult } from '../../../protocol/protocol.js';
import type { BrowsingContextStorage } from '../context/BrowsingContextStorage.js';
import { NetworkStorage } from './NetworkStorage.js';
/** Dispatches Network domain commands. */
export declare class NetworkProcessor {
#private;
constructor(browsingContextStorage: BrowsingContextStorage, networkStorage: NetworkStorage);
addIntercept(params: Network.AddInterceptParameters): Promise<Network.AddInterceptResult>;
continueRequest(params: Network.ContinueRequestParameters): Promise<EmptyResult>;
continueResponse(params: Network.ContinueResponseParameters): Promise<EmptyResult>;
continueWithAuth(params: Network.ContinueWithAuthParameters): Promise<EmptyResult>;
failRequest({ request: networkId, }: Network.FailRequestParameters): Promise<EmptyResult>;
provideResponse(params: Network.ProvideResponseParameters): Promise<EmptyResult>;
removeIntercept(params: Network.RemoveInterceptParameters): Promise<EmptyResult>;
/**
* Attempts to parse the given url.
* Throws an InvalidArgumentException if the url is invalid.
*/
static parseUrlString(url: string): URL;
static parseUrlPatterns(urlPatterns: Network.UrlPattern[]): Network.UrlPattern[];
}

View File

@@ -0,0 +1,230 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NetworkProcessor = void 0;
const protocol_js_1 = require("../../../protocol/protocol.js");
const assert_js_1 = require("../../../utils/assert.js");
const NetworkStorage_js_1 = require("./NetworkStorage.js");
const NetworkUtils_js_1 = require("./NetworkUtils.js");
/** Dispatches Network domain commands. */
class NetworkProcessor {
#browsingContextStorage;
#networkStorage;
constructor(browsingContextStorage, networkStorage) {
this.#browsingContextStorage = browsingContextStorage;
this.#networkStorage = networkStorage;
}
async addIntercept(params) {
// If AuthRequired is specified, BeforeRequestSent must also be specified.
// This is a CDP quirk.
if (params.phases.includes("authRequired" /* Network.InterceptPhase.AuthRequired */) &&
!params.phases.includes("beforeRequestSent" /* Network.InterceptPhase.BeforeRequestSent */)) {
params.phases.unshift("beforeRequestSent" /* Network.InterceptPhase.BeforeRequestSent */);
}
const urlPatterns = params.urlPatterns ?? [];
const parsedUrlPatterns = NetworkProcessor.parseUrlPatterns(urlPatterns);
const intercept = this.#networkStorage.addIntercept({
urlPatterns: parsedUrlPatterns,
phases: params.phases,
});
await this.#fetchApply();
return {
intercept,
};
}
async continueRequest(params) {
const networkId = params.request;
const { request: fetchId, phase } = this.#getBlockedRequest(networkId);
if (phase !== "beforeRequestSent" /* Network.InterceptPhase.BeforeRequestSent */) {
throw new protocol_js_1.InvalidArgumentException(`Blocked request for network id '${networkId}' is not in 'BeforeRequestSent' phase`);
}
if (params.url !== undefined) {
NetworkProcessor.parseUrlString(params.url);
}
const { url, method, headers } = params;
// TODO: Set / expand.
// ; Step 9. cookies
// ; Step 10. body
const requestHeaders = (0, NetworkUtils_js_1.cdpFetchHeadersFromBidiNetworkHeaders)(headers);
const request = this.#getRequestOrFail(networkId);
await request.continueRequest(fetchId, url, method, requestHeaders);
this.#networkStorage.removeBlockedRequest(networkId);
return {};
}
async continueResponse(params) {
const networkId = params.request;
const { request: fetchId, phase } = this.#getBlockedRequest(networkId);
if (phase === "beforeRequestSent" /* Network.InterceptPhase.BeforeRequestSent */) {
throw new protocol_js_1.InvalidArgumentException(`Blocked request for network id '${networkId}' is in 'BeforeRequestSent' phase`);
}
const { statusCode, reasonPhrase, headers } = params;
const responseHeaders = (0, NetworkUtils_js_1.cdpFetchHeadersFromBidiNetworkHeaders)(headers);
// TODO: Set / expand.
// ; Step 10. cookies
// ; Step 11. credentials
const request = this.#getRequestOrFail(networkId);
await request.continueResponse(fetchId, statusCode, reasonPhrase, responseHeaders);
this.#networkStorage.removeBlockedRequest(networkId);
return {};
}
async continueWithAuth(params) {
const networkId = params.request;
const { request: fetchId, phase } = this.#getBlockedRequest(networkId);
if (phase !== "authRequired" /* Network.InterceptPhase.AuthRequired */) {
throw new protocol_js_1.InvalidArgumentException(`Blocked request for network id '${networkId}' is not in 'AuthRequired' phase`);
}
const request = this.#getRequestOrFail(networkId);
let username;
let password;
if (params.action === 'provideCredentials') {
const { credentials } = params;
username = credentials.username;
password = credentials.password;
// TODO: This should be invalid argument exception.
// Spec may need to be updated.
(0, assert_js_1.assert)(credentials.type === 'password', `Credentials type ${credentials.type} must be 'password'`);
}
const response = (0, NetworkUtils_js_1.cdpAuthChallengeResponseFromBidiAuthContinueWithAuthAction)(params.action);
await request.continueWithAuth(fetchId, response, username, password);
return {};
}
async failRequest({ request: networkId, }) {
const { request: fetchId, phase } = this.#getBlockedRequest(networkId);
if (phase === "authRequired" /* Network.InterceptPhase.AuthRequired */) {
throw new protocol_js_1.InvalidArgumentException(`Blocked request for network id '${networkId}' is in 'AuthRequired' phase`);
}
const request = this.#getRequestOrFail(networkId);
await request.failRequest(fetchId, 'Failed');
this.#networkStorage.removeBlockedRequest(networkId);
return {};
}
async provideResponse(params) {
const { statusCode, reasonPhrase, headers, body, request: networkId, } = params;
const { request: fetchId } = this.#getBlockedRequest(networkId);
// TODO: Step 6
// https://w3c.github.io/webdriver-bidi/#command-network-continueResponse
const responseHeaders = (0, NetworkUtils_js_1.cdpFetchHeadersFromBidiNetworkHeaders)(headers);
// TODO: Set / expand.
// ; Step 10. cookies
// ; Step 11. credentials
const request = this.#getRequestOrFail(networkId);
await request.provideResponse(fetchId, statusCode ?? request.statusCode, reasonPhrase, responseHeaders, body?.value // TODO: Differ base64 / string
);
this.#networkStorage.removeBlockedRequest(networkId);
return {};
}
async removeIntercept(params) {
this.#networkStorage.removeIntercept(params.intercept);
await this.#fetchApply();
return {};
}
/** Applies all existing network intercepts to all CDP targets concurrently. */
async #fetchEnable() {
await Promise.all(this.#browsingContextStorage.getAllContexts().map(async (context) => {
await context.cdpTarget.fetchEnable();
}));
}
/** Removes all existing network intercepts from all CDP targets concurrently. */
async #fetchDisable() {
await Promise.all(this.#browsingContextStorage.getAllContexts().map(async (context) => {
await context.cdpTarget.fetchDisable();
}));
}
/**
* Either enables or disables the Fetch domain.
*
* If enabling, applies all existing network intercepts to all CDP targets.
* If disabling, removes all existing network intercepts from all CDP targets.
*
* Disabling is only performed when there are no remaining intercepts or
* // blocked requests.
*/
async #fetchApply() {
if (this.#networkStorage.hasIntercepts() ||
this.#networkStorage.hasBlockedRequests() ||
this.#networkStorage.hasNetworkRequests()) {
// TODO: Add try/catch. Remove the intercept if CDP Fetch commands fail.
await this.#fetchEnable();
}
else {
// The last intercept has been removed, and there are no pending
// blocked requests.
// Disable the Fetch domain.
await this.#fetchDisable();
}
}
/**
* Returns the blocked request associated with the given network ID.
* If none, throws a NoSuchRequestException.
*/
#getBlockedRequest(networkId) {
const blockedRequest = this.#networkStorage.getBlockedRequest(networkId);
if (!blockedRequest) {
throw new protocol_js_1.NoSuchRequestException(`No blocked request found for network id '${networkId}'`);
}
return blockedRequest;
}
#getRequestOrFail(id) {
const request = this.#networkStorage.getRequest(id);
if (!request) {
throw new protocol_js_1.NoSuchRequestException(`Network request with ID ${id} doesn't exist`);
}
return request;
}
/**
* Attempts to parse the given url.
* Throws an InvalidArgumentException if the url is invalid.
*/
static parseUrlString(url) {
try {
return new URL(url);
}
catch (error) {
throw new protocol_js_1.InvalidArgumentException(`Invalid URL '${url}': ${error}`);
}
}
static parseUrlPatterns(urlPatterns) {
return urlPatterns.map((urlPattern) => {
switch (urlPattern.type) {
case 'string': {
NetworkProcessor.parseUrlString(urlPattern.pattern);
return urlPattern;
}
case 'pattern':
// No params signifies intercept all
if (urlPattern.protocol === undefined &&
urlPattern.hostname === undefined &&
urlPattern.port === undefined &&
urlPattern.pathname === undefined &&
urlPattern.search === undefined) {
return urlPattern;
}
if (urlPattern.protocol === '') {
throw new protocol_js_1.InvalidArgumentException(`URL pattern must specify a protocol`);
}
if (urlPattern.hostname === '') {
throw new protocol_js_1.InvalidArgumentException(`URL pattern must specify a hostname`);
}
if ((urlPattern.hostname?.length ?? 0) > 0) {
if (urlPattern.protocol?.match(/^file/i)) {
throw new protocol_js_1.InvalidArgumentException(`URL pattern protocol cannot be 'file'`);
}
if (urlPattern.hostname?.includes(':')) {
throw new protocol_js_1.InvalidArgumentException(`URL pattern hostname must not contain a colon`);
}
}
if (urlPattern.port === '') {
throw new protocol_js_1.InvalidArgumentException(`URL pattern must specify a port`);
}
try {
new URL(NetworkStorage_js_1.NetworkStorage.buildUrlPatternString(urlPattern));
}
catch (error) {
throw new protocol_js_1.InvalidArgumentException(`${error}`);
}
return urlPattern;
}
});
}
}
exports.NetworkProcessor = NetworkProcessor;
//# sourceMappingURL=NetworkProcessor.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,41 @@
/**
* @fileoverview `NetworkRequest` represents a single network request and keeps
* track of all the related CDP events.
*/
import type { Protocol } from 'devtools-protocol';
import { Network, type JsUint } from '../../../protocol/protocol.js';
import type { CdpTarget } from '../context/CdpTarget.js';
import type { EventManager } from '../session/EventManager.js';
import type { NetworkStorage } from './NetworkStorage.js';
/** Abstracts one individual network request. */
export declare class NetworkRequest {
#private;
constructor(requestId: Network.Request, eventManager: EventManager, networkStorage: NetworkStorage, cdpTarget: CdpTarget, redirectCount?: number);
get requestId(): string;
get url(): string | undefined;
get redirectCount(): number;
get cdpTarget(): CdpTarget;
isRedirecting(): boolean;
handleRedirect(event: Protocol.Network.RequestWillBeSentEvent): void;
onRequestWillBeSentEvent(event: Protocol.Network.RequestWillBeSentEvent): void;
onRequestWillBeSentExtraInfoEvent(event: Protocol.Network.RequestWillBeSentExtraInfoEvent): void;
onResponseReceivedExtraInfoEvent(event: Protocol.Network.ResponseReceivedExtraInfoEvent): void;
onResponseReceivedEvent(event: Protocol.Network.ResponseReceivedEvent): void;
onServedFromCache(): void;
onLoadingFailedEvent(event: Protocol.Network.LoadingFailedEvent): void;
/** Fired whenever a network request interception is hit. */
onRequestPaused(params: Protocol.Fetch.RequestPausedEvent): void;
/** @see https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#method-failRequest */
failRequest(networkId: Network.Request, errorReason: Protocol.Network.ErrorReason): Promise<void>;
/** @see https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#method-continueRequest */
continueRequest(cdpFetchRequestId: Protocol.Fetch.RequestId, url?: string, method?: string, headers?: Protocol.Fetch.HeaderEntry[]): Promise<void>;
/** @see https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#method-continueResponse */
continueResponse(cdpFetchRequestId: Protocol.Fetch.RequestId, responseCode?: JsUint, responsePhrase?: string, responseHeaders?: Protocol.Fetch.HeaderEntry[]): Promise<void>;
/** @see https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#method-continueWithAuth */
continueWithAuth(cdpFetchRequestId: Protocol.Fetch.RequestId, response: 'Default' | 'CancelAuth' | 'ProvideCredentials', username?: string, password?: string): Promise<void>;
/** @see https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#method-provideResponse */
provideResponse(cdpFetchRequestId: Protocol.Fetch.RequestId, responseCode: JsUint, responsePhrase?: string, responseHeaders?: Protocol.Fetch.HeaderEntry[], body?: string): Promise<void>;
dispose(): void;
/** Returns the HTTP status code associated with this request if any. */
get statusCode(): number;
}

View File

@@ -0,0 +1,532 @@
"use strict";
/*
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.NetworkRequest = void 0;
const protocol_js_1 = require("../../../protocol/protocol.js");
const assert_js_1 = require("../../../utils/assert.js");
const Deferred_js_1 = require("../../../utils/Deferred.js");
const NetworkUtils_js_1 = require("./NetworkUtils.js");
/** Abstracts one individual network request. */
class NetworkRequest {
static #unknown = 'UNKNOWN';
/**
* Each network request has an associated request id, which is a string
* uniquely identifying that request.
*
* The identifier for a request resulting from a redirect matches that of the
* request that initiated it.
*/
#requestId;
// TODO: Handle auth required?
/**
* Indicates the network intercept phase, if the request is currently blocked.
* Undefined necessarily implies that the request is not blocked.
*/
#interceptPhase = undefined;
#servedFromCache = false;
#redirectCount;
#eventManager;
#networkStorage;
#request = {};
#response = {};
#beforeRequestSentDeferred = new Deferred_js_1.Deferred();
#responseStartedDeferred = new Deferred_js_1.Deferred();
#responseCompletedDeferred = new Deferred_js_1.Deferred();
#cdpTarget;
constructor(requestId, eventManager, networkStorage, cdpTarget, redirectCount = 0) {
this.#requestId = requestId;
this.#eventManager = eventManager;
this.#networkStorage = networkStorage;
this.#cdpTarget = cdpTarget;
this.#redirectCount = redirectCount;
}
get requestId() {
return this.#requestId;
}
get url() {
return this.#response.info?.url ?? this.#request.info?.request.url;
}
get redirectCount() {
return this.#redirectCount;
}
get cdpTarget() {
return this.#cdpTarget;
}
isRedirecting() {
return Boolean(this.#request.info);
}
handleRedirect(event) {
this.#queueResponseStartedEvent();
this.#queueResponseCompletedEvent();
this.#response.hasExtraInfo = event.redirectHasExtraInfo;
this.#response.info = event.redirectResponse;
this.#emitEventsIfReady(true);
}
#emitEventsIfReady(wasRedirected = false) {
const requestExtraInfoCompleted =
// Flush redirects
wasRedirected ||
Boolean(this.#request.extraInfo) ||
// Requests from cache don't have extra info
this.#servedFromCache ||
// Sometimes there is no extra info and the response
// is the only place we can find out
Boolean(this.#response.info && !this.#response.hasExtraInfo) ||
this.#interceptPhase === "beforeRequestSent" /* Network.InterceptPhase.BeforeRequestSent */;
if (this.#request.info && requestExtraInfoCompleted) {
this.#beforeRequestSentDeferred.resolve({
kind: 'success',
value: undefined,
});
}
const responseExtraInfoCompleted = Boolean(this.#response.extraInfo) ||
// Response from cache don't have extra info
this.#servedFromCache ||
// Don't expect extra info if the flag is false
Boolean(this.#response.info && !this.#response.hasExtraInfo) ||
this.#interceptPhase === "responseStarted" /* Network.InterceptPhase.ResponseStarted */;
if (this.#response.info && responseExtraInfoCompleted) {
this.#responseStartedDeferred.resolve({
kind: 'success',
value: undefined,
});
this.#responseCompletedDeferred.resolve({
kind: 'success',
value: undefined,
});
}
}
onRequestWillBeSentEvent(event) {
this.#request.info = event;
this.#queueBeforeRequestSentEvent();
this.#emitEventsIfReady();
}
onRequestWillBeSentExtraInfoEvent(event) {
this.#request.extraInfo = event;
this.#emitEventsIfReady();
}
onResponseReceivedExtraInfoEvent(event) {
this.#response.extraInfo = event;
this.#emitEventsIfReady();
}
onResponseReceivedEvent(event) {
this.#response.hasExtraInfo = event.hasExtraInfo;
this.#response.info = event.response;
this.#queueResponseStartedEvent();
this.#queueResponseCompletedEvent();
this.#emitEventsIfReady();
}
onServedFromCache() {
this.#servedFromCache = true;
this.#emitEventsIfReady();
}
onLoadingFailedEvent(event) {
this.#beforeRequestSentDeferred.resolve({
kind: 'success',
value: undefined,
});
this.#responseStartedDeferred.resolve({
kind: 'error',
error: new Error('Network event loading failed'),
});
this.#responseCompletedDeferred.resolve({
kind: 'error',
error: new Error('Network event loading failed'),
});
this.#eventManager.registerEvent({
type: 'event',
method: protocol_js_1.ChromiumBidi.Network.EventNames.FetchError,
params: {
...this.#getBaseEventParams(),
errorText: event.errorText,
},
}, this.#context);
}
/** Fired whenever a network request interception is hit. */
onRequestPaused(params) {
if (this.#isIgnoredEvent()) {
void this.continueRequest(params.requestId).catch(() => {
// TODO: Add some logging
});
return;
}
// The stage of the request can be determined by presence of
// responseErrorReason and responseStatusCode -- the request is at
// the response stage if either of these fields is present and in the
// request stage otherwise.
let phase;
if (params.responseErrorReason === undefined &&
params.responseStatusCode === undefined) {
phase = "beforeRequestSent" /* Network.InterceptPhase.BeforeRequestSent */;
}
else if (params.responseStatusCode === 401 &&
params.responseStatusText === 'Unauthorized') {
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401
phase = "authRequired" /* Network.InterceptPhase.AuthRequired */;
}
else {
phase = "responseStarted" /* Network.InterceptPhase.ResponseStarted */;
}
const headers = (0, NetworkUtils_js_1.bidiNetworkHeadersFromCdpFetchHeaders)(
// TODO: Use params.request.headers if request?
params.responseHeaders);
this.#networkStorage.addBlockedRequest(this.requestId, {
request: params.requestId, // intercept request id
phase,
// TODO: Finish populating response / ResponseData.
response: {
url: params.request.url,
// TODO: populate.
protocol: '',
status: params.responseStatusCode ?? 0,
statusText: params.responseStatusText ?? '',
// TODO: populate.
fromCache: false,
headers,
// TODO: populate.
mimeType: '',
// TODO: populate.
bytesReceived: 0,
headersSize: (0, NetworkUtils_js_1.computeHeadersSize)(headers),
// TODO: consider removing from spec.
bodySize: 0,
// TODO: consider removing from spec.
content: {
size: 0,
},
// TODO: populate.
authChallenge: undefined,
},
});
this.#interceptPhase = phase;
this.#emitEventsIfReady();
}
/** @see https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#method-failRequest */
async failRequest(networkId, errorReason) {
await this.#cdpTarget.cdpClient.sendCommand('Fetch.failRequest', {
requestId: networkId,
errorReason,
});
this.#interceptPhase = undefined;
}
/** @see https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#method-continueRequest */
async continueRequest(cdpFetchRequestId, url, method, headers) {
// TODO: Expand.
await this.#cdpTarget.cdpClient.sendCommand('Fetch.continueRequest', {
requestId: cdpFetchRequestId,
url,
method,
headers,
// TODO: Set?
// postData:,
// interceptResponse:,
});
this.#interceptPhase = undefined;
}
/** @see https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#method-continueResponse */
async continueResponse(cdpFetchRequestId, responseCode, responsePhrase, responseHeaders) {
await this.#cdpTarget.cdpClient.sendCommand('Fetch.continueResponse', {
requestId: cdpFetchRequestId,
responseCode,
responsePhrase,
responseHeaders,
});
this.#interceptPhase = undefined;
}
/** @see https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#method-continueWithAuth */
async continueWithAuth(cdpFetchRequestId, response, username, password) {
await this.#cdpTarget.cdpClient.sendCommand('Fetch.continueWithAuth', {
requestId: cdpFetchRequestId,
authChallengeResponse: {
response,
username,
password,
},
});
this.#interceptPhase = undefined;
}
/** @see https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#method-provideResponse */
async provideResponse(cdpFetchRequestId, responseCode, responsePhrase, responseHeaders, body) {
await this.#cdpTarget.cdpClient.sendCommand('Fetch.fulfillRequest', {
requestId: cdpFetchRequestId,
responseCode,
responsePhrase,
responseHeaders,
...(body ? { body: btoa(body) } : {}), // TODO: Double-check if btoa usage is correct.
});
this.#interceptPhase = undefined;
}
dispose() {
const result = {
kind: 'error',
error: new Error('Network processor detached'),
};
this.#beforeRequestSentDeferred.resolve(result);
this.#responseStartedDeferred.resolve(result);
this.#responseCompletedDeferred.resolve(result);
}
get #context() {
return this.#request.info?.frameId ?? null;
}
/** Returns the HTTP status code associated with this request if any. */
get statusCode() {
return (this.#response.info?.status ?? this.#response.extraInfo?.statusCode ?? -1 // TODO: Throw an exception or use some other status code?
);
}
#getBaseEventParams(phase) {
// TODO: Set this in terms of intercepts?
const isBlocked = phase !== undefined && phase === this.#interceptPhase;
const intercepts = this.#networkStorage.getNetworkIntercepts(this.#requestId, phase);
return {
isBlocked,
context: this.#context,
navigation: this.#getNavigationId(),
redirectCount: this.#redirectCount,
request: this.#getRequestData(),
// Timestamp should be in milliseconds, while CDP provides it in seconds.
timestamp: Math.round((this.#request.info?.wallTime ?? 0) * 1000),
// XXX: we should return correct types from the function.
intercepts: isBlocked ? intercepts : undefined,
};
}
#getNavigationId() {
if (!this.#request.info ||
!this.#request.info.loaderId ||
// When we navigate all CDP network events have `loaderId`
// CDP's `loaderId` and `requestId` match when
// that request triggered the loading
this.#request.info.loaderId !== this.#request.info.requestId) {
return null;
}
return this.#request.info.loaderId;
}
#getRequestData() {
const cookies = this.#request.extraInfo
? NetworkRequest.#getCookies(this.#request.extraInfo.associatedCookies)
: [];
const headers = (0, NetworkUtils_js_1.bidiNetworkHeadersFromCdpNetworkHeaders)(this.#request.info?.request.headers);
return {
request: this.#request.info?.requestId ?? NetworkRequest.#unknown,
url: this.#request.info?.request.url ?? NetworkRequest.#unknown,
method: this.#request.info?.request.method ?? NetworkRequest.#unknown,
headers,
cookies,
headersSize: (0, NetworkUtils_js_1.computeHeadersSize)(headers),
// TODO: implement.
bodySize: 0,
timings: this.#getTimings(),
};
}
// TODO: implement.
#getTimings() {
return {
timeOrigin: 0,
requestTime: 0,
redirectStart: 0,
redirectEnd: 0,
fetchStart: 0,
dnsStart: 0,
dnsEnd: 0,
connectStart: 0,
connectEnd: 0,
tlsStart: 0,
requestStart: 0,
responseStart: 0,
responseEnd: 0,
};
}
#queueBeforeRequestSentEvent() {
if (this.#isIgnoredEvent()) {
return;
}
this.#eventManager.registerPromiseEvent(this.#beforeRequestSentDeferred.then((result) => {
if (result.kind === 'success') {
try {
return {
kind: 'success',
value: Object.assign(this.#getBeforeRequestEvent(), {
type: 'event',
}),
};
}
catch (error) {
return {
kind: 'error',
error: error instanceof Error ? error : new Error('Unknown'),
};
}
}
return result;
}), this.#context, protocol_js_1.ChromiumBidi.Network.EventNames.BeforeRequestSent);
}
#getBeforeRequestEvent() {
(0, assert_js_1.assert)(this.#request.info, 'RequestWillBeSentEvent is not set');
return {
method: protocol_js_1.ChromiumBidi.Network.EventNames.BeforeRequestSent,
params: {
...this.#getBaseEventParams("beforeRequestSent" /* Network.InterceptPhase.BeforeRequestSent */),
initiator: {
type: NetworkRequest.#getInitiatorType(this.#request.info.initiator.type),
},
},
};
}
#queueResponseStartedEvent() {
if (this.#isIgnoredEvent()) {
return;
}
this.#eventManager.registerPromiseEvent(this.#responseStartedDeferred.then((result) => {
if (result.kind === 'success') {
try {
return {
kind: 'success',
value: Object.assign(this.#getResponseStartedEvent(), {
type: 'event',
}),
};
}
catch (error) {
return {
kind: 'error',
error: error instanceof Error ? error : new Error('Unknown'),
};
}
}
return result;
}), this.#context, protocol_js_1.ChromiumBidi.Network.EventNames.ResponseStarted);
}
#getResponseStartedEvent() {
(0, assert_js_1.assert)(this.#request.info, 'RequestWillBeSentEvent is not set');
(0, assert_js_1.assert)(this.#response.info, 'ResponseReceivedEvent is not set');
// Chromium sends wrong extraInfo events for responses served from cache.
// See https://github.com/puppeteer/puppeteer/issues/9965 and
// https://crbug.com/1340398.
if (this.#response.info.fromDiskCache) {
this.#response.extraInfo = undefined;
}
const headers = (0, NetworkUtils_js_1.bidiNetworkHeadersFromCdpNetworkHeaders)(this.#response.info.headers);
return {
method: protocol_js_1.ChromiumBidi.Network.EventNames.ResponseStarted,
params: {
...this.#getBaseEventParams(),
response: {
url: this.#response.info.url ?? NetworkRequest.#unknown,
protocol: this.#response.info.protocol ?? '',
status: this.statusCode,
statusText: this.#response.info.statusText,
fromCache: this.#response.info.fromDiskCache ||
this.#response.info.fromPrefetchCache ||
this.#servedFromCache,
headers,
mimeType: this.#response.info.mimeType,
bytesReceived: this.#response.info.encodedDataLength,
headersSize: (0, NetworkUtils_js_1.computeHeadersSize)(headers),
// TODO: consider removing from spec.
bodySize: 0,
content: {
// TODO: consider removing from spec.
size: 0,
},
},
},
};
}
#queueResponseCompletedEvent() {
if (this.#isIgnoredEvent()) {
return;
}
this.#eventManager.registerPromiseEvent(this.#responseCompletedDeferred.then((result) => {
if (result.kind === 'success') {
try {
return {
kind: 'success',
value: Object.assign(this.#getResponseReceivedEvent(), {
type: 'event',
}),
};
}
catch (error) {
return {
kind: 'error',
error: error instanceof Error ? error : new Error('Unknown'),
};
}
}
return result;
}), this.#context, protocol_js_1.ChromiumBidi.Network.EventNames.ResponseCompleted);
}
#getResponseReceivedEvent() {
(0, assert_js_1.assert)(this.#request.info, 'RequestWillBeSentEvent is not set');
(0, assert_js_1.assert)(this.#response.info, 'ResponseReceivedEvent is not set');
// Chromium sends wrong extraInfo events for responses served from cache.
// See https://github.com/puppeteer/puppeteer/issues/9965 and
// https://crbug.com/1340398.
if (this.#response.info.fromDiskCache) {
this.#response.extraInfo = undefined;
}
const headers = (0, NetworkUtils_js_1.bidiNetworkHeadersFromCdpNetworkHeaders)(this.#response.info.headers);
return {
method: protocol_js_1.ChromiumBidi.Network.EventNames.ResponseCompleted,
params: {
...this.#getBaseEventParams(),
response: {
url: this.#response.info.url ?? NetworkRequest.#unknown,
protocol: this.#response.info.protocol ?? '',
status: this.statusCode,
statusText: this.#response.info.statusText,
fromCache: this.#response.info.fromDiskCache ||
this.#response.info.fromPrefetchCache ||
this.#servedFromCache,
headers,
mimeType: this.#response.info.mimeType,
bytesReceived: this.#response.info.encodedDataLength,
headersSize: (0, NetworkUtils_js_1.computeHeadersSize)(headers),
// TODO: consider removing from spec.
bodySize: 0,
content: {
// TODO: consider removing from spec.
size: 0,
},
},
},
};
}
#isIgnoredEvent() {
return this.#request.info?.request.url.endsWith('/favicon.ico') ?? false;
}
static #getInitiatorType(initiatorType) {
switch (initiatorType) {
case 'parser':
case 'script':
case 'preflight':
return initiatorType;
default:
return 'other';
}
}
static #getCookies(associatedCookies) {
return associatedCookies
.filter(({ blockedReasons }) => {
return !Array.isArray(blockedReasons) || blockedReasons.length === 0;
})
.map(({ cookie }) => (0, NetworkUtils_js_1.cdpToBiDiCookie)(cookie));
}
}
exports.NetworkRequest = NetworkRequest;
//# sourceMappingURL=NetworkRequest.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,84 @@
/**
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Protocol } from 'devtools-protocol';
import { Network } from '../../../protocol/protocol.js';
import type { NetworkRequest } from './NetworkRequest.js';
interface NetworkInterception {
urlPatterns: Network.UrlPattern[];
phases: Network.AddInterceptParameters['phases'];
}
export interface BlockedRequest {
request: Protocol.Fetch.RequestId;
phase: Network.InterceptPhase;
response: Network.ResponseData;
}
/** Stores network and intercept maps. */
export declare class NetworkStorage {
#private;
disposeRequestMap(): void;
/**
* Adds the given entry to the intercept map.
* URL patterns are assumed to be parsed.
*
* @return The intercept ID.
*/
addIntercept(value: NetworkInterception): Network.Intercept;
/**
* Removes the given intercept from the intercept map.
* Throws NoSuchInterceptException if the intercept does not exist.
*/
removeIntercept(intercept: Network.Intercept): void;
/** Returns true if there's at least one added intercept. */
hasIntercepts(): boolean;
/** Gets parameters for CDP 'Fetch.enable' command from the intercept map. */
getFetchEnableParams(): Protocol.Fetch.EnableRequest;
getRequest(id: Network.Request): NetworkRequest | undefined;
addRequest(request: NetworkRequest): void;
deleteRequest(id: Network.Request): void;
/** Returns true if there's at least one network request. */
hasNetworkRequests(): boolean;
/** Returns true if there's at least one blocked network request. */
hasBlockedRequests(): boolean;
/** Converts a URL pattern from the spec to a CDP URL pattern. */
static cdpFromSpecUrlPattern(urlPattern: Network.UrlPattern): string;
static buildUrlPatternString({ protocol, hostname, port, pathname, search, }: Network.UrlPatternPattern): string;
/**
* Maps spec Network.InterceptPhase to CDP Fetch.RequestStage.
* AuthRequired has no CDP equivalent..
*/
static requestStageFromPhase(phase: Network.InterceptPhase): Protocol.Fetch.RequestStage;
/**
* Returns true if the given protocol is special.
* Special protocols are those that have a default port.
*
* Example inputs: 'http', 'http:'
*
* @see https://url.spec.whatwg.org/#special-scheme
*/
static isSpecialScheme(protocol: string): boolean;
addBlockedRequest(requestId: Network.Request, value: BlockedRequest): void;
removeBlockedRequest(requestId: Network.Request): void;
/**
* Returns the blocked request associated with the given network ID, if any.
*/
getBlockedRequest(networkId: Network.Request): BlockedRequest | undefined;
/** #@see https://w3c.github.io/webdriver-bidi/#get-the-network-intercepts */
getNetworkIntercepts(requestId: Network.Request, phase?: Network.InterceptPhase): Network.Intercept[];
/** Matches the given URLPattern against the given URL. */
static matchUrlPattern(urlPattern: Network.UrlPattern, url: string | undefined): boolean;
}
export {};

View File

@@ -0,0 +1,217 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NetworkStorage = void 0;
const protocol_js_1 = require("../../../protocol/protocol.js");
const UrlPattern_js_1 = require("../../../utils/UrlPattern.js");
const uuid_js_1 = require("../../../utils/uuid.js");
/** Stores network and intercept maps. */
class NetworkStorage {
/**
* A map from network request ID to Network Request objects.
* Needed as long as information about requests comes from different events.
*/
#requestMap = new Map();
/** A map from intercept ID to track active network intercepts. */
#interceptMap = new Map();
/** A map from network request ID to track actively blocked requests. */
#blockedRequestMap = new Map();
disposeRequestMap() {
for (const request of this.#requestMap.values()) {
request.dispose();
}
this.#requestMap.clear();
}
/**
* Adds the given entry to the intercept map.
* URL patterns are assumed to be parsed.
*
* @return The intercept ID.
*/
addIntercept(value) {
const interceptId = (0, uuid_js_1.uuidv4)();
this.#interceptMap.set(interceptId, value);
return interceptId;
}
/**
* Removes the given intercept from the intercept map.
* Throws NoSuchInterceptException if the intercept does not exist.
*/
removeIntercept(intercept) {
if (!this.#interceptMap.has(intercept)) {
throw new protocol_js_1.NoSuchInterceptException(`Intercept '${intercept}' does not exist.`);
}
this.#interceptMap.delete(intercept);
}
/** Returns true if there's at least one added intercept. */
hasIntercepts() {
return this.#interceptMap.size > 0;
}
/** Gets parameters for CDP 'Fetch.enable' command from the intercept map. */
getFetchEnableParams() {
const patterns = [];
for (const value of this.#interceptMap.values()) {
for (const phase of value.phases) {
const requestStage = NetworkStorage.requestStageFromPhase(phase);
if (value.urlPatterns.length === 0) {
patterns.push({
urlPattern: '*',
requestStage,
});
continue;
}
for (const urlPatternSpec of value.urlPatterns) {
const urlPattern = NetworkStorage.cdpFromSpecUrlPattern(urlPatternSpec);
patterns.push({
urlPattern,
requestStage,
});
}
}
}
return {
patterns,
// If there's at least one intercept that requires auth, enable the
// 'Fetch.authRequired' event.
handleAuthRequests: [...this.#interceptMap.values()].some((param) => {
return param.phases.includes("authRequired" /* Network.InterceptPhase.AuthRequired */);
}),
};
}
getRequest(id) {
return this.#requestMap.get(id);
}
addRequest(request) {
this.#requestMap.set(request.requestId, request);
}
deleteRequest(id) {
const request = this.#requestMap.get(id);
if (request) {
request.dispose();
this.#requestMap.delete(id);
}
}
/** Returns true if there's at least one network request. */
hasNetworkRequests() {
return this.#requestMap.size > 0;
}
/** Returns true if there's at least one blocked network request. */
hasBlockedRequests() {
return this.#blockedRequestMap.size > 0;
}
/** Converts a URL pattern from the spec to a CDP URL pattern. */
static cdpFromSpecUrlPattern(urlPattern) {
switch (urlPattern.type) {
case 'string':
return urlPattern.pattern;
case 'pattern':
return NetworkStorage.buildUrlPatternString(urlPattern);
}
}
static buildUrlPatternString({ protocol, hostname, port, pathname, search, }) {
if (!protocol && !hostname && !port && !pathname && !search) {
return '*';
}
let url = '';
if (protocol) {
url += protocol;
if (!protocol.endsWith(':')) {
url += ':';
}
if (NetworkStorage.isSpecialScheme(protocol)) {
url += '//';
}
}
if (hostname) {
url += hostname;
}
if (port) {
url += `:${port}`;
}
if (pathname) {
if (!pathname.startsWith('/')) {
url += '/';
}
url += pathname;
}
if (search) {
if (!search.startsWith('?')) {
url += '?';
}
url += search;
}
return url;
}
/**
* Maps spec Network.InterceptPhase to CDP Fetch.RequestStage.
* AuthRequired has no CDP equivalent..
*/
static requestStageFromPhase(phase) {
switch (phase) {
case "beforeRequestSent" /* Network.InterceptPhase.BeforeRequestSent */:
return 'Request';
case "responseStarted" /* Network.InterceptPhase.ResponseStarted */:
case "authRequired" /* Network.InterceptPhase.AuthRequired */:
return 'Response';
}
}
/**
* Returns true if the given protocol is special.
* Special protocols are those that have a default port.
*
* Example inputs: 'http', 'http:'
*
* @see https://url.spec.whatwg.org/#special-scheme
*/
static isSpecialScheme(protocol) {
return ['ftp', 'file', 'http', 'https', 'ws', 'wss'].includes(protocol.replace(/:$/, ''));
}
addBlockedRequest(requestId, value) {
this.#blockedRequestMap.set(requestId, value);
}
removeBlockedRequest(requestId) {
this.#blockedRequestMap.delete(requestId);
}
/**
* Returns the blocked request associated with the given network ID, if any.
*/
getBlockedRequest(networkId) {
return this.#blockedRequestMap.get(networkId);
}
/** #@see https://w3c.github.io/webdriver-bidi/#get-the-network-intercepts */
getNetworkIntercepts(requestId, phase) {
const request = this.#requestMap.get(requestId);
if (!request) {
return [];
}
const interceptIds = [];
for (const [interceptId, { phases, urlPatterns },] of this.#interceptMap.entries()) {
if (phase && phases.includes(phase)) {
if (urlPatterns.length === 0) {
interceptIds.push(interceptId);
}
else if (urlPatterns.some((urlPattern) => NetworkStorage.matchUrlPattern(urlPattern, request.url))) {
interceptIds.push(interceptId);
}
}
}
return interceptIds;
}
/** Matches the given URLPattern against the given URL. */
static matchUrlPattern(urlPattern, url) {
switch (urlPattern.type) {
case 'string':
return urlPattern.pattern === url;
case 'pattern': {
return (new UrlPattern_js_1.URLPattern({
protocol: urlPattern.protocol,
hostname: urlPattern.hostname,
port: urlPattern.port,
pathname: urlPattern.pathname,
search: urlPattern.search,
}).exec(url) !== null);
}
}
}
}
exports.NetworkStorage = NetworkStorage;
//# sourceMappingURL=NetworkStorage.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,28 @@
/**
* @fileoverview Utility functions for the Network domain.
*/
import type { Protocol } from 'devtools-protocol';
import { Network, type Storage } from '../../../protocol/protocol.js';
export declare function computeHeadersSize(headers: Network.Header[]): number;
/** Converts from CDP Network domain headers to Bidi network headers. */
export declare function bidiNetworkHeadersFromCdpNetworkHeaders(headers?: Protocol.Network.Headers): Network.Header[];
/** Converts from Bidi network headers to CDP Network domain headers. */
export declare function cdpNetworkHeadersFromBidiNetworkHeaders(headers?: Network.Header[]): Protocol.Network.Headers | undefined;
/** Converts from CDP Fetch domain header entries to Bidi network headers. */
export declare function bidiNetworkHeadersFromCdpFetchHeaders(headers?: Protocol.Fetch.HeaderEntry[]): Network.Header[];
/** Converts from Bidi network headers to CDP Fetch domain header entries. */
export declare function cdpFetchHeadersFromBidiNetworkHeaders(headers?: Network.Header[]): Protocol.Fetch.HeaderEntry[] | undefined;
/** Converts from Bidi auth action to CDP auth challenge response. */
export declare function cdpAuthChallengeResponseFromBidiAuthContinueWithAuthAction(action: 'default' | 'cancel' | 'provideCredentials'): "Default" | "CancelAuth" | "ProvideCredentials";
/**
* Converts from CDP Network domain cookie to BiDi network cookie.
* * https://chromedevtools.github.io/devtools-protocol/tot/Network/#type-Cookie
* * https://w3c.github.io/webdriver-bidi/#type-network-Cookie
*/
export declare function cdpToBiDiCookie(cookie: Protocol.Network.Cookie): Network.Cookie;
/**
* Converts from BiDi set network cookie params to CDP Network domain cookie.
* * https://w3c.github.io/webdriver-bidi/#type-network-Cookie
* * https://chromedevtools.github.io/devtools-protocol/tot/Network/#type-CookieParam
*/
export declare function bidiToCdpCookie(params: Storage.SetCookieParameters, partitionKey: Storage.PartitionKey): Protocol.Network.CookieParam;

View File

@@ -0,0 +1,199 @@
"use strict";
/*
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.bidiToCdpCookie = exports.cdpToBiDiCookie = exports.cdpAuthChallengeResponseFromBidiAuthContinueWithAuthAction = exports.cdpFetchHeadersFromBidiNetworkHeaders = exports.bidiNetworkHeadersFromCdpFetchHeaders = exports.cdpNetworkHeadersFromBidiNetworkHeaders = exports.bidiNetworkHeadersFromCdpNetworkHeaders = exports.computeHeadersSize = void 0;
const ErrorResponse_1 = require("../../../protocol/ErrorResponse");
function computeHeadersSize(headers) {
const requestHeaders = headers.reduce((acc, header) => {
return `${acc}${header.name}: ${header.value.value}\r\n`;
}, '');
return new TextEncoder().encode(requestHeaders).length;
}
exports.computeHeadersSize = computeHeadersSize;
/** Converts from CDP Network domain headers to Bidi network headers. */
function bidiNetworkHeadersFromCdpNetworkHeaders(headers) {
if (!headers) {
return [];
}
return Object.entries(headers).map(([name, value]) => ({
name,
value: {
type: 'string',
value,
},
}));
}
exports.bidiNetworkHeadersFromCdpNetworkHeaders = bidiNetworkHeadersFromCdpNetworkHeaders;
/** Converts from Bidi network headers to CDP Network domain headers. */
function cdpNetworkHeadersFromBidiNetworkHeaders(headers) {
if (headers === undefined) {
return undefined;
}
return headers.reduce((result, header) => {
// TODO: Distinguish between string and bytes?
result[header.name] = header.value.value;
return result;
}, {});
}
exports.cdpNetworkHeadersFromBidiNetworkHeaders = cdpNetworkHeadersFromBidiNetworkHeaders;
/** Converts from CDP Fetch domain header entries to Bidi network headers. */
function bidiNetworkHeadersFromCdpFetchHeaders(headers) {
if (!headers) {
return [];
}
return headers.map(({ name, value }) => ({
name,
value: {
type: 'string',
value,
},
}));
}
exports.bidiNetworkHeadersFromCdpFetchHeaders = bidiNetworkHeadersFromCdpFetchHeaders;
/** Converts from Bidi network headers to CDP Fetch domain header entries. */
function cdpFetchHeadersFromBidiNetworkHeaders(headers) {
if (headers === undefined) {
return undefined;
}
return headers.map(({ name, value }) => ({
name,
value: value.value,
}));
}
exports.cdpFetchHeadersFromBidiNetworkHeaders = cdpFetchHeadersFromBidiNetworkHeaders;
/** Converts from Bidi auth action to CDP auth challenge response. */
function cdpAuthChallengeResponseFromBidiAuthContinueWithAuthAction(action) {
switch (action) {
case 'default':
return 'Default';
case 'cancel':
return 'CancelAuth';
case 'provideCredentials':
return 'ProvideCredentials';
}
}
exports.cdpAuthChallengeResponseFromBidiAuthContinueWithAuthAction = cdpAuthChallengeResponseFromBidiAuthContinueWithAuthAction;
/**
* Converts from CDP Network domain cookie to BiDi network cookie.
* * https://chromedevtools.github.io/devtools-protocol/tot/Network/#type-Cookie
* * https://w3c.github.io/webdriver-bidi/#type-network-Cookie
*/
function cdpToBiDiCookie(cookie) {
const result = {
name: cookie.name,
value: { type: 'string', value: cookie.value },
domain: cookie.domain,
path: cookie.path,
size: cookie.size,
httpOnly: cookie.httpOnly,
secure: cookie.secure,
sameSite: cookie.sameSite === undefined
? "none" /* Network.SameSite.None */
: sameSiteCdpToBiDi(cookie.sameSite),
...(cookie.expires >= 0 ? { expiry: cookie.expires } : undefined),
};
// Extending with CDP-specific properties with `goog:` prefix.
result[`goog:session`] = cookie.session;
result[`goog:priority`] = cookie.priority;
result[`goog:sameParty`] = cookie.sameParty;
result[`goog:sourceScheme`] = cookie.sourceScheme;
result[`goog:sourcePort`] = cookie.sourcePort;
if (cookie.partitionKey !== undefined) {
result[`goog:partitionKey`] = cookie.partitionKey;
}
if (cookie.partitionKeyOpaque !== undefined) {
result[`goog:partitionKeyOpaque`] = cookie.partitionKeyOpaque;
}
return result;
}
exports.cdpToBiDiCookie = cdpToBiDiCookie;
/**
* Converts from BiDi set network cookie params to CDP Network domain cookie.
* * https://w3c.github.io/webdriver-bidi/#type-network-Cookie
* * https://chromedevtools.github.io/devtools-protocol/tot/Network/#type-CookieParam
*/
function bidiToCdpCookie(params, partitionKey) {
if (params.cookie.value.type !== 'string') {
// CDP supports only string values in cookies.
throw new ErrorResponse_1.UnsupportedOperationException('Only string cookie values are supported');
}
const deserializedValue = params.cookie.value.value;
const result = {
name: params.cookie.name,
value: deserializedValue,
domain: params.cookie.domain,
path: params.cookie.path ?? '/',
secure: params.cookie.secure ?? false,
httpOnly: params.cookie.httpOnly ?? false,
// CDP's `partitionKey` is the BiDi's `partition.sourceOrigin`.
...(partitionKey.sourceOrigin !== undefined && {
partitionKey: partitionKey.sourceOrigin,
}),
...(params.cookie.expiry !== undefined && {
expires: params.cookie.expiry,
}),
...(params.cookie.sameSite !== undefined && {
sameSite: sameSiteBiDiToCdp(params.cookie.sameSite),
}),
};
// Extending with CDP-specific properties with `goog:` prefix.
if (params.cookie[`goog:url`] !== undefined) {
result.url = params.cookie[`goog:url`];
}
if (params.cookie[`goog:priority`] !== undefined) {
result.priority = params.cookie[`goog:priority`];
}
if (params.cookie[`goog:sameParty`] !== undefined) {
result.sameParty = params.cookie[`goog:sameParty`];
}
if (params.cookie[`goog:sourceScheme`] !== undefined) {
result.sourceScheme = params.cookie[`goog:sourceScheme`];
}
if (params.cookie[`goog:sourcePort`] !== undefined) {
result.sourcePort = params.cookie[`goog:sourcePort`];
}
return result;
}
exports.bidiToCdpCookie = bidiToCdpCookie;
function sameSiteCdpToBiDi(sameSite) {
switch (sameSite) {
case 'Strict':
return "strict" /* Network.SameSite.Strict */;
case 'None':
return "none" /* Network.SameSite.None */;
case 'Lax':
return "lax" /* Network.SameSite.Lax */;
default:
// Defaults to `Lax`:
// https://web.dev/articles/samesite-cookies-explained#samesitelax_by_default
return "lax" /* Network.SameSite.Lax */;
}
}
function sameSiteBiDiToCdp(sameSite) {
switch (sameSite) {
case "strict" /* Network.SameSite.Strict */:
return 'Strict';
case "lax" /* Network.SameSite.Lax */:
return 'Lax';
case "none" /* Network.SameSite.None */:
return 'None';
}
throw new ErrorResponse_1.InvalidArgumentException(`Unknown 'sameSite' value ${sameSite}`);
}
//# sourceMappingURL=NetworkUtils.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,23 @@
/**
* Copyright 2024 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { CdpClient } from '../../../cdp/CdpClient.js';
import { type EmptyResult, type Permissions } from '../../../protocol/protocol.js';
export declare class PermissionsProcessor {
#private;
constructor(browserCdpClient: CdpClient);
setPermissions(params: Permissions.SetPermissionParameters): Promise<EmptyResult>;
}

View File

@@ -0,0 +1,49 @@
"use strict";
/**
* Copyright 2024 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.PermissionsProcessor = void 0;
const protocol_js_1 = require("../../../protocol/protocol.js");
class PermissionsProcessor {
#browserCdpClient;
constructor(browserCdpClient) {
this.#browserCdpClient = browserCdpClient;
}
async setPermissions(params) {
try {
await this.#browserCdpClient.sendCommand('Browser.setPermission', {
origin: params.origin,
permission: {
name: params.descriptor.name,
},
setting: params.state,
});
}
catch (err) {
if (err.message ===
`Permission can't be granted to opaque origins.`) {
// Return success if the origin is not valid (does not match any
// existing origins).
return {};
}
throw new protocol_js_1.InvalidArgumentException(err.message);
}
return {};
}
}
exports.PermissionsProcessor = PermissionsProcessor;
//# sourceMappingURL=PermissionsProcessor.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"PermissionsProcessor.js","sourceRoot":"","sources":["../../../../../src/bidiMapper/domains/permissions/PermissionsProcessor.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;GAeG;;;AAGH,+DAIuC;AAEvC,MAAa,oBAAoB;IAC/B,iBAAiB,CAAY;IAE7B,YAAY,gBAA2B;QACrC,IAAI,CAAC,iBAAiB,GAAG,gBAAgB,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,cAAc,CAClB,MAA2C;QAE3C,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,iBAAiB,CAAC,WAAW,CAAC,uBAAuB,EAAE;gBAChE,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,UAAU,EAAE;oBACV,IAAI,EAAE,MAAM,CAAC,UAAU,CAAC,IAAI;iBAC7B;gBACD,OAAO,EAAE,MAAM,CAAC,KAAK;aACtB,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IACG,GAAa,CAAC,OAAO;gBACtB,gDAAgD,EAChD,CAAC;gBACD,gEAAgE;gBAChE,qBAAqB;gBACrB,OAAO,EAAE,CAAC;YACZ,CAAC;YACD,MAAM,IAAI,sCAAwB,CAAE,GAAa,CAAC,OAAO,CAAC,CAAC;QAC7D,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;CACF;AA/BD,oDA+BC"}

View File

@@ -0,0 +1,30 @@
import { Script } from '../../../protocol/protocol.js';
import { type LoggerFn } from '../../../utils/log.js';
import type { EventManager } from '../session/EventManager.js';
import type { Realm } from './Realm.js';
/**
* Used to send messages from realm to BiDi user.
*/
export declare class ChannelProxy {
#private;
constructor(channel: Script.ChannelProperties, logger?: LoggerFn);
/**
* Creates a channel proxy in the given realm, initialises listener and
* returns a handle to `sendMessage` delegate.
*/
init(realm: Realm, eventManager: EventManager): Promise<Script.Handle>;
/** Gets a ChannelProxy from window and returns its handle. */
startListenerFromWindow(realm: Realm, eventManager: EventManager): Promise<void>;
/**
* String to be evaluated to create a ProxyChannel and put it to window.
* Returns the delegate `sendMessage`. Used to provide an argument for preload
* script. Does the following:
* 1. Creates a ChannelProxy.
* 2. Puts the ChannelProxy to window['${this.#id}'] or resolves the promise
* by calling delegate stored in window['${this.#id}'].
* This is needed because `#getHandleFromWindow` can be called before or
* after this method.
* 3. Returns the delegate `sendMessage` of the created ChannelProxy.
*/
getEvalInWindowStr(): string;
}

View File

@@ -0,0 +1,233 @@
"use strict";
/*
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.ChannelProxy = void 0;
const protocol_js_1 = require("../../../protocol/protocol.js");
const log_js_1 = require("../../../utils/log.js");
const uuid_js_1 = require("../../../utils/uuid.js");
/**
* Used to send messages from realm to BiDi user.
*/
class ChannelProxy {
#properties;
#id = (0, uuid_js_1.uuidv4)();
#logger;
constructor(channel, logger) {
this.#properties = channel;
this.#logger = logger;
}
/**
* Creates a channel proxy in the given realm, initialises listener and
* returns a handle to `sendMessage` delegate.
*/
async init(realm, eventManager) {
const channelHandle = await ChannelProxy.#createAndGetHandleInRealm(realm);
const sendMessageHandle = await ChannelProxy.#createSendMessageHandle(realm, channelHandle);
void this.#startListener(realm, channelHandle, eventManager);
return sendMessageHandle;
}
/** Gets a ChannelProxy from window and returns its handle. */
async startListenerFromWindow(realm, eventManager) {
try {
const channelHandle = await this.#getHandleFromWindow(realm);
void this.#startListener(realm, channelHandle, eventManager);
}
catch (error) {
this.#logger?.(log_js_1.LogType.debugError, error);
}
}
/**
* Evaluation string which creates a ChannelProxy object on the client side.
*/
static #createChannelProxyEvalStr() {
const functionStr = String(() => {
const queue = [];
let queueNonEmptyResolver = null;
return {
/**
* Gets a promise, which is resolved as soon as a message occurs
* in the queue.
*/
async getMessage() {
const onMessage = queue.length > 0
? Promise.resolve()
: new Promise((resolve) => {
queueNonEmptyResolver = resolve;
});
await onMessage;
return queue.shift();
},
/**
* Adds a message to the queue.
* Resolves the pending promise if needed.
*/
sendMessage(message) {
queue.push(message);
if (queueNonEmptyResolver !== null) {
queueNonEmptyResolver();
queueNonEmptyResolver = null;
}
},
};
});
return `(${functionStr})()`;
}
/** Creates a ChannelProxy in the given realm. */
static async #createAndGetHandleInRealm(realm) {
const createChannelHandleResult = await realm.cdpClient.sendCommand('Runtime.evaluate', {
expression: this.#createChannelProxyEvalStr(),
contextId: realm.executionContextId,
serializationOptions: {
serialization: "idOnly" /* Protocol.Runtime.SerializationOptionsSerialization.IdOnly */,
},
});
if (createChannelHandleResult.exceptionDetails ||
createChannelHandleResult.result.objectId === undefined) {
throw new Error(`Cannot create channel`);
}
return createChannelHandleResult.result.objectId;
}
/** Gets a handle to `sendMessage` delegate from the ChannelProxy handle. */
static async #createSendMessageHandle(realm, channelHandle) {
const sendMessageArgResult = await realm.cdpClient.sendCommand('Runtime.callFunctionOn', {
functionDeclaration: String((channelHandle) => {
return channelHandle.sendMessage;
}),
arguments: [{ objectId: channelHandle }],
executionContextId: realm.executionContextId,
serializationOptions: {
serialization: "idOnly" /* Protocol.Runtime.SerializationOptionsSerialization.IdOnly */,
},
});
// TODO: check for exceptionDetails.
return sendMessageArgResult.result.objectId;
}
/** Starts listening for the channel events of the provided ChannelProxy. */
async #startListener(realm, channelHandle, eventManager) {
// noinspection InfiniteLoopJS
for (;;) {
try {
const message = await realm.cdpClient.sendCommand('Runtime.callFunctionOn', {
functionDeclaration: String(async (channelHandle) => await channelHandle.getMessage()),
arguments: [
{
objectId: channelHandle,
},
],
awaitPromise: true,
executionContextId: realm.executionContextId,
serializationOptions: {
serialization: "deep" /* Protocol.Runtime.SerializationOptionsSerialization.Deep */,
maxDepth: this.#properties.serializationOptions?.maxObjectDepth ??
undefined,
},
});
if (message.exceptionDetails) {
throw message.exceptionDetails;
}
for (const browsingContext of realm.associatedBrowsingContexts) {
eventManager.registerEvent({
type: 'event',
method: protocol_js_1.ChromiumBidi.Script.EventNames.Message,
params: {
channel: this.#properties.channel,
data: realm.cdpToBidiValue(message, this.#properties.ownership ?? "none" /* Script.ResultOwnership.None */),
source: realm.source,
},
}, browsingContext.id);
}
}
catch (error) {
// If an error is thrown, then the channel is permanently broken, so we
// exit the loop.
this.#logger?.(log_js_1.LogType.debugError, error);
break;
}
}
}
/**
* Returns a handle of ChannelProxy from window's property which was set there
* by `getEvalInWindowStr`. If window property is not set yet, sets a promise
* resolver to the window property, so that `getEvalInWindowStr` can resolve
* the promise later on with the channel.
* This is needed because `getEvalInWindowStr` can be called before or
* after this method.
*/
async #getHandleFromWindow(realm) {
const channelHandleResult = await realm.cdpClient.sendCommand('Runtime.callFunctionOn', {
functionDeclaration: String((id) => {
const w = window;
if (w[id] === undefined) {
// The channelProxy is not created yet. Create a promise, put the
// resolver to window property and return the promise.
// `getEvalInWindowStr` will resolve the promise later.
return new Promise((resolve) => (w[id] = resolve));
}
// The channelProxy is already created by `getEvalInWindowStr` and
// is set into window property. Return it.
const channelProxy = w[id];
delete w[id];
return channelProxy;
}),
arguments: [{ value: this.#id }],
executionContextId: realm.executionContextId,
awaitPromise: true,
serializationOptions: {
serialization: "idOnly" /* Protocol.Runtime.SerializationOptionsSerialization.IdOnly */,
},
});
if (channelHandleResult.exceptionDetails !== undefined ||
channelHandleResult.result.objectId === undefined) {
throw new Error(`ChannelHandle not found in window["${this.#id}"]`);
}
return channelHandleResult.result.objectId;
}
/**
* String to be evaluated to create a ProxyChannel and put it to window.
* Returns the delegate `sendMessage`. Used to provide an argument for preload
* script. Does the following:
* 1. Creates a ChannelProxy.
* 2. Puts the ChannelProxy to window['${this.#id}'] or resolves the promise
* by calling delegate stored in window['${this.#id}'].
* This is needed because `#getHandleFromWindow` can be called before or
* after this method.
* 3. Returns the delegate `sendMessage` of the created ChannelProxy.
*/
getEvalInWindowStr() {
const delegate = String((id, channelProxy) => {
const w = window;
if (w[id] === undefined) {
// `#getHandleFromWindow` is not initialized yet, and will get the
// channelProxy later.
w[id] = channelProxy;
}
else {
// `#getHandleFromWindow` is already set a delegate to window property
// and is waiting for it to be called with the channelProxy.
w[id](channelProxy);
delete w[id];
}
return channelProxy.sendMessage;
});
const channelProxyEval = ChannelProxy.#createChannelProxyEvalStr();
return `(${delegate})('${this.#id}',${channelProxyEval})`;
}
}
exports.ChannelProxy = ChannelProxy;
//# sourceMappingURL=ChannelProxy.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,32 @@
/**
* Copyright 2024 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type { Protocol } from 'devtools-protocol';
import type { CdpClient } from '../../../cdp/CdpClient.js';
import type { Script } from '../../../protocol/protocol.js';
import type { LoggerFn } from '../../../utils/log.js';
import type { BrowsingContextImpl } from '../context/BrowsingContextImpl.js';
import type { EventManager } from '../session/EventManager.js';
import { Realm } from './Realm.js';
import type { RealmStorage } from './RealmStorage.js';
export declare class DedicatedWorkerRealm extends Realm {
#private;
constructor(cdpClient: CdpClient, eventManager: EventManager, executionContextId: Protocol.Runtime.ExecutionContextId, logger: LoggerFn | undefined, origin: string, ownerRealm: Realm, realmId: Script.Realm, realmStorage: RealmStorage);
get associatedBrowsingContexts(): BrowsingContextImpl[];
get realmType(): 'dedicated-worker';
get source(): Script.Source;
get realmInfo(): Script.DedicatedWorkerRealmInfo;
}

View File

@@ -0,0 +1,51 @@
"use strict";
/**
* Copyright 2024 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.DedicatedWorkerRealm = void 0;
const Realm_js_1 = require("./Realm.js");
class DedicatedWorkerRealm extends Realm_js_1.Realm {
#ownerRealm;
constructor(cdpClient, eventManager, executionContextId, logger, origin, ownerRealm, realmId, realmStorage) {
super(cdpClient, eventManager, executionContextId, logger, origin, realmId, realmStorage);
this.#ownerRealm = ownerRealm;
this.initialize();
}
get associatedBrowsingContexts() {
return this.#ownerRealm.associatedBrowsingContexts;
}
get realmType() {
return 'dedicated-worker';
}
get source() {
return {
realm: this.realmId,
// This is a hack to make Puppeteer able to track workers.
// TODO: remove after Puppeteer tracks workers by owners and use the base version.
context: this.associatedBrowsingContexts[0]?.id,
};
}
get realmInfo() {
return {
...this.baseInfo,
type: this.realmType,
owners: [this.#ownerRealm.realmId],
};
}
}
exports.DedicatedWorkerRealm = DedicatedWorkerRealm;
//# sourceMappingURL=DedicatedWorkerRealm.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"DedicatedWorkerRealm.js","sourceRoot":"","sources":["../../../../../src/bidiMapper/domains/script/DedicatedWorkerRealm.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;GAeG;;;AAUH,yCAAiC;AAGjC,MAAa,oBAAqB,SAAQ,gBAAK;IACpC,WAAW,CAAQ;IAE5B,YACE,SAAoB,EACpB,YAA0B,EAC1B,kBAAuD,EACvD,MAA4B,EAC5B,MAAc,EACd,UAAiB,EACjB,OAAqB,EACrB,YAA0B;QAE1B,KAAK,CACH,SAAS,EACT,YAAY,EACZ,kBAAkB,EAClB,MAAM,EACN,MAAM,EACN,OAAO,EACP,YAAY,CACb,CAAC;QAEF,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC;QAE9B,IAAI,CAAC,UAAU,EAAE,CAAC;IACpB,CAAC;IAED,IAAa,0BAA0B;QACrC,OAAO,IAAI,CAAC,WAAW,CAAC,0BAA0B,CAAC;IACrD,CAAC;IAED,IAAa,SAAS;QACpB,OAAO,kBAAkB,CAAC;IAC5B,CAAC;IAED,IAAa,MAAM;QACjB,OAAO;YACL,KAAK,EAAE,IAAI,CAAC,OAAO;YACnB,0DAA0D;YAC1D,kFAAkF;YAClF,OAAO,EAAE,IAAI,CAAC,0BAA0B,CAAC,CAAC,CAAC,EAAE,EAAE;SAChD,CAAC;IACJ,CAAC;IAED,IAAa,SAAS;QACpB,OAAO;YACL,GAAG,IAAI,CAAC,QAAQ;YAChB,IAAI,EAAE,IAAI,CAAC,SAAS;YACpB,MAAM,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC;SACnC,CAAC;IACJ,CAAC;CACF;AApDD,oDAoDC"}

View File

@@ -0,0 +1,41 @@
import type { Protocol } from 'devtools-protocol';
import type { BrowsingContext, Script } from '../../../protocol/protocol.js';
import type { LoggerFn } from '../../../utils/log.js';
import type { CdpTarget } from '../context/CdpTarget.js';
import { ChannelProxy } from './ChannelProxy.js';
/**
* BiDi IDs are generated by the server and are unique within contexts.
*
* CDP preload script IDs are generated by the client and are unique
* within sessions.
*
* The mapping between BiDi and CDP preload script IDs is 1:many.
* BiDi IDs are needed by the mapper to keep track of potential multiple CDP IDs
* in the client.
*/
export declare class PreloadScript {
#private;
get id(): string;
get targetIds(): Set<Protocol.Target.TargetID>;
constructor(params: Script.AddPreloadScriptParameters, logger?: LoggerFn);
/** Channels of the preload script. */
get channels(): ChannelProxy[];
/** Contexts of the preload script, if any */
get contexts(): BrowsingContext.BrowsingContext[] | undefined;
/**
* Adds the script to the given CDP targets by calling the
* `Page.addScriptToEvaluateOnNewDocument` command.
*/
initInTargets(cdpTargets: Iterable<CdpTarget>, runImmediately: boolean): Promise<void>;
/**
* Adds the script to the given CDP target by calling the
* `Page.addScriptToEvaluateOnNewDocument` command.
*/
initInTarget(cdpTarget: CdpTarget, runImmediately: boolean): Promise<void>;
/**
* Removes this script from all CDP targets.
*/
remove(): Promise<void>;
/** Removes the provided cdp target from the list of cdp preload scripts. */
dispose(cdpTargetId: Protocol.Target.TargetID): void;
}

View File

@@ -0,0 +1,124 @@
"use strict";
/*
* Copyright 2023 Google LLC.
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.PreloadScript = void 0;
const uuid_js_1 = require("../../../utils/uuid.js");
const ChannelProxy_js_1 = require("./ChannelProxy.js");
/**
* BiDi IDs are generated by the server and are unique within contexts.
*
* CDP preload script IDs are generated by the client and are unique
* within sessions.
*
* The mapping between BiDi and CDP preload script IDs is 1:many.
* BiDi IDs are needed by the mapper to keep track of potential multiple CDP IDs
* in the client.
*/
class PreloadScript {
/** BiDi ID, an automatically generated UUID. */
#id = (0, uuid_js_1.uuidv4)();
/** CDP preload scripts. */
#cdpPreloadScripts = [];
/** The script itself, in a format expected by the spec i.e. a function. */
#functionDeclaration;
/** Targets, in which the preload script is initialized. */
#targetIds = new Set();
/** Channels to be added as arguments to functionDeclaration. */
#channels;
/** The script sandbox / world name. */
#sandbox;
/** The browsing contexts to execute the preload scripts in, if any. */
#contexts;
get id() {
return this.#id;
}
get targetIds() {
return this.#targetIds;
}
constructor(params, logger) {
this.#channels =
params.arguments?.map((a) => new ChannelProxy_js_1.ChannelProxy(a.value, logger)) ?? [];
this.#functionDeclaration = params.functionDeclaration;
this.#sandbox = params.sandbox;
this.#contexts = params.contexts;
}
/** Channels of the preload script. */
get channels() {
return this.#channels;
}
/** Contexts of the preload script, if any */
get contexts() {
return this.#contexts;
}
/**
* String to be evaluated. Wraps user-provided function so that the following
* steps are run:
* 1. Create channels.
* 2. Store the created channels in window.
* 3. Call the user-provided function with channels as arguments.
*/
#getEvaluateString() {
const channelsArgStr = `[${this.channels
.map((c) => c.getEvalInWindowStr())
.join(', ')}]`;
return `(()=>{(${this.#functionDeclaration})(...${channelsArgStr})})()`;
}
/**
* Adds the script to the given CDP targets by calling the
* `Page.addScriptToEvaluateOnNewDocument` command.
*/
async initInTargets(cdpTargets, runImmediately) {
await Promise.all(Array.from(cdpTargets).map((cdpTarget) => this.initInTarget(cdpTarget, runImmediately)));
}
/**
* Adds the script to the given CDP target by calling the
* `Page.addScriptToEvaluateOnNewDocument` command.
*/
async initInTarget(cdpTarget, runImmediately) {
const addCdpPreloadScriptResult = await cdpTarget.cdpClient.sendCommand('Page.addScriptToEvaluateOnNewDocument', {
source: this.#getEvaluateString(),
worldName: this.#sandbox,
runImmediately,
});
this.#cdpPreloadScripts.push({
target: cdpTarget,
preloadScriptId: addCdpPreloadScriptResult.identifier,
});
this.#targetIds.add(cdpTarget.id);
}
/**
* Removes this script from all CDP targets.
*/
async remove() {
for (const cdpPreloadScript of this.#cdpPreloadScripts) {
const cdpTarget = cdpPreloadScript.target;
const cdpPreloadScriptId = cdpPreloadScript.preloadScriptId;
await cdpTarget.cdpClient.sendCommand('Page.removeScriptToEvaluateOnNewDocument', {
identifier: cdpPreloadScriptId,
});
}
}
/** Removes the provided cdp target from the list of cdp preload scripts. */
dispose(cdpTargetId) {
this.#cdpPreloadScripts = this.#cdpPreloadScripts.filter((cdpPreloadScript) => cdpPreloadScript.target?.id !== cdpTargetId);
this.#targetIds.delete(cdpTargetId);
}
}
exports.PreloadScript = PreloadScript;
//# sourceMappingURL=PreloadScript.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"PreloadScript.js","sourceRoot":"","sources":["../../../../../src/bidiMapper/domains/script/PreloadScript.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,19 @@
import type { CdpTarget } from '../context/CdpTarget.js';
import type { PreloadScript } from './PreloadScript.js';
/** PreloadScripts can be filtered by BiDi ID or target ID. */
export type PreloadScriptFilter = Partial<{
id: PreloadScript['id'];
targetId: CdpTarget['id'];
global: boolean;
}>;
/**
* Container class for preload scripts.
*/
export declare class PreloadScriptStorage {
#private;
/** Finds all entries that match the given filter. */
find(filter?: PreloadScriptFilter): PreloadScript[];
add(preloadScript: PreloadScript): void;
/** Deletes all BiDi preload script entries that match the given filter. */
remove(filter?: PreloadScriptFilter): void;
}

Some files were not shown because too many files have changed in this diff Show More