Add Brave CDP automation, replace Oracle browser mode
Connects to user's running Brave via Chrome DevTools Protocol to automate ChatGPT interaction. Uses puppeteer-core to open a tab, send the prompt, wait for response, and extract the result. No cookies, no separate profiles, no copy/paste. Just connects to the browser where the user is already logged in. One-time setup: relaunch Brave with --remote-debugging-port=9222 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
13
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/network/CollectorsStorage.d.ts
generated
vendored
Normal file
13
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/network/CollectorsStorage.d.ts
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { Browser, BrowsingContext } from '../../../protocol/generated/webdriver-bidi.js';
|
||||
import { Network } from '../../../protocol/generated/webdriver-bidi.js';
|
||||
import { type LoggerFn } from '../../../utils/log.js';
|
||||
import type { NetworkRequest } from './NetworkRequest.js';
|
||||
export declare class CollectorsStorage {
|
||||
#private;
|
||||
constructor(maxEncodedDataSize: number, logger?: LoggerFn);
|
||||
addDataCollector(params: Network.AddDataCollectorParameters): `${string}-${string}-${string}-${string}-${string}`;
|
||||
isCollected(requestId: Network.Request, dataType?: Network.DataType, collectorId?: string): boolean;
|
||||
disownData(requestId: Network.Request, dataType: Network.DataType, collectorId?: string): void;
|
||||
collectIfNeeded(request: NetworkRequest, dataType: Network.DataType, topLevelBrowsingContext: BrowsingContext.BrowsingContext, userContext: Browser.UserContext): void;
|
||||
removeDataCollector(collectorId: Network.Collector): Network.Request[];
|
||||
}
|
||||
153
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/network/CollectorsStorage.js
generated
vendored
Normal file
153
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/network/CollectorsStorage.js
generated
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
"use strict";
|
||||
/*
|
||||
* Copyright 2025 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.CollectorsStorage = void 0;
|
||||
const ErrorResponse_js_1 = require("../../../protocol/ErrorResponse.js");
|
||||
const log_js_1 = require("../../../utils/log.js");
|
||||
const uuid_js_1 = require("../../../utils/uuid.js");
|
||||
class CollectorsStorage {
|
||||
#collectors = new Map();
|
||||
#responseCollectors = new Map();
|
||||
#requestBodyCollectors = new Map();
|
||||
#maxEncodedDataSize;
|
||||
#logger;
|
||||
constructor(maxEncodedDataSize, logger) {
|
||||
this.#maxEncodedDataSize = maxEncodedDataSize;
|
||||
this.#logger = logger;
|
||||
}
|
||||
addDataCollector(params) {
|
||||
if (params.maxEncodedDataSize < 1 ||
|
||||
params.maxEncodedDataSize > this.#maxEncodedDataSize) {
|
||||
// 200 MB is the default limit in CDP:
|
||||
// https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/inspector/inspector_network_agent.cc;drc=da1f749634c9a401cc756f36c2e6ce233e1c9b4d;l=133
|
||||
throw new ErrorResponse_js_1.InvalidArgumentException(`Max encoded data size should be between 1 and ${this.#maxEncodedDataSize}`);
|
||||
}
|
||||
const collectorId = (0, uuid_js_1.uuidv4)();
|
||||
this.#collectors.set(collectorId, params);
|
||||
return collectorId;
|
||||
}
|
||||
isCollected(requestId, dataType, collectorId) {
|
||||
if (collectorId !== undefined && !this.#collectors.has(collectorId)) {
|
||||
throw new ErrorResponse_js_1.NoSuchNetworkCollectorException(`Unknown collector ${collectorId}`);
|
||||
}
|
||||
if (dataType === undefined) {
|
||||
return (this.isCollected(requestId, "response" /* Network.DataType.Response */, collectorId) ||
|
||||
this.isCollected(requestId, "request" /* Network.DataType.Request */, collectorId));
|
||||
}
|
||||
const requestToCollectorsMap = this.#getRequestToCollectorMap(dataType).get(requestId);
|
||||
if (requestToCollectorsMap === undefined ||
|
||||
requestToCollectorsMap.size === 0) {
|
||||
return false;
|
||||
}
|
||||
if (collectorId === undefined) {
|
||||
// There is at least 1 collector for the data.
|
||||
return true;
|
||||
}
|
||||
if (!requestToCollectorsMap.has(collectorId)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#getRequestToCollectorMap(dataType) {
|
||||
switch (dataType) {
|
||||
case "response" /* Network.DataType.Response */:
|
||||
return this.#responseCollectors;
|
||||
case "request" /* Network.DataType.Request */:
|
||||
return this.#requestBodyCollectors;
|
||||
default:
|
||||
throw new ErrorResponse_js_1.UnsupportedOperationException(`Unsupported data type ${dataType}`);
|
||||
}
|
||||
}
|
||||
disownData(requestId, dataType, collectorId) {
|
||||
const requestToCollectorsMap = this.#getRequestToCollectorMap(dataType);
|
||||
if (collectorId !== undefined) {
|
||||
requestToCollectorsMap.get(requestId)?.delete(collectorId);
|
||||
}
|
||||
if (collectorId === undefined ||
|
||||
requestToCollectorsMap.get(requestId)?.size === 0) {
|
||||
requestToCollectorsMap.delete(requestId);
|
||||
}
|
||||
}
|
||||
#shouldCollectRequest(collectorId, request, dataType, topLevelBrowsingContext, userContext) {
|
||||
const collector = this.#collectors.get(collectorId);
|
||||
if (collector === undefined) {
|
||||
throw new ErrorResponse_js_1.NoSuchNetworkCollectorException(`Unknown collector ${collectorId}`);
|
||||
}
|
||||
if (collector.userContexts &&
|
||||
!collector.userContexts.includes(userContext)) {
|
||||
// Collector is aimed for a different user context.
|
||||
return false;
|
||||
}
|
||||
if (collector.contexts &&
|
||||
!collector.contexts.includes(topLevelBrowsingContext)) {
|
||||
// Collector is aimed for a different top-level browsing context.
|
||||
return false;
|
||||
}
|
||||
if (!collector.dataTypes.includes(dataType)) {
|
||||
// Collector is aimed for a different data type.
|
||||
return false;
|
||||
}
|
||||
if (dataType === "request" /* Network.DataType.Request */ &&
|
||||
request.bodySize > collector.maxEncodedDataSize) {
|
||||
this.#logger?.(log_js_1.LogType.debug, `Request's ${request.id} body size is too big for the collector ${collectorId}`);
|
||||
return false;
|
||||
}
|
||||
if (dataType === "response" /* Network.DataType.Response */ &&
|
||||
request.encodedResponseBodySize > collector.maxEncodedDataSize) {
|
||||
this.#logger?.(log_js_1.LogType.debug, `Request's ${request.id} response is too big for the collector ${collectorId}`);
|
||||
return false;
|
||||
}
|
||||
this.#logger?.(log_js_1.LogType.debug, `Collector ${collectorId} collected ${dataType} of ${request.id}`);
|
||||
return true;
|
||||
}
|
||||
collectIfNeeded(request, dataType, topLevelBrowsingContext, userContext) {
|
||||
const collectorIds = [...this.#collectors.keys()].filter((collectorId) => this.#shouldCollectRequest(collectorId, request, dataType, topLevelBrowsingContext, userContext));
|
||||
if (collectorIds.length > 0) {
|
||||
this.#getRequestToCollectorMap(dataType).set(request.id, new Set(collectorIds));
|
||||
}
|
||||
}
|
||||
removeDataCollector(collectorId) {
|
||||
if (!this.#collectors.has(collectorId)) {
|
||||
throw new ErrorResponse_js_1.NoSuchNetworkCollectorException(`Collector ${collectorId} does not exist`);
|
||||
}
|
||||
this.#collectors.delete(collectorId);
|
||||
const affectedRequests = [];
|
||||
// Clean up collected responses.
|
||||
for (const [requestId, collectorIds] of this.#responseCollectors) {
|
||||
if (collectorIds.has(collectorId)) {
|
||||
collectorIds.delete(collectorId);
|
||||
if (collectorIds.size === 0) {
|
||||
this.#responseCollectors.delete(requestId);
|
||||
affectedRequests.push(requestId);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const [requestId, collectorIds] of this.#requestBodyCollectors) {
|
||||
if (collectorIds.has(collectorId)) {
|
||||
collectorIds.delete(collectorId);
|
||||
if (collectorIds.size === 0) {
|
||||
this.#requestBodyCollectors.delete(requestId);
|
||||
affectedRequests.push(requestId);
|
||||
}
|
||||
}
|
||||
}
|
||||
return affectedRequests;
|
||||
}
|
||||
}
|
||||
exports.CollectorsStorage = CollectorsStorage;
|
||||
//# sourceMappingURL=CollectorsStorage.js.map
|
||||
1
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/network/CollectorsStorage.js.map
generated
vendored
Normal file
1
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/network/CollectorsStorage.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"CollectorsStorage.js","sourceRoot":"","sources":["../../../../../src/bidiMapper/modules/network/CollectorsStorage.ts"],"names":[],"mappings":"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"}
|
||||
54
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/network/NetworkProcessor.d.ts
generated
vendored
Normal file
54
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/network/NetworkProcessor.d.ts
generated
vendored
Normal 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 { Protocol } from 'devtools-protocol';
|
||||
import { Network, type EmptyResult } from '../../../protocol/protocol.js';
|
||||
import type { ContextConfigStorage } from '../browser/ContextConfigStorage.js';
|
||||
import type { UserContextStorage } from '../browser/UserContextStorage.js';
|
||||
import type { BrowsingContextStorage } from '../context/BrowsingContextStorage.js';
|
||||
import type { NetworkStorage } from './NetworkStorage.js';
|
||||
import { type ParsedUrlPattern } from './NetworkUtils.js';
|
||||
/** Dispatches Network module commands. */
|
||||
export declare class NetworkProcessor {
|
||||
#private;
|
||||
constructor(browsingContextStorage: BrowsingContextStorage, networkStorage: NetworkStorage, userContextStorage: UserContextStorage, contextConfigStorage: ContextConfigStorage);
|
||||
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>;
|
||||
setCacheBehavior(params: Network.SetCacheBehaviorParameters): Promise<EmptyResult>;
|
||||
/**
|
||||
* Validate https://fetch.spec.whatwg.org/#header-value
|
||||
*/
|
||||
static validateHeaders(headers: Network.Header[]): void;
|
||||
static isMethodValid(method: string): boolean;
|
||||
/**
|
||||
* Attempts to parse the given url.
|
||||
* Throws an InvalidArgumentException if the url is invalid.
|
||||
*/
|
||||
static parseUrlString(url: string): URL;
|
||||
static parseUrlPatterns(urlPatterns: Network.UrlPattern[]): ParsedUrlPattern[];
|
||||
static wrapInterceptionError(error: any): any;
|
||||
addDataCollector(params: Network.AddDataCollectorParameters): Promise<Network.AddDataCollectorResult>;
|
||||
getData(params: Network.GetDataParameters): Promise<Network.GetDataResult>;
|
||||
removeDataCollector(params: Network.RemoveDataCollectorParameters): Promise<EmptyResult>;
|
||||
disownData(params: Network.DisownDataParameters): EmptyResult;
|
||||
setExtraHeaders(params: Network.SetExtraHeadersParameters): Promise<EmptyResult>;
|
||||
}
|
||||
export declare function parseBiDiHeaders(headers: Network.Header[]): Protocol.Network.Headers;
|
||||
546
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/network/NetworkProcessor.js
generated
vendored
Normal file
546
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/network/NetworkProcessor.js
generated
vendored
Normal file
@@ -0,0 +1,546 @@
|
||||
"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.NetworkProcessor = void 0;
|
||||
exports.parseBiDiHeaders = parseBiDiHeaders;
|
||||
const protocol_js_1 = require("../../../protocol/protocol.js");
|
||||
const NetworkUtils_js_1 = require("./NetworkUtils.js");
|
||||
/** Dispatches Network module commands. */
|
||||
class NetworkProcessor {
|
||||
#browsingContextStorage;
|
||||
#networkStorage;
|
||||
#userContextStorage;
|
||||
#contextConfigStorage;
|
||||
constructor(browsingContextStorage, networkStorage, userContextStorage, contextConfigStorage) {
|
||||
this.#userContextStorage = userContextStorage;
|
||||
this.#browsingContextStorage = browsingContextStorage;
|
||||
this.#networkStorage = networkStorage;
|
||||
this.#contextConfigStorage = contextConfigStorage;
|
||||
}
|
||||
async addIntercept(params) {
|
||||
this.#browsingContextStorage.verifyTopLevelContextsList(params.contexts);
|
||||
const urlPatterns = params.urlPatterns ?? [];
|
||||
const parsedUrlPatterns = NetworkProcessor.parseUrlPatterns(urlPatterns);
|
||||
const intercept = this.#networkStorage.addIntercept({
|
||||
urlPatterns: parsedUrlPatterns,
|
||||
phases: params.phases,
|
||||
contexts: params.contexts,
|
||||
});
|
||||
// Adding interception may require enabling CDP Network domains.
|
||||
await this.#toggleNetwork();
|
||||
return {
|
||||
intercept,
|
||||
};
|
||||
}
|
||||
async continueRequest(params) {
|
||||
if (params.url !== undefined) {
|
||||
NetworkProcessor.parseUrlString(params.url);
|
||||
}
|
||||
if (params.method !== undefined) {
|
||||
if (!NetworkProcessor.isMethodValid(params.method)) {
|
||||
throw new protocol_js_1.InvalidArgumentException(`Method '${params.method}' is invalid.`);
|
||||
}
|
||||
}
|
||||
if (params.headers) {
|
||||
NetworkProcessor.validateHeaders(params.headers);
|
||||
}
|
||||
const request = this.#getBlockedRequestOrFail(params.request, [
|
||||
"beforeRequestSent" /* Network.InterceptPhase.BeforeRequestSent */,
|
||||
]);
|
||||
try {
|
||||
await request.continueRequest(params);
|
||||
}
|
||||
catch (error) {
|
||||
throw NetworkProcessor.wrapInterceptionError(error);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
async continueResponse(params) {
|
||||
if (params.headers) {
|
||||
NetworkProcessor.validateHeaders(params.headers);
|
||||
}
|
||||
const request = this.#getBlockedRequestOrFail(params.request, [
|
||||
"authRequired" /* Network.InterceptPhase.AuthRequired */,
|
||||
"responseStarted" /* Network.InterceptPhase.ResponseStarted */,
|
||||
]);
|
||||
try {
|
||||
await request.continueResponse(params);
|
||||
}
|
||||
catch (error) {
|
||||
throw NetworkProcessor.wrapInterceptionError(error);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
async continueWithAuth(params) {
|
||||
const networkId = params.request;
|
||||
const request = this.#getBlockedRequestOrFail(networkId, [
|
||||
"authRequired" /* Network.InterceptPhase.AuthRequired */,
|
||||
]);
|
||||
await request.continueWithAuth(params);
|
||||
return {};
|
||||
}
|
||||
async failRequest({ request: networkId, }) {
|
||||
const request = this.#getRequestOrFail(networkId);
|
||||
if (request.interceptPhase === "authRequired" /* Network.InterceptPhase.AuthRequired */) {
|
||||
throw new protocol_js_1.InvalidArgumentException(`Request '${networkId}' in 'authRequired' phase cannot be failed`);
|
||||
}
|
||||
if (!request.interceptPhase) {
|
||||
throw new protocol_js_1.NoSuchRequestException(`No blocked request found for network id '${networkId}'`);
|
||||
}
|
||||
await request.failRequest('Failed');
|
||||
return {};
|
||||
}
|
||||
async provideResponse(params) {
|
||||
if (params.headers) {
|
||||
NetworkProcessor.validateHeaders(params.headers);
|
||||
}
|
||||
const request = this.#getBlockedRequestOrFail(params.request, [
|
||||
"beforeRequestSent" /* Network.InterceptPhase.BeforeRequestSent */,
|
||||
"responseStarted" /* Network.InterceptPhase.ResponseStarted */,
|
||||
"authRequired" /* Network.InterceptPhase.AuthRequired */,
|
||||
]);
|
||||
try {
|
||||
await request.provideResponse(params);
|
||||
}
|
||||
catch (error) {
|
||||
throw NetworkProcessor.wrapInterceptionError(error);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
/**
|
||||
* In some states CDP Network and Fetch domains are not required, but in some they have
|
||||
* to be updated. Whenever potential change in these kinds of states is introduced,
|
||||
* update the states of all the CDP targets.
|
||||
*/
|
||||
async #toggleNetwork() {
|
||||
await Promise.all(this.#browsingContextStorage.getAllContexts().map((context) => {
|
||||
return context.cdpTarget.toggleNetwork();
|
||||
}));
|
||||
}
|
||||
async removeIntercept(params) {
|
||||
this.#networkStorage.removeIntercept(params.intercept);
|
||||
// Removing interception may allow for disabling CDP Network domains.
|
||||
await this.#toggleNetwork();
|
||||
return {};
|
||||
}
|
||||
async setCacheBehavior(params) {
|
||||
const contexts = this.#browsingContextStorage.verifyTopLevelContextsList(params.contexts);
|
||||
// Change all targets
|
||||
if (contexts.size === 0) {
|
||||
this.#networkStorage.defaultCacheBehavior = params.cacheBehavior;
|
||||
await Promise.all(this.#browsingContextStorage.getAllContexts().map((context) => {
|
||||
return context.cdpTarget.toggleSetCacheDisabled();
|
||||
}));
|
||||
return {};
|
||||
}
|
||||
const cacheDisabled = params.cacheBehavior === 'bypass';
|
||||
await Promise.all([...contexts.values()].map((context) => {
|
||||
return context.cdpTarget.toggleSetCacheDisabled(cacheDisabled);
|
||||
}));
|
||||
return {};
|
||||
}
|
||||
#getRequestOrFail(id) {
|
||||
const request = this.#networkStorage.getRequestById(id);
|
||||
if (!request) {
|
||||
throw new protocol_js_1.NoSuchRequestException(`Network request with ID '${id}' doesn't exist`);
|
||||
}
|
||||
return request;
|
||||
}
|
||||
#getBlockedRequestOrFail(id, phases) {
|
||||
const request = this.#getRequestOrFail(id);
|
||||
if (!request.interceptPhase) {
|
||||
throw new protocol_js_1.NoSuchRequestException(`No blocked request found for network id '${id}'`);
|
||||
}
|
||||
if (request.interceptPhase && !phases.includes(request.interceptPhase)) {
|
||||
throw new protocol_js_1.InvalidArgumentException(`Blocked request for network id '${id}' is in '${request.interceptPhase}' phase`);
|
||||
}
|
||||
return request;
|
||||
}
|
||||
/**
|
||||
* Validate https://fetch.spec.whatwg.org/#header-value
|
||||
*/
|
||||
static validateHeaders(headers) {
|
||||
for (const header of headers) {
|
||||
let headerValue;
|
||||
if (header.value.type === 'string') {
|
||||
headerValue = header.value.value;
|
||||
}
|
||||
else {
|
||||
headerValue = atob(header.value.value);
|
||||
}
|
||||
if (headerValue !== headerValue.trim() ||
|
||||
headerValue.includes('\n') ||
|
||||
headerValue.includes('\0')) {
|
||||
throw new protocol_js_1.InvalidArgumentException(`Header value '${headerValue}' is not acceptable value`);
|
||||
}
|
||||
}
|
||||
}
|
||||
static isMethodValid(method) {
|
||||
// https://httpwg.org/specs/rfc9110.html#method.overview
|
||||
return /^[!#$%&'*+\-.^_`|~a-zA-Z\d]+$/.test(method);
|
||||
}
|
||||
/**
|
||||
* 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) => {
|
||||
let patternUrl = '';
|
||||
let hasProtocol = true;
|
||||
let hasHostname = true;
|
||||
let hasPort = true;
|
||||
let hasPathname = true;
|
||||
let hasSearch = true;
|
||||
switch (urlPattern.type) {
|
||||
case 'string': {
|
||||
patternUrl = unescapeURLPattern(urlPattern.pattern);
|
||||
break;
|
||||
}
|
||||
case 'pattern': {
|
||||
if (urlPattern.protocol === undefined) {
|
||||
hasProtocol = false;
|
||||
patternUrl += 'http';
|
||||
}
|
||||
else {
|
||||
if (urlPattern.protocol === '') {
|
||||
throw new protocol_js_1.InvalidArgumentException('URL pattern must specify a protocol');
|
||||
}
|
||||
urlPattern.protocol = unescapeURLPattern(urlPattern.protocol);
|
||||
if (!urlPattern.protocol.match(/^[a-zA-Z+-.]+$/)) {
|
||||
throw new protocol_js_1.InvalidArgumentException('Forbidden characters');
|
||||
}
|
||||
patternUrl += urlPattern.protocol;
|
||||
}
|
||||
const scheme = patternUrl.toLocaleLowerCase();
|
||||
patternUrl += ':';
|
||||
if ((0, NetworkUtils_js_1.isSpecialScheme)(scheme)) {
|
||||
patternUrl += '//';
|
||||
}
|
||||
if (urlPattern.hostname === undefined) {
|
||||
if (scheme !== 'file') {
|
||||
patternUrl += 'placeholder';
|
||||
}
|
||||
hasHostname = false;
|
||||
}
|
||||
else {
|
||||
if (urlPattern.hostname === '') {
|
||||
throw new protocol_js_1.InvalidArgumentException('URL pattern must specify a hostname');
|
||||
}
|
||||
if (urlPattern.protocol === 'file') {
|
||||
throw new protocol_js_1.InvalidArgumentException(`URL pattern protocol cannot be 'file'`);
|
||||
}
|
||||
urlPattern.hostname = unescapeURLPattern(urlPattern.hostname);
|
||||
let insideBrackets = false;
|
||||
for (const c of urlPattern.hostname) {
|
||||
if (c === '/' || c === '?' || c === '#') {
|
||||
throw new protocol_js_1.InvalidArgumentException(`'/', '?', '#' are forbidden in hostname`);
|
||||
}
|
||||
if (!insideBrackets && c === ':') {
|
||||
throw new protocol_js_1.InvalidArgumentException(`':' is only allowed inside brackets in hostname`);
|
||||
}
|
||||
if (c === '[') {
|
||||
insideBrackets = true;
|
||||
}
|
||||
if (c === ']') {
|
||||
insideBrackets = false;
|
||||
}
|
||||
}
|
||||
patternUrl += urlPattern.hostname;
|
||||
}
|
||||
if (urlPattern.port === undefined) {
|
||||
hasPort = false;
|
||||
}
|
||||
else {
|
||||
if (urlPattern.port === '') {
|
||||
throw new protocol_js_1.InvalidArgumentException(`URL pattern must specify a port`);
|
||||
}
|
||||
urlPattern.port = unescapeURLPattern(urlPattern.port);
|
||||
patternUrl += ':';
|
||||
if (!urlPattern.port.match(/^\d+$/)) {
|
||||
throw new protocol_js_1.InvalidArgumentException('Forbidden characters');
|
||||
}
|
||||
patternUrl += urlPattern.port;
|
||||
}
|
||||
if (urlPattern.pathname === undefined) {
|
||||
hasPathname = false;
|
||||
}
|
||||
else {
|
||||
urlPattern.pathname = unescapeURLPattern(urlPattern.pathname);
|
||||
if (urlPattern.pathname[0] !== '/') {
|
||||
patternUrl += '/';
|
||||
}
|
||||
if (urlPattern.pathname.includes('#') ||
|
||||
urlPattern.pathname.includes('?')) {
|
||||
throw new protocol_js_1.InvalidArgumentException('Forbidden characters');
|
||||
}
|
||||
patternUrl += urlPattern.pathname;
|
||||
}
|
||||
if (urlPattern.search === undefined) {
|
||||
hasSearch = false;
|
||||
}
|
||||
else {
|
||||
urlPattern.search = unescapeURLPattern(urlPattern.search);
|
||||
if (urlPattern.search[0] !== '?') {
|
||||
patternUrl += '?';
|
||||
}
|
||||
if (urlPattern.search.includes('#')) {
|
||||
throw new protocol_js_1.InvalidArgumentException('Forbidden characters');
|
||||
}
|
||||
patternUrl += urlPattern.search;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
const serializePort = (url) => {
|
||||
const defaultPorts = {
|
||||
'ftp:': 21,
|
||||
'file:': null,
|
||||
'http:': 80,
|
||||
'https:': 443,
|
||||
'ws:': 80,
|
||||
'wss:': 443,
|
||||
};
|
||||
if ((0, NetworkUtils_js_1.isSpecialScheme)(url.protocol) &&
|
||||
defaultPorts[url.protocol] !== null &&
|
||||
(!url.port || String(defaultPorts[url.protocol]) === url.port)) {
|
||||
return '';
|
||||
}
|
||||
else if (url.port) {
|
||||
return url.port;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
try {
|
||||
const url = new URL(patternUrl);
|
||||
return {
|
||||
protocol: hasProtocol ? url.protocol.replace(/:$/, '') : undefined,
|
||||
hostname: hasHostname ? url.hostname : undefined,
|
||||
port: hasPort ? serializePort(url) : undefined,
|
||||
pathname: hasPathname && url.pathname ? url.pathname : undefined,
|
||||
search: hasSearch ? url.search : undefined,
|
||||
};
|
||||
}
|
||||
catch (err) {
|
||||
throw new protocol_js_1.InvalidArgumentException(`${err.message} '${patternUrl}'`);
|
||||
}
|
||||
});
|
||||
}
|
||||
static wrapInterceptionError(error) {
|
||||
// https://source.chromium.org/chromium/chromium/src/+/main:content/browser/devtools/protocol/fetch_handler.cc;l=169
|
||||
if (error?.message.includes('Invalid header') ||
|
||||
error?.message.includes('Unsafe header')) {
|
||||
return new protocol_js_1.InvalidArgumentException(error.message);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
async addDataCollector(params) {
|
||||
if (params.userContexts !== undefined && params.contexts !== undefined) {
|
||||
throw new protocol_js_1.InvalidArgumentException("'contexts' and 'userContexts' are mutually exclusive");
|
||||
}
|
||||
if (params.userContexts !== undefined) {
|
||||
// Assert the user contexts exist.
|
||||
await this.#userContextStorage.verifyUserContextIdList(params.userContexts);
|
||||
}
|
||||
if (params.contexts !== undefined) {
|
||||
for (const browsingContextId of params.contexts) {
|
||||
// Assert the browsing context exists and are top-level.
|
||||
const browsingContext = this.#browsingContextStorage.getContext(browsingContextId);
|
||||
if (!browsingContext.isTopLevelContext()) {
|
||||
throw new protocol_js_1.InvalidArgumentException(`Data collectors are available only on top-level browsing contexts`);
|
||||
}
|
||||
}
|
||||
}
|
||||
const collectorId = this.#networkStorage.addDataCollector(params);
|
||||
// Adding data collectors may require enabling CDP Network domains.
|
||||
await this.#toggleNetwork();
|
||||
return { collector: collectorId };
|
||||
}
|
||||
async getData(params) {
|
||||
return await this.#networkStorage.getCollectedData(params);
|
||||
}
|
||||
async removeDataCollector(params) {
|
||||
this.#networkStorage.removeDataCollector(params);
|
||||
// Removing data collectors may allow disabling CDP Network domains.
|
||||
await this.#toggleNetwork();
|
||||
return {};
|
||||
}
|
||||
disownData(params) {
|
||||
this.#networkStorage.disownData(params);
|
||||
return {};
|
||||
}
|
||||
async #getRelatedTopLevelBrowsingContexts(browsingContextIds, userContextIds) {
|
||||
// Duplicated with EmulationProcessor logic. Consider moving to ConfigStorage.
|
||||
if (browsingContextIds === undefined && userContextIds === undefined) {
|
||||
return this.#browsingContextStorage.getTopLevelContexts();
|
||||
}
|
||||
if (browsingContextIds !== undefined && userContextIds !== undefined) {
|
||||
throw new protocol_js_1.InvalidArgumentException('User contexts and browsing contexts are mutually exclusive');
|
||||
}
|
||||
const result = [];
|
||||
if (userContextIds !== undefined) {
|
||||
if (userContextIds.length === 0) {
|
||||
throw new protocol_js_1.InvalidArgumentException('user context should be provided');
|
||||
}
|
||||
// Verify that all user contexts exist.
|
||||
await this.#userContextStorage.verifyUserContextIdList(userContextIds);
|
||||
for (const userContextId of userContextIds) {
|
||||
const topLevelBrowsingContexts = this.#browsingContextStorage
|
||||
.getTopLevelContexts()
|
||||
.filter((browsingContext) => browsingContext.userContext === userContextId);
|
||||
result.push(...topLevelBrowsingContexts);
|
||||
}
|
||||
}
|
||||
if (browsingContextIds !== undefined) {
|
||||
if (browsingContextIds.length === 0) {
|
||||
throw new protocol_js_1.InvalidArgumentException('browsing context should be provided');
|
||||
}
|
||||
for (const browsingContextId of browsingContextIds) {
|
||||
const browsingContext = this.#browsingContextStorage.getContext(browsingContextId);
|
||||
if (!browsingContext.isTopLevelContext()) {
|
||||
throw new protocol_js_1.InvalidArgumentException('The command is only supported on the top-level context');
|
||||
}
|
||||
result.push(browsingContext);
|
||||
}
|
||||
}
|
||||
// Remove duplicates. Compare `BrowsingContextImpl` by reference is correct here, as
|
||||
// `browsingContextStorage` returns the same instance for the same id.
|
||||
return [...new Set(result).values()];
|
||||
}
|
||||
async setExtraHeaders(params) {
|
||||
const affectedBrowsingContexts = await this.#getRelatedTopLevelBrowsingContexts(params.contexts, params.userContexts);
|
||||
const cdpExtraHeaders = parseBiDiHeaders(params.headers);
|
||||
if (params.userContexts === undefined && params.contexts === undefined) {
|
||||
this.#contextConfigStorage.updateGlobalConfig({
|
||||
extraHeaders: cdpExtraHeaders,
|
||||
});
|
||||
}
|
||||
if (params.userContexts !== undefined) {
|
||||
params.userContexts.forEach((userContext) => {
|
||||
this.#contextConfigStorage.updateUserContextConfig(userContext, {
|
||||
extraHeaders: cdpExtraHeaders,
|
||||
});
|
||||
});
|
||||
}
|
||||
if (params.contexts !== undefined) {
|
||||
params.contexts.forEach((browsingContextId) => {
|
||||
this.#contextConfigStorage.updateBrowsingContextConfig(browsingContextId, { extraHeaders: cdpExtraHeaders });
|
||||
});
|
||||
}
|
||||
await Promise.all(affectedBrowsingContexts.map(async (context) => {
|
||||
// Actual value can be different from the one in params, e.g. in case of already
|
||||
// existing setting.
|
||||
const extraHeaders = this.#contextConfigStorage.getActiveConfig(context.id, context.userContext).extraHeaders ?? {};
|
||||
await context.setExtraHeaders(extraHeaders);
|
||||
}));
|
||||
return {};
|
||||
}
|
||||
}
|
||||
exports.NetworkProcessor = NetworkProcessor;
|
||||
/**
|
||||
* See https://w3c.github.io/webdriver-bidi/#unescape-url-pattern
|
||||
*/
|
||||
function unescapeURLPattern(pattern) {
|
||||
const forbidden = new Set(['(', ')', '*', '{', '}']);
|
||||
let result = '';
|
||||
let isEscaped = false;
|
||||
for (const c of pattern) {
|
||||
if (!isEscaped) {
|
||||
if (forbidden.has(c)) {
|
||||
throw new protocol_js_1.InvalidArgumentException('Forbidden characters');
|
||||
}
|
||||
if (c === '\\') {
|
||||
isEscaped = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
result += c;
|
||||
isEscaped = false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
// https://fetch.spec.whatwg.org/#header-name
|
||||
const FORBIDDEN_HEADER_NAME_SYMBOLS = new Set([
|
||||
' ',
|
||||
'\t',
|
||||
'\n',
|
||||
'"',
|
||||
'(',
|
||||
')',
|
||||
',',
|
||||
'/',
|
||||
':',
|
||||
';',
|
||||
'<',
|
||||
'=',
|
||||
'>',
|
||||
'?',
|
||||
'@',
|
||||
'[',
|
||||
'\\',
|
||||
']',
|
||||
'{',
|
||||
'}',
|
||||
]);
|
||||
// https://fetch.spec.whatwg.org/#header-value
|
||||
const FORBIDDEN_HEADER_VALUE_SYMBOLS = new Set(['\0', '\n', '\r']);
|
||||
function includesChar(str, chars) {
|
||||
for (const char of str) {
|
||||
if (chars.has(char)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// Export for testing.
|
||||
function parseBiDiHeaders(headers) {
|
||||
const parsedHeaders = {};
|
||||
for (const bidiHeader of headers) {
|
||||
if (bidiHeader.value.type === 'string') {
|
||||
const name = bidiHeader.name;
|
||||
const value = bidiHeader.value.value;
|
||||
if (name.length === 0) {
|
||||
throw new protocol_js_1.InvalidArgumentException(`Empty header name is not allowed`);
|
||||
}
|
||||
if (includesChar(name, FORBIDDEN_HEADER_NAME_SYMBOLS)) {
|
||||
throw new protocol_js_1.InvalidArgumentException(`Header name '${name}' contains forbidden symbols`);
|
||||
}
|
||||
if (includesChar(value, FORBIDDEN_HEADER_VALUE_SYMBOLS)) {
|
||||
throw new protocol_js_1.InvalidArgumentException(`Header value '${value}' contains forbidden symbols`);
|
||||
}
|
||||
if (value.trim() !== value) {
|
||||
throw new protocol_js_1.InvalidArgumentException(`Header value should not contain trailing or ending whitespaces`);
|
||||
}
|
||||
// BiDi spec does not combine but overrides the headers with the same names.
|
||||
// https://www.w3.org/TR/webdriver-bidi/#update-headers
|
||||
parsedHeaders[bidiHeader.name] = bidiHeader.value.value;
|
||||
}
|
||||
else {
|
||||
throw new protocol_js_1.UnsupportedOperationException('Only string headers values are supported');
|
||||
}
|
||||
}
|
||||
return parsedHeaders;
|
||||
}
|
||||
//# sourceMappingURL=NetworkProcessor.js.map
|
||||
1
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/network/NetworkProcessor.js.map
generated
vendored
Normal file
1
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/network/NetworkProcessor.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
55
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/network/NetworkRequest.d.ts
generated
vendored
Normal file
55
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/network/NetworkRequest.d.ts
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* @fileoverview `NetworkRequest` represents a single network request and keeps
|
||||
* track of all the related CDP events.
|
||||
*/
|
||||
import type { Protocol } from 'devtools-protocol';
|
||||
import { Network } from '../../../protocol/protocol.js';
|
||||
import { Deferred } from '../../../utils/Deferred.js';
|
||||
import { type LoggerFn } from '../../../utils/log.js';
|
||||
import type { CdpTarget } from '../cdp/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;
|
||||
static unknownParameter: string;
|
||||
waitNextPhase: Deferred<void>;
|
||||
constructor(id: Network.Request, eventManager: EventManager, networkStorage: NetworkStorage, cdpTarget: CdpTarget, redirectCount?: number, logger?: LoggerFn);
|
||||
get id(): string;
|
||||
get fetchId(): string | undefined;
|
||||
/**
|
||||
* When blocked returns the phase for it
|
||||
*/
|
||||
get interceptPhase(): Network.InterceptPhase | undefined;
|
||||
get url(): string;
|
||||
get redirectCount(): number;
|
||||
get cdpTarget(): CdpTarget;
|
||||
/** CdpTarget can be changed when frame is moving out of process. */
|
||||
updateCdpTarget(cdpTarget: CdpTarget): void;
|
||||
get cdpClient(): import("../../BidiMapper.js").CdpClient;
|
||||
isRedirecting(): boolean;
|
||||
get bodySize(): number;
|
||||
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;
|
||||
onLoadingFinishedEvent(event: Protocol.Network.LoadingFinishedEvent): void;
|
||||
onDataReceivedEvent(event: Protocol.Network.DataReceivedEvent): void;
|
||||
onLoadingFailedEvent(event: Protocol.Network.LoadingFailedEvent): void;
|
||||
/** @see https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#method-failRequest */
|
||||
failRequest(errorReason: Protocol.Network.ErrorReason): Promise<void>;
|
||||
onRequestPaused(event: Protocol.Fetch.RequestPausedEvent): void;
|
||||
onAuthRequired(event: Protocol.Fetch.AuthRequiredEvent): void;
|
||||
/** @see https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#method-continueRequest */
|
||||
continueRequest(overrides?: Omit<Network.ContinueRequestParameters, 'request'>): Promise<void>;
|
||||
/** @see https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#method-continueResponse */
|
||||
continueResponse(overrides?: Omit<Network.ContinueResponseParameters, 'request'>): Promise<void>;
|
||||
/** @see https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#method-continueWithAuth */
|
||||
continueWithAuth(authChallenge: Omit<Network.ContinueWithAuthParameters, 'request'>): Promise<void>;
|
||||
/** @see https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#method-provideResponse */
|
||||
provideResponse(overrides: Omit<Network.ProvideResponseParameters, 'request'>): Promise<void>;
|
||||
dispose(): void;
|
||||
get encodedResponseBodySize(): number;
|
||||
}
|
||||
894
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/network/NetworkRequest.js
generated
vendored
Normal file
894
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/network/NetworkRequest.js
generated
vendored
Normal file
@@ -0,0 +1,894 @@
|
||||
"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.
|
||||
*
|
||||
*/
|
||||
var _a;
|
||||
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 DefaultMap_js_1 = require("../../../utils/DefaultMap.js");
|
||||
const Deferred_js_1 = require("../../../utils/Deferred.js");
|
||||
const log_js_1 = require("../../../utils/log.js");
|
||||
const NetworkUtils_js_1 = require("./NetworkUtils.js");
|
||||
const REALM_REGEX = /(?<=realm=").*(?=")/;
|
||||
/** Abstracts one individual network request. */
|
||||
class NetworkRequest {
|
||||
static unknownParameter = '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.
|
||||
*/
|
||||
#id;
|
||||
#fetchId;
|
||||
/**
|
||||
* Indicates the network intercept phase, if the request is currently blocked.
|
||||
* Undefined necessarily implies that the request is not blocked.
|
||||
*/
|
||||
#interceptPhase;
|
||||
#servedFromCache = false;
|
||||
#redirectCount;
|
||||
#request = {};
|
||||
#requestOverrides;
|
||||
#responseOverrides;
|
||||
#response = {
|
||||
decodedSize: 0,
|
||||
encodedSize: 0,
|
||||
};
|
||||
#eventManager;
|
||||
#networkStorage;
|
||||
#cdpTarget;
|
||||
#logger;
|
||||
#emittedEvents = {
|
||||
[protocol_js_1.ChromiumBidi.Network.EventNames.AuthRequired]: false,
|
||||
[protocol_js_1.ChromiumBidi.Network.EventNames.BeforeRequestSent]: false,
|
||||
[protocol_js_1.ChromiumBidi.Network.EventNames.FetchError]: false,
|
||||
[protocol_js_1.ChromiumBidi.Network.EventNames.ResponseCompleted]: false,
|
||||
[protocol_js_1.ChromiumBidi.Network.EventNames.ResponseStarted]: false,
|
||||
};
|
||||
waitNextPhase = new Deferred_js_1.Deferred();
|
||||
constructor(id, eventManager, networkStorage, cdpTarget, redirectCount = 0, logger) {
|
||||
this.#id = id;
|
||||
this.#eventManager = eventManager;
|
||||
this.#networkStorage = networkStorage;
|
||||
this.#cdpTarget = cdpTarget;
|
||||
this.#redirectCount = redirectCount;
|
||||
this.#logger = logger;
|
||||
}
|
||||
get id() {
|
||||
return this.#id;
|
||||
}
|
||||
get fetchId() {
|
||||
return this.#fetchId;
|
||||
}
|
||||
/**
|
||||
* When blocked returns the phase for it
|
||||
*/
|
||||
get interceptPhase() {
|
||||
return this.#interceptPhase;
|
||||
}
|
||||
get url() {
|
||||
const fragment = this.#request.info?.request.urlFragment ??
|
||||
this.#request.paused?.request.urlFragment ??
|
||||
'';
|
||||
const url = this.#response.paused?.request.url ??
|
||||
this.#requestOverrides?.url ??
|
||||
this.#response.info?.url ??
|
||||
this.#request.auth?.request.url ??
|
||||
this.#request.info?.request.url ??
|
||||
this.#request.paused?.request.url ??
|
||||
_a.unknownParameter;
|
||||
return `${url}${fragment}`;
|
||||
}
|
||||
get redirectCount() {
|
||||
return this.#redirectCount;
|
||||
}
|
||||
get cdpTarget() {
|
||||
return this.#cdpTarget;
|
||||
}
|
||||
/** CdpTarget can be changed when frame is moving out of process. */
|
||||
updateCdpTarget(cdpTarget) {
|
||||
if (cdpTarget !== this.#cdpTarget) {
|
||||
this.#logger?.(log_js_1.LogType.debugInfo, `Request ${this.id} was moved from ${this.#cdpTarget.id} to ${cdpTarget.id}`);
|
||||
this.#cdpTarget = cdpTarget;
|
||||
}
|
||||
}
|
||||
get cdpClient() {
|
||||
return this.#cdpTarget.cdpClient;
|
||||
}
|
||||
isRedirecting() {
|
||||
return Boolean(this.#request.info);
|
||||
}
|
||||
#isDataUrl() {
|
||||
return this.url.startsWith('data:');
|
||||
}
|
||||
#isNonInterceptable() {
|
||||
return (
|
||||
// We can't intercept data urls from CDP
|
||||
this.#isDataUrl() ||
|
||||
// Cached requests never hit the network
|
||||
this.#servedFromCache);
|
||||
}
|
||||
get #method() {
|
||||
return (this.#requestOverrides?.method ??
|
||||
this.#request.info?.request.method ??
|
||||
this.#request.paused?.request.method ??
|
||||
this.#request.auth?.request.method ??
|
||||
this.#response.paused?.request.method);
|
||||
}
|
||||
get #navigationId() {
|
||||
// Heuristic to determine if this is a navigation request, and if not return null.
|
||||
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;
|
||||
}
|
||||
// Get virtual navigation ID from the browsing context.
|
||||
return this.#networkStorage.getNavigationId(this.#context ?? undefined);
|
||||
}
|
||||
get #cookies() {
|
||||
let cookies = [];
|
||||
if (this.#request.extraInfo) {
|
||||
cookies = this.#request.extraInfo.associatedCookies
|
||||
.filter(({ blockedReasons }) => {
|
||||
return !Array.isArray(blockedReasons) || blockedReasons.length === 0;
|
||||
})
|
||||
.map(({ cookie }) => (0, NetworkUtils_js_1.cdpToBiDiCookie)(cookie));
|
||||
}
|
||||
return cookies;
|
||||
}
|
||||
#getBodySizeFromHeaders(headers) {
|
||||
if (headers === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if (headers['Content-Length'] !== undefined) {
|
||||
const bodySize = Number.parseInt(headers['Content-Length']);
|
||||
if (Number.isInteger(bodySize)) {
|
||||
return bodySize;
|
||||
}
|
||||
this.#logger?.(log_js_1.LogType.debugError, "Unexpected non-integer 'Content-Length' header");
|
||||
}
|
||||
// TODO: process `Transfer-Encoding: chunked` case properly.
|
||||
return undefined;
|
||||
}
|
||||
get bodySize() {
|
||||
if (typeof this.#requestOverrides?.bodySize === 'number') {
|
||||
return this.#requestOverrides.bodySize;
|
||||
}
|
||||
if (this.#request.info?.request.postDataEntries !== undefined) {
|
||||
return (0, NetworkUtils_js_1.bidiBodySizeFromCdpPostDataEntries)(this.#request.info?.request.postDataEntries);
|
||||
}
|
||||
// Try to guess the body size based on the `Content-Length` header.
|
||||
return (this.#getBodySizeFromHeaders(this.#request.info?.request.headers) ??
|
||||
this.#getBodySizeFromHeaders(this.#request.extraInfo?.headers) ??
|
||||
0);
|
||||
}
|
||||
get #context() {
|
||||
const result = this.#response.paused?.frameId ??
|
||||
this.#request.info?.frameId ??
|
||||
this.#request.paused?.frameId ??
|
||||
this.#request.auth?.frameId;
|
||||
if (result !== undefined) {
|
||||
return result;
|
||||
}
|
||||
// Heuristic for associating a preflight request with context via it's initiator
|
||||
// request. Useful for preflight requests.
|
||||
// https://github.com/GoogleChromeLabs/chromium-bidi/issues/3570
|
||||
if (this.#request?.info?.initiator.type === 'preflight' &&
|
||||
this.#request?.info?.initiator.requestId !== undefined) {
|
||||
const maybeInitiator = this.#networkStorage.getRequestById(this.#request?.info?.initiator.requestId);
|
||||
if (maybeInitiator !== undefined) {
|
||||
return maybeInitiator.#request.info?.frameId ?? null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/** Returns the HTTP status code associated with this request if any. */
|
||||
get #statusCode() {
|
||||
return (this.#responseOverrides?.statusCode ??
|
||||
this.#response.paused?.responseStatusCode ??
|
||||
this.#response.extraInfo?.statusCode ??
|
||||
this.#response.info?.status);
|
||||
}
|
||||
get #requestHeaders() {
|
||||
let headers = [];
|
||||
if (this.#requestOverrides?.headers) {
|
||||
const headerMap = new DefaultMap_js_1.DefaultMap(() => []);
|
||||
for (const header of this.#requestOverrides.headers) {
|
||||
headerMap.get(header.name).push(header.value.value);
|
||||
}
|
||||
for (const [name, value] of headerMap.entries()) {
|
||||
headers.push({
|
||||
name,
|
||||
value: {
|
||||
type: 'string',
|
||||
value: value.join('\n').trimEnd(),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
headers = [
|
||||
...(0, NetworkUtils_js_1.bidiNetworkHeadersFromCdpNetworkHeaders)(this.#request.info?.request.headers),
|
||||
...(0, NetworkUtils_js_1.bidiNetworkHeadersFromCdpNetworkHeaders)(this.#request.extraInfo?.headers),
|
||||
];
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
get #authChallenges() {
|
||||
// TODO: get headers from Fetch.requestPaused
|
||||
if (!this.#response.info) {
|
||||
return;
|
||||
}
|
||||
if (!(this.#statusCode === 401 || this.#statusCode === 407)) {
|
||||
return undefined;
|
||||
}
|
||||
const headerName = this.#statusCode === 401 ? 'WWW-Authenticate' : 'Proxy-Authenticate';
|
||||
const authChallenges = [];
|
||||
for (const [header, value] of Object.entries(this.#response.info.headers)) {
|
||||
// TODO: Do a proper match based on https://httpwg.org/specs/rfc9110.html#credentials
|
||||
// Or verify this works
|
||||
if (header.localeCompare(headerName, undefined, { sensitivity: 'base' }) === 0) {
|
||||
authChallenges.push({
|
||||
scheme: value.split(' ').at(0) ?? '',
|
||||
realm: value.match(REALM_REGEX)?.at(0) ?? '',
|
||||
});
|
||||
}
|
||||
}
|
||||
return authChallenges;
|
||||
}
|
||||
get #timings() {
|
||||
// The timing in the CDP events are provided relative to the event's baseline.
|
||||
// However, the baseline can be different for different events, and the events have to
|
||||
// be normalized throughout resource events. Normalize events timestamps by the
|
||||
// request.
|
||||
// TODO: Verify this is correct.
|
||||
const responseTimeOffset = (0, NetworkUtils_js_1.getTiming)((0, NetworkUtils_js_1.getTiming)(this.#response.info?.timing?.requestTime) -
|
||||
(0, NetworkUtils_js_1.getTiming)(this.#request.info?.timestamp));
|
||||
return {
|
||||
// TODO: Verify this is correct
|
||||
timeOrigin: Math.round((0, NetworkUtils_js_1.getTiming)(this.#request.info?.wallTime) * 1000),
|
||||
// Timing baseline.
|
||||
// TODO: Verify this is correct.
|
||||
requestTime: 0,
|
||||
// TODO: set if redirect detected.
|
||||
redirectStart: 0,
|
||||
// TODO: set if redirect detected.
|
||||
redirectEnd: 0,
|
||||
// TODO: Verify this is correct
|
||||
// https://source.chromium.org/chromium/chromium/src/+/main:net/base/load_timing_info.h;l=145
|
||||
fetchStart: (0, NetworkUtils_js_1.getTiming)(this.#response.info?.timing?.workerFetchStart, responseTimeOffset),
|
||||
// fetchStart: 0,
|
||||
dnsStart: (0, NetworkUtils_js_1.getTiming)(this.#response.info?.timing?.dnsStart, responseTimeOffset),
|
||||
dnsEnd: (0, NetworkUtils_js_1.getTiming)(this.#response.info?.timing?.dnsEnd, responseTimeOffset),
|
||||
connectStart: (0, NetworkUtils_js_1.getTiming)(this.#response.info?.timing?.connectStart, responseTimeOffset),
|
||||
connectEnd: (0, NetworkUtils_js_1.getTiming)(this.#response.info?.timing?.connectEnd, responseTimeOffset),
|
||||
tlsStart: (0, NetworkUtils_js_1.getTiming)(this.#response.info?.timing?.sslStart, responseTimeOffset),
|
||||
requestStart: (0, NetworkUtils_js_1.getTiming)(this.#response.info?.timing?.sendStart, responseTimeOffset),
|
||||
// https://source.chromium.org/chromium/chromium/src/+/main:net/base/load_timing_info.h;l=196
|
||||
responseStart: (0, NetworkUtils_js_1.getTiming)(this.#response.info?.timing?.receiveHeadersStart, responseTimeOffset),
|
||||
responseEnd: (0, NetworkUtils_js_1.getTiming)(this.#response.info?.timing?.receiveHeadersEnd, responseTimeOffset),
|
||||
};
|
||||
}
|
||||
#phaseChanged() {
|
||||
this.waitNextPhase.resolve();
|
||||
this.waitNextPhase = new Deferred_js_1.Deferred();
|
||||
}
|
||||
#interceptsInPhase(phase) {
|
||||
if (this.#isNonInterceptable() ||
|
||||
!this.#cdpTarget.isSubscribedTo(`network.${phase}`)) {
|
||||
return new Set();
|
||||
}
|
||||
return this.#networkStorage.getInterceptsForPhase(this, phase);
|
||||
}
|
||||
#isBlockedInPhase(phase) {
|
||||
return this.#interceptsInPhase(phase).size > 0;
|
||||
}
|
||||
handleRedirect(event) {
|
||||
// TODO: use event.redirectResponse;
|
||||
// Temporary workaround to emit ResponseCompleted event for redirects
|
||||
this.#response.hasExtraInfo = false;
|
||||
this.#response.decodedSize = 0;
|
||||
this.#response.encodedSize = 0;
|
||||
this.#response.info = event.redirectResponse;
|
||||
this.#emitEventsIfReady({
|
||||
wasRedirected: true,
|
||||
});
|
||||
}
|
||||
#emitEventsIfReady(options = {}) {
|
||||
const requestExtraInfoCompleted =
|
||||
// Flush redirects
|
||||
options.wasRedirected ||
|
||||
Boolean(this.#response.loadingFailed) ||
|
||||
this.#isDataUrl() ||
|
||||
Boolean(this.#request.extraInfo) ||
|
||||
// If the request is intercepted during the `authRequired` phase, there
|
||||
// will be no `Network.requestWillBeSentExtraInfo` CDP events.
|
||||
this.#isBlockedInPhase("authRequired" /* Network.InterceptPhase.AuthRequired */) ||
|
||||
// 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);
|
||||
const noInterceptionExpected = this.#isNonInterceptable();
|
||||
const requestInterceptionExpected = !noInterceptionExpected &&
|
||||
this.#isBlockedInPhase("beforeRequestSent" /* Network.InterceptPhase.BeforeRequestSent */);
|
||||
const requestInterceptionCompleted = !requestInterceptionExpected ||
|
||||
(requestInterceptionExpected && Boolean(this.#request.paused));
|
||||
if (Boolean(this.#request.info) &&
|
||||
(requestInterceptionExpected
|
||||
? requestInterceptionCompleted
|
||||
: requestExtraInfoCompleted)) {
|
||||
this.#emitEvent(this.#getBeforeRequestEvent.bind(this));
|
||||
}
|
||||
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);
|
||||
const responseInterceptionExpected = !noInterceptionExpected &&
|
||||
this.#isBlockedInPhase("responseStarted" /* Network.InterceptPhase.ResponseStarted */);
|
||||
if (this.#response.info ||
|
||||
(responseInterceptionExpected && Boolean(this.#response.paused))) {
|
||||
this.#emitEvent(this.#getResponseStartedEvent.bind(this));
|
||||
}
|
||||
const responseInterceptionCompleted = !responseInterceptionExpected ||
|
||||
(responseInterceptionExpected && Boolean(this.#response.paused));
|
||||
const loadingFinished = Boolean(this.#response.loadingFailed) ||
|
||||
Boolean(this.#response.loadingFinished);
|
||||
if (Boolean(this.#response.info) &&
|
||||
responseExtraInfoCompleted &&
|
||||
responseInterceptionCompleted &&
|
||||
(loadingFinished || options.wasRedirected)) {
|
||||
this.#emitEvent(this.#getResponseReceivedEvent.bind(this));
|
||||
this.#networkStorage.disposeRequest(this.id);
|
||||
}
|
||||
}
|
||||
onRequestWillBeSentEvent(event) {
|
||||
this.#request.info = event;
|
||||
this.#networkStorage.collectIfNeeded(this, "request" /* Network.DataType.Request */);
|
||||
this.#emitEventsIfReady();
|
||||
}
|
||||
onRequestWillBeSentExtraInfoEvent(event) {
|
||||
this.#request.extraInfo = event;
|
||||
this.#emitEventsIfReady();
|
||||
}
|
||||
onResponseReceivedExtraInfoEvent(event) {
|
||||
if (event.statusCode >= 300 &&
|
||||
event.statusCode <= 399 &&
|
||||
this.#request.info &&
|
||||
event.headers['location'] === this.#request.info.request.url) {
|
||||
// We received the Response Extra info for the redirect
|
||||
// Too late so we need to skip it as it will
|
||||
// fire wrongly for the last one
|
||||
return;
|
||||
}
|
||||
this.#response.extraInfo = event;
|
||||
this.#emitEventsIfReady();
|
||||
}
|
||||
onResponseReceivedEvent(event) {
|
||||
this.#response.hasExtraInfo = event.hasExtraInfo;
|
||||
this.#response.info = event.response;
|
||||
this.#networkStorage.collectIfNeeded(this, "response" /* Network.DataType.Response */);
|
||||
this.#emitEventsIfReady();
|
||||
}
|
||||
onServedFromCache() {
|
||||
this.#servedFromCache = true;
|
||||
this.#emitEventsIfReady();
|
||||
}
|
||||
onLoadingFinishedEvent(event) {
|
||||
this.#response.loadingFinished = event;
|
||||
this.#emitEventsIfReady();
|
||||
}
|
||||
onDataReceivedEvent(event) {
|
||||
this.#response.decodedSize += event.dataLength;
|
||||
this.#response.encodedSize += event.encodedDataLength;
|
||||
}
|
||||
onLoadingFailedEvent(event) {
|
||||
this.#response.loadingFailed = event;
|
||||
this.#emitEventsIfReady();
|
||||
this.#emitEvent(() => {
|
||||
return {
|
||||
method: protocol_js_1.ChromiumBidi.Network.EventNames.FetchError,
|
||||
params: {
|
||||
...this.#getBaseEventParams(),
|
||||
errorText: event.errorText,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
/** @see https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#method-failRequest */
|
||||
async failRequest(errorReason) {
|
||||
(0, assert_js_1.assert)(this.#fetchId, 'Network Interception not set-up.');
|
||||
await this.cdpClient.sendCommand('Fetch.failRequest', {
|
||||
requestId: this.#fetchId,
|
||||
errorReason,
|
||||
});
|
||||
this.#interceptPhase = undefined;
|
||||
}
|
||||
onRequestPaused(event) {
|
||||
this.#fetchId = event.requestId;
|
||||
// CDP https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#event-requestPaused
|
||||
if (event.responseStatusCode || event.responseErrorReason) {
|
||||
this.#response.paused = event;
|
||||
if (this.#isBlockedInPhase("responseStarted" /* Network.InterceptPhase.ResponseStarted */) &&
|
||||
// CDP may emit multiple events for a single request
|
||||
!this.#emittedEvents[protocol_js_1.ChromiumBidi.Network.EventNames.ResponseStarted] &&
|
||||
// Continue all response that have not enabled Network domain
|
||||
this.#fetchId !== this.id) {
|
||||
this.#interceptPhase = "responseStarted" /* Network.InterceptPhase.ResponseStarted */;
|
||||
}
|
||||
else {
|
||||
void this.#continueResponse();
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.#request.paused = event;
|
||||
if (this.#isBlockedInPhase("beforeRequestSent" /* Network.InterceptPhase.BeforeRequestSent */) &&
|
||||
// CDP may emit multiple events for a single request
|
||||
!this.#emittedEvents[protocol_js_1.ChromiumBidi.Network.EventNames.BeforeRequestSent] &&
|
||||
// Continue all requests that have not enabled Network domain
|
||||
this.#fetchId !== this.id) {
|
||||
this.#interceptPhase = "beforeRequestSent" /* Network.InterceptPhase.BeforeRequestSent */;
|
||||
}
|
||||
else {
|
||||
void this.#continueRequest();
|
||||
}
|
||||
}
|
||||
this.#emitEventsIfReady();
|
||||
}
|
||||
onAuthRequired(event) {
|
||||
this.#fetchId = event.requestId;
|
||||
this.#request.auth = event;
|
||||
if (this.#isBlockedInPhase("authRequired" /* Network.InterceptPhase.AuthRequired */) &&
|
||||
// Continue all auth requests that have not enabled Network domain
|
||||
this.#fetchId !== this.id) {
|
||||
this.#interceptPhase = "authRequired" /* Network.InterceptPhase.AuthRequired */;
|
||||
// Make sure the `network.beforeRequestSent` is emitted before
|
||||
// `network.authRequired`.
|
||||
this.#emitEventsIfReady();
|
||||
}
|
||||
else {
|
||||
void this.#continueWithAuth({
|
||||
response: 'Default',
|
||||
});
|
||||
}
|
||||
this.#emitEvent(() => {
|
||||
return {
|
||||
method: protocol_js_1.ChromiumBidi.Network.EventNames.AuthRequired,
|
||||
params: {
|
||||
...this.#getBaseEventParams("authRequired" /* Network.InterceptPhase.AuthRequired */),
|
||||
response: this.#getResponseEventParams(),
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
/** @see https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#method-continueRequest */
|
||||
async continueRequest(overrides = {}) {
|
||||
const overrideHeaders = this.#getOverrideHeader(overrides.headers, overrides.cookies);
|
||||
const headers = (0, NetworkUtils_js_1.cdpFetchHeadersFromBidiNetworkHeaders)(overrideHeaders);
|
||||
const postData = getCdpBodyFromBiDiBytesValue(overrides.body);
|
||||
await this.#continueRequest({
|
||||
url: overrides.url,
|
||||
method: overrides.method,
|
||||
headers,
|
||||
postData,
|
||||
});
|
||||
this.#requestOverrides = {
|
||||
url: overrides.url,
|
||||
method: overrides.method,
|
||||
headers: overrides.headers,
|
||||
cookies: overrides.cookies,
|
||||
bodySize: getSizeFromBiDiBytesValue(overrides.body),
|
||||
};
|
||||
}
|
||||
async #continueRequest(overrides = {}) {
|
||||
(0, assert_js_1.assert)(this.#fetchId, 'Network Interception not set-up.');
|
||||
await this.cdpClient.sendCommand('Fetch.continueRequest', {
|
||||
requestId: this.#fetchId,
|
||||
url: overrides.url,
|
||||
method: overrides.method,
|
||||
headers: overrides.headers,
|
||||
postData: overrides.postData,
|
||||
});
|
||||
this.#interceptPhase = undefined;
|
||||
}
|
||||
/** @see https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#method-continueResponse */
|
||||
async continueResponse(overrides = {}) {
|
||||
if (this.interceptPhase === "authRequired" /* Network.InterceptPhase.AuthRequired */) {
|
||||
if (overrides.credentials) {
|
||||
await Promise.all([
|
||||
this.waitNextPhase,
|
||||
await this.#continueWithAuth({
|
||||
response: 'ProvideCredentials',
|
||||
username: overrides.credentials.username,
|
||||
password: overrides.credentials.password,
|
||||
}),
|
||||
]);
|
||||
}
|
||||
else {
|
||||
// We need to use `ProvideCredentials`
|
||||
// As `Default` may cancel the request
|
||||
return await this.#continueWithAuth({
|
||||
response: 'ProvideCredentials',
|
||||
});
|
||||
}
|
||||
}
|
||||
if (this.#interceptPhase === "responseStarted" /* Network.InterceptPhase.ResponseStarted */) {
|
||||
const overrideHeaders = this.#getOverrideHeader(overrides.headers, overrides.cookies);
|
||||
const responseHeaders = (0, NetworkUtils_js_1.cdpFetchHeadersFromBidiNetworkHeaders)(overrideHeaders);
|
||||
await this.#continueResponse({
|
||||
responseCode: overrides.statusCode ?? this.#response.paused?.responseStatusCode,
|
||||
responsePhrase: overrides.reasonPhrase ?? this.#response.paused?.responseStatusText,
|
||||
responseHeaders: responseHeaders ?? this.#response.paused?.responseHeaders,
|
||||
});
|
||||
this.#responseOverrides = {
|
||||
statusCode: overrides.statusCode,
|
||||
headers: overrideHeaders,
|
||||
};
|
||||
}
|
||||
}
|
||||
async #continueResponse({ responseCode, responsePhrase, responseHeaders, } = {}) {
|
||||
(0, assert_js_1.assert)(this.#fetchId, 'Network Interception not set-up.');
|
||||
await this.cdpClient.sendCommand('Fetch.continueResponse', {
|
||||
requestId: this.#fetchId,
|
||||
responseCode,
|
||||
responsePhrase,
|
||||
responseHeaders,
|
||||
});
|
||||
this.#interceptPhase = undefined;
|
||||
}
|
||||
/** @see https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#method-continueWithAuth */
|
||||
async continueWithAuth(authChallenge) {
|
||||
let username;
|
||||
let password;
|
||||
if (authChallenge.action === 'provideCredentials') {
|
||||
const { credentials } = authChallenge;
|
||||
username = credentials.username;
|
||||
password = credentials.password;
|
||||
}
|
||||
const response = (0, NetworkUtils_js_1.cdpAuthChallengeResponseFromBidiAuthContinueWithAuthAction)(authChallenge.action);
|
||||
await this.#continueWithAuth({
|
||||
response,
|
||||
username,
|
||||
password,
|
||||
});
|
||||
}
|
||||
/** @see https://chromedevtools.github.io/devtools-protocol/tot/Fetch/#method-provideResponse */
|
||||
async provideResponse(overrides) {
|
||||
(0, assert_js_1.assert)(this.#fetchId, 'Network Interception not set-up.');
|
||||
// We need to pass through if the request is already in
|
||||
// AuthRequired phase
|
||||
if (this.interceptPhase === "authRequired" /* Network.InterceptPhase.AuthRequired */) {
|
||||
// We need to use `ProvideCredentials`
|
||||
// As `Default` may cancel the request
|
||||
return await this.#continueWithAuth({
|
||||
response: 'ProvideCredentials',
|
||||
});
|
||||
}
|
||||
// If we don't modify the response
|
||||
// just continue the request
|
||||
if (!overrides.body && !overrides.headers) {
|
||||
return await this.#continueRequest();
|
||||
}
|
||||
const overrideHeaders = this.#getOverrideHeader(overrides.headers, overrides.cookies);
|
||||
const responseHeaders = (0, NetworkUtils_js_1.cdpFetchHeadersFromBidiNetworkHeaders)(overrideHeaders);
|
||||
const responseCode = overrides.statusCode ?? this.#statusCode ?? 200;
|
||||
await this.cdpClient.sendCommand('Fetch.fulfillRequest', {
|
||||
requestId: this.#fetchId,
|
||||
responseCode,
|
||||
responsePhrase: overrides.reasonPhrase,
|
||||
responseHeaders,
|
||||
body: getCdpBodyFromBiDiBytesValue(overrides.body),
|
||||
});
|
||||
this.#interceptPhase = undefined;
|
||||
}
|
||||
dispose() {
|
||||
this.waitNextPhase.reject(new Error('waitNextPhase disposed'));
|
||||
}
|
||||
async #continueWithAuth(authChallengeResponse) {
|
||||
(0, assert_js_1.assert)(this.#fetchId, 'Network Interception not set-up.');
|
||||
await this.cdpClient.sendCommand('Fetch.continueWithAuth', {
|
||||
requestId: this.#fetchId,
|
||||
authChallengeResponse,
|
||||
});
|
||||
this.#interceptPhase = undefined;
|
||||
}
|
||||
#emitEvent(getEvent) {
|
||||
let event;
|
||||
try {
|
||||
event = getEvent();
|
||||
}
|
||||
catch (error) {
|
||||
this.#logger?.(log_js_1.LogType.debugError, error);
|
||||
return;
|
||||
}
|
||||
if (this.#isIgnoredEvent() ||
|
||||
(this.#emittedEvents[event.method] &&
|
||||
// Special case this event can be emitted multiple times
|
||||
event.method !== protocol_js_1.ChromiumBidi.Network.EventNames.AuthRequired)) {
|
||||
return;
|
||||
}
|
||||
this.#phaseChanged();
|
||||
this.#emittedEvents[event.method] = true;
|
||||
if (this.#context) {
|
||||
this.#eventManager.registerEvent(Object.assign(event, {
|
||||
type: 'event',
|
||||
}), this.#context);
|
||||
}
|
||||
else {
|
||||
this.#eventManager.registerGlobalEvent(Object.assign(event, {
|
||||
type: 'event',
|
||||
}));
|
||||
}
|
||||
}
|
||||
#getBaseEventParams(phase) {
|
||||
const interceptProps = {
|
||||
isBlocked: false,
|
||||
};
|
||||
if (phase) {
|
||||
const blockedBy = this.#interceptsInPhase(phase);
|
||||
interceptProps.isBlocked = blockedBy.size > 0;
|
||||
if (interceptProps.isBlocked) {
|
||||
interceptProps.intercepts = [...blockedBy];
|
||||
}
|
||||
}
|
||||
return {
|
||||
context: this.#context,
|
||||
navigation: this.#navigationId,
|
||||
redirectCount: this.#redirectCount,
|
||||
request: this.#getRequestData(),
|
||||
// Timestamp should be in milliseconds, while CDP provides it in seconds.
|
||||
timestamp: Math.round((0, NetworkUtils_js_1.getTiming)(this.#request.info?.wallTime) * 1000),
|
||||
// Contains isBlocked and intercepts
|
||||
...interceptProps,
|
||||
};
|
||||
}
|
||||
#getResponseEventParams() {
|
||||
// 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;
|
||||
}
|
||||
// TODO: Also this.#response.paused?.responseHeaders have to be merged here.
|
||||
const cdpHeaders = this.#response.info?.headers ?? {};
|
||||
const cdpRawHeaders = this.#response.extraInfo?.headers ?? {};
|
||||
for (const [key, value] of Object.entries(cdpRawHeaders)) {
|
||||
cdpHeaders[key] = value;
|
||||
}
|
||||
const headers = (0, NetworkUtils_js_1.bidiNetworkHeadersFromCdpNetworkHeaders)(cdpHeaders);
|
||||
const authChallenges = this.#authChallenges;
|
||||
const response = {
|
||||
url: this.url,
|
||||
protocol: this.#response.info?.protocol ?? '',
|
||||
status: this.#statusCode ?? -1, // TODO: Throw an exception or use some other status code?
|
||||
statusText: this.#response.info?.statusText ||
|
||||
this.#response.paused?.responseStatusText ||
|
||||
'',
|
||||
fromCache: this.#response.info?.fromDiskCache ||
|
||||
this.#response.info?.fromPrefetchCache ||
|
||||
this.#servedFromCache,
|
||||
headers: this.#responseOverrides?.headers ?? headers,
|
||||
mimeType: this.#response.info?.mimeType || '',
|
||||
// TODO: this should be the size for the entire HTTP response.
|
||||
bytesReceived: this.encodedResponseBodySize,
|
||||
headersSize: (0, NetworkUtils_js_1.computeHeadersSize)(headers),
|
||||
bodySize: this.encodedResponseBodySize,
|
||||
content: {
|
||||
size: this.#response.decodedSize ?? 0,
|
||||
},
|
||||
...(authChallenges ? { authChallenges } : {}),
|
||||
};
|
||||
return {
|
||||
...response,
|
||||
'goog:securityDetails': this.#response.info?.securityDetails,
|
||||
};
|
||||
}
|
||||
get encodedResponseBodySize() {
|
||||
return (this.#response.loadingFinished?.encodedDataLength ??
|
||||
this.#response.info?.encodedDataLength ??
|
||||
this.#response.encodedSize ??
|
||||
0);
|
||||
}
|
||||
#getRequestData() {
|
||||
const headers = this.#requestHeaders;
|
||||
const request = {
|
||||
request: this.#id,
|
||||
url: this.url,
|
||||
method: this.#method ?? _a.unknownParameter,
|
||||
headers,
|
||||
cookies: this.#cookies,
|
||||
headersSize: (0, NetworkUtils_js_1.computeHeadersSize)(headers),
|
||||
bodySize: this.bodySize,
|
||||
// TODO: populate
|
||||
destination: this.#getDestination(),
|
||||
// TODO: populate
|
||||
initiatorType: this.#getInitiatorType(),
|
||||
timings: this.#timings,
|
||||
};
|
||||
return {
|
||||
...request,
|
||||
'goog:postData': this.#request.info?.request?.postData,
|
||||
'goog:hasPostData': this.#request.info?.request?.hasPostData,
|
||||
'goog:resourceType': this.#request.info?.type,
|
||||
'goog:resourceInitiator': this.#request.info?.initiator,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Heuristic trying to guess the destination.
|
||||
* Specification: https://fetch.spec.whatwg.org/#concept-request-destination.
|
||||
* Specified values: "audio", "audioworklet", "document", "embed", "font", "frame",
|
||||
* "iframe", "image", "json", "manifest", "object", "paintworklet", "report", "script",
|
||||
* "serviceworker", "sharedworker", "style", "track", "video", "webidentity", "worker",
|
||||
* "xslt".
|
||||
*/
|
||||
#getDestination() {
|
||||
switch (this.#request.info?.type) {
|
||||
case 'Script':
|
||||
return 'script';
|
||||
case 'Stylesheet':
|
||||
return 'style';
|
||||
case 'Image':
|
||||
return 'image';
|
||||
case 'Document':
|
||||
// If request to document is initiated by parser, assume it is expected to
|
||||
// arrive in an iframe. Otherwise, consider it is a navigation and the request
|
||||
// result will end up in the document.
|
||||
return this.#request.info?.initiator.type === 'parser'
|
||||
? 'iframe'
|
||||
: 'document';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Heuristic trying to guess the initiator type.
|
||||
* Specification: https://fetch.spec.whatwg.org/#request-initiator-type.
|
||||
* Specified values: "audio", "beacon", "body", "css", "early-hints", "embed", "fetch",
|
||||
* "font", "frame", "iframe", "image", "img", "input", "link", "object", "ping",
|
||||
* "script", "track", "video", "xmlhttprequest", "other".
|
||||
*/
|
||||
#getInitiatorType() {
|
||||
if (this.#request.info?.initiator.type === 'parser') {
|
||||
switch (this.#request.info?.type) {
|
||||
case 'Document':
|
||||
// The request to document is initiated by the parser. Assuming it's an iframe.
|
||||
return 'iframe';
|
||||
case 'Font':
|
||||
// If the document's url is not the parser's url, assume the resource is loaded
|
||||
// from css. Otherwise, it's a `font` element.
|
||||
return this.#request.info?.initiator?.url ===
|
||||
this.#request.info?.documentURL
|
||||
? 'font'
|
||||
: 'css';
|
||||
case 'Image':
|
||||
// If the document's url is not the parser's url, assume the resource is loaded
|
||||
// from css. Otherwise, it's a `img` element.
|
||||
return this.#request.info?.initiator?.url ===
|
||||
this.#request.info?.documentURL
|
||||
? 'img'
|
||||
: 'css';
|
||||
case 'Script':
|
||||
return 'script';
|
||||
case 'Stylesheet':
|
||||
return 'link';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
if (this.#request?.info?.type === 'Fetch') {
|
||||
return 'fetch';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
#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: _a.#getInitiator(this.#request.info.initiator.type),
|
||||
columnNumber: this.#request.info.initiator.columnNumber,
|
||||
lineNumber: this.#request.info.initiator.lineNumber,
|
||||
stackTrace: this.#request.info.initiator.stack,
|
||||
request: this.#request.info.initiator.requestId,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
#getResponseStartedEvent() {
|
||||
return {
|
||||
method: protocol_js_1.ChromiumBidi.Network.EventNames.ResponseStarted,
|
||||
params: {
|
||||
...this.#getBaseEventParams("responseStarted" /* Network.InterceptPhase.ResponseStarted */),
|
||||
response: this.#getResponseEventParams(),
|
||||
},
|
||||
};
|
||||
}
|
||||
#getResponseReceivedEvent() {
|
||||
return {
|
||||
method: protocol_js_1.ChromiumBidi.Network.EventNames.ResponseCompleted,
|
||||
params: {
|
||||
...this.#getBaseEventParams(),
|
||||
response: this.#getResponseEventParams(),
|
||||
},
|
||||
};
|
||||
}
|
||||
#isIgnoredEvent() {
|
||||
const faviconUrl = '/favicon.ico';
|
||||
return (this.#request.paused?.request.url.endsWith(faviconUrl) ??
|
||||
this.#request.info?.request.url.endsWith(faviconUrl) ??
|
||||
false);
|
||||
}
|
||||
#getOverrideHeader(headers, cookies) {
|
||||
if (!headers && !cookies) {
|
||||
return undefined;
|
||||
}
|
||||
let overrideHeaders = headers;
|
||||
const cookieHeader = (0, NetworkUtils_js_1.networkHeaderFromCookieHeaders)(cookies);
|
||||
if (cookieHeader && !overrideHeaders) {
|
||||
overrideHeaders = this.#requestHeaders;
|
||||
}
|
||||
if (cookieHeader && overrideHeaders) {
|
||||
overrideHeaders.filter((header) => header.name.localeCompare('cookie', undefined, {
|
||||
sensitivity: 'base',
|
||||
}) !== 0);
|
||||
overrideHeaders.push(cookieHeader);
|
||||
}
|
||||
return overrideHeaders;
|
||||
}
|
||||
static #getInitiator(initiatorType) {
|
||||
switch (initiatorType) {
|
||||
case 'parser':
|
||||
case 'script':
|
||||
case 'preflight':
|
||||
return initiatorType;
|
||||
default:
|
||||
return 'other';
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.NetworkRequest = NetworkRequest;
|
||||
_a = NetworkRequest;
|
||||
function getCdpBodyFromBiDiBytesValue(body) {
|
||||
let parsedBody;
|
||||
if (body?.type === 'string') {
|
||||
parsedBody = (0, NetworkUtils_js_1.stringToBase64)(body.value);
|
||||
}
|
||||
else if (body?.type === 'base64') {
|
||||
parsedBody = body.value;
|
||||
}
|
||||
return parsedBody;
|
||||
}
|
||||
function getSizeFromBiDiBytesValue(body) {
|
||||
if (body?.type === 'string') {
|
||||
return body.value.length;
|
||||
}
|
||||
else if (body?.type === 'base64') {
|
||||
return atob(body.value).length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
//# sourceMappingURL=NetworkRequest.js.map
|
||||
1
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/network/NetworkRequest.js.map
generated
vendored
Normal file
1
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/network/NetworkRequest.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
57
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/network/NetworkStorage.d.ts
generated
vendored
Normal file
57
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/network/NetworkStorage.d.ts
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
import { type BrowsingContext, Network } from '../../../protocol/protocol.js';
|
||||
import type { LoggerFn } from '../../../utils/log.js';
|
||||
import type { CdpClient } from '../../BidiMapper.js';
|
||||
import type { CdpTarget } from '../cdp/CdpTarget.js';
|
||||
import type { BrowsingContextStorage } from '../context/BrowsingContextStorage.js';
|
||||
import type { EventManager } from '../session/EventManager.js';
|
||||
import { NetworkRequest } from './NetworkRequest.js';
|
||||
import { type ParsedUrlPattern } from './NetworkUtils.js';
|
||||
export declare const MAX_TOTAL_COLLECTED_SIZE = 200000000;
|
||||
type NetworkInterception = Omit<Network.AddInterceptParameters, 'urlPatterns'> & {
|
||||
urlPatterns: ParsedUrlPattern[];
|
||||
};
|
||||
/** Stores network and intercept maps. */
|
||||
export declare class NetworkStorage {
|
||||
#private;
|
||||
constructor(eventManager: EventManager, browsingContextStorage: BrowsingContextStorage, browserClient: CdpClient, logger?: LoggerFn);
|
||||
onCdpTargetCreated(cdpTarget: CdpTarget): void;
|
||||
getCollectedData(params: Network.GetDataParameters): Promise<Network.GetDataResult>;
|
||||
collectIfNeeded(request: NetworkRequest, dataType: Network.DataType): void;
|
||||
getInterceptionStages(browsingContextId: BrowsingContext.BrowsingContext): {
|
||||
request: boolean;
|
||||
response: boolean;
|
||||
auth: boolean;
|
||||
};
|
||||
getInterceptsForPhase(request: NetworkRequest, phase: Network.InterceptPhase): Set<Network.Intercept>;
|
||||
disposeRequestMap(sessionId: string): 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;
|
||||
getRequestsByTarget(target: CdpTarget): NetworkRequest[];
|
||||
getRequestById(id: Network.Request): NetworkRequest | undefined;
|
||||
getRequestByFetchId(fetchId: Network.Request): NetworkRequest | undefined;
|
||||
addRequest(request: NetworkRequest): void;
|
||||
/**
|
||||
* Disposes the given request, if no collectors targeting it are left.
|
||||
*/
|
||||
disposeRequest(id: Network.Request): void;
|
||||
/**
|
||||
* Gets the virtual navigation ID for the given navigable ID.
|
||||
*/
|
||||
getNavigationId(contextId: string | undefined): string | null;
|
||||
set defaultCacheBehavior(behavior: Network.SetCacheBehaviorParameters['cacheBehavior']);
|
||||
get defaultCacheBehavior(): Network.SetCacheBehaviorParameters["cacheBehavior"];
|
||||
addDataCollector(params: Network.AddDataCollectorParameters): string;
|
||||
removeDataCollector(params: Network.RemoveDataCollectorParameters): void;
|
||||
disownData(params: Network.DisownDataParameters): void;
|
||||
}
|
||||
export {};
|
||||
353
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/network/NetworkStorage.js
generated
vendored
Normal file
353
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/network/NetworkStorage.js
generated
vendored
Normal file
@@ -0,0 +1,353 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.NetworkStorage = exports.MAX_TOTAL_COLLECTED_SIZE = void 0;
|
||||
const protocol_js_1 = require("../../../protocol/protocol.js");
|
||||
const uuid_js_1 = require("../../../utils/uuid.js");
|
||||
const CollectorsStorage_js_1 = require("./CollectorsStorage.js");
|
||||
const NetworkRequest_js_1 = require("./NetworkRequest.js");
|
||||
const NetworkUtils_js_1 = require("./NetworkUtils.js");
|
||||
// The default total data size limit in CDP.
|
||||
// https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/inspector/inspector_network_agent.cc;drc=da1f749634c9a401cc756f36c2e6ce233e1c9b4d;l=133
|
||||
exports.MAX_TOTAL_COLLECTED_SIZE = 200_000_000;
|
||||
/** Stores network and intercept maps. */
|
||||
class NetworkStorage {
|
||||
#browsingContextStorage;
|
||||
#eventManager;
|
||||
#collectorsStorage;
|
||||
#logger;
|
||||
/**
|
||||
* A map from network request ID to Network Request objects.
|
||||
* Needed as long as information about requests comes from different events.
|
||||
*/
|
||||
#requests = new Map();
|
||||
/** A map from intercept ID to track active network intercepts. */
|
||||
#intercepts = new Map();
|
||||
#defaultCacheBehavior = 'default';
|
||||
constructor(eventManager, browsingContextStorage, browserClient, logger) {
|
||||
this.#browsingContextStorage = browsingContextStorage;
|
||||
this.#eventManager = eventManager;
|
||||
this.#collectorsStorage = new CollectorsStorage_js_1.CollectorsStorage(exports.MAX_TOTAL_COLLECTED_SIZE, logger);
|
||||
browserClient.on('Target.detachedFromTarget', ({ sessionId }) => {
|
||||
this.disposeRequestMap(sessionId);
|
||||
});
|
||||
this.#logger = logger;
|
||||
}
|
||||
/**
|
||||
* 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, cdpTarget, redirectCount) {
|
||||
let request = this.getRequestById(id);
|
||||
if (redirectCount === undefined && request) {
|
||||
// Force re-creating requests for redirects.
|
||||
return request;
|
||||
}
|
||||
request = new NetworkRequest_js_1.NetworkRequest(id, this.#eventManager, this, cdpTarget, redirectCount, this.#logger);
|
||||
this.addRequest(request);
|
||||
return request;
|
||||
}
|
||||
onCdpTargetCreated(cdpTarget) {
|
||||
const cdpClient = cdpTarget.cdpClient;
|
||||
// TODO: Wrap into object
|
||||
const listeners = [
|
||||
[
|
||||
'Network.requestWillBeSent',
|
||||
(params) => {
|
||||
const request = this.getRequestById(params.requestId);
|
||||
request?.updateCdpTarget(cdpTarget);
|
||||
if (request && request.isRedirecting()) {
|
||||
request.handleRedirect(params);
|
||||
this.disposeRequest(params.requestId);
|
||||
this.#getOrCreateNetworkRequest(params.requestId, cdpTarget, request.redirectCount + 1).onRequestWillBeSentEvent(params);
|
||||
}
|
||||
else {
|
||||
this.#getOrCreateNetworkRequest(params.requestId, cdpTarget).onRequestWillBeSentEvent(params);
|
||||
}
|
||||
},
|
||||
],
|
||||
[
|
||||
'Network.requestWillBeSentExtraInfo',
|
||||
(params) => {
|
||||
const request = this.#getOrCreateNetworkRequest(params.requestId, cdpTarget);
|
||||
request.updateCdpTarget(cdpTarget);
|
||||
request.onRequestWillBeSentExtraInfoEvent(params);
|
||||
},
|
||||
],
|
||||
[
|
||||
'Network.responseReceived',
|
||||
(params) => {
|
||||
const request = this.#getOrCreateNetworkRequest(params.requestId, cdpTarget);
|
||||
request.updateCdpTarget(cdpTarget);
|
||||
request.onResponseReceivedEvent(params);
|
||||
},
|
||||
],
|
||||
[
|
||||
'Network.responseReceivedExtraInfo',
|
||||
(params) => {
|
||||
const request = this.#getOrCreateNetworkRequest(params.requestId, cdpTarget);
|
||||
request.updateCdpTarget(cdpTarget);
|
||||
request.onResponseReceivedExtraInfoEvent(params);
|
||||
},
|
||||
],
|
||||
[
|
||||
'Network.requestServedFromCache',
|
||||
(params) => {
|
||||
const request = this.#getOrCreateNetworkRequest(params.requestId, cdpTarget);
|
||||
request.updateCdpTarget(cdpTarget);
|
||||
request.onServedFromCache();
|
||||
},
|
||||
],
|
||||
[
|
||||
'Fetch.requestPaused',
|
||||
(event) => {
|
||||
const request = this.#getOrCreateNetworkRequest(
|
||||
// CDP quirk if the Network domain is not present this is undefined
|
||||
event.networkId ?? event.requestId, cdpTarget);
|
||||
request.updateCdpTarget(cdpTarget);
|
||||
request.onRequestPaused(event);
|
||||
},
|
||||
],
|
||||
[
|
||||
'Fetch.authRequired',
|
||||
(event) => {
|
||||
let request = this.getRequestByFetchId(event.requestId);
|
||||
if (!request) {
|
||||
request = this.#getOrCreateNetworkRequest(event.requestId, cdpTarget);
|
||||
}
|
||||
request.updateCdpTarget(cdpTarget);
|
||||
request.onAuthRequired(event);
|
||||
},
|
||||
],
|
||||
[
|
||||
'Network.dataReceived',
|
||||
(params) => {
|
||||
const request = this.getRequestById(params.requestId);
|
||||
request?.updateCdpTarget(cdpTarget);
|
||||
request?.onDataReceivedEvent(params);
|
||||
},
|
||||
],
|
||||
[
|
||||
'Network.loadingFailed',
|
||||
(params) => {
|
||||
const request = this.#getOrCreateNetworkRequest(params.requestId, cdpTarget);
|
||||
request.updateCdpTarget(cdpTarget);
|
||||
request.onLoadingFailedEvent(params);
|
||||
},
|
||||
],
|
||||
[
|
||||
'Network.loadingFinished',
|
||||
(params) => {
|
||||
const request = this.getRequestById(params.requestId);
|
||||
request?.updateCdpTarget(cdpTarget);
|
||||
request?.onLoadingFinishedEvent(params);
|
||||
},
|
||||
],
|
||||
];
|
||||
for (const [event, listener] of listeners) {
|
||||
cdpClient.on(event, listener);
|
||||
}
|
||||
}
|
||||
async getCollectedData(params) {
|
||||
if (!this.#collectorsStorage.isCollected(params.request, params.dataType, params.collector)) {
|
||||
throw new protocol_js_1.NoSuchNetworkDataException(params.collector === undefined
|
||||
? `No collected ${params.dataType} data`
|
||||
: `Collector ${params.collector} didn't collect ${params.dataType} data`);
|
||||
}
|
||||
if (params.disown && params.collector === undefined) {
|
||||
throw new protocol_js_1.InvalidArgumentException('Cannot disown collected data without collector ID');
|
||||
}
|
||||
const request = this.getRequestById(params.request);
|
||||
if (request === undefined) {
|
||||
throw new protocol_js_1.NoSuchNetworkDataException(`No data for ${params.request}`);
|
||||
}
|
||||
let result = undefined;
|
||||
switch (params.dataType) {
|
||||
case "response" /* Network.DataType.Response */:
|
||||
result = await this.#getCollectedResponseData(request);
|
||||
break;
|
||||
case "request" /* Network.DataType.Request */:
|
||||
result = await this.#getCollectedRequestData(request);
|
||||
break;
|
||||
default:
|
||||
throw new protocol_js_1.UnsupportedOperationException(`Unsupported data type ${params.dataType}`);
|
||||
}
|
||||
if (params.disown && params.collector !== undefined) {
|
||||
this.#collectorsStorage.disownData(request.id, params.dataType, params.collector);
|
||||
// `disposeRequest` disposes request only if no other collectors for it are left.
|
||||
this.disposeRequest(request.id);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
async #getCollectedResponseData(request) {
|
||||
try {
|
||||
const responseBody = await request.cdpClient.sendCommand('Network.getResponseBody', { requestId: request.id });
|
||||
return {
|
||||
bytes: {
|
||||
type: responseBody.base64Encoded ? 'base64' : 'string',
|
||||
value: responseBody.body,
|
||||
},
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
if (error.code === -32000 /* CdpErrorConstants.GENERIC_ERROR */ &&
|
||||
error.message === 'No resource with given identifier found') {
|
||||
// The data has be gone for whatever reason.
|
||||
throw new protocol_js_1.NoSuchNetworkDataException(`Response data was disposed`);
|
||||
}
|
||||
if (error.code === -32001 /* CdpErrorConstants.CONNECTION_CLOSED */) {
|
||||
// The request's CDP session is gone. http://b/450771615.
|
||||
throw new protocol_js_1.NoSuchNetworkDataException(`Response data is disposed after the related page`);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
async #getCollectedRequestData(request) {
|
||||
// TODO: handle CDP error in case of the renderer is gone.
|
||||
const requestPostData = await request.cdpClient.sendCommand('Network.getRequestPostData', { requestId: request.id });
|
||||
return {
|
||||
bytes: {
|
||||
type: 'string',
|
||||
value: requestPostData.postData,
|
||||
},
|
||||
};
|
||||
}
|
||||
collectIfNeeded(request, dataType) {
|
||||
this.#collectorsStorage.collectIfNeeded(request, dataType, request.cdpTarget.topLevelId, request.cdpTarget.userContext);
|
||||
}
|
||||
getInterceptionStages(browsingContextId) {
|
||||
const stages = {
|
||||
request: false,
|
||||
response: false,
|
||||
auth: false,
|
||||
};
|
||||
for (const intercept of this.#intercepts.values()) {
|
||||
if (intercept.contexts &&
|
||||
!intercept.contexts.includes(browsingContextId)) {
|
||||
continue;
|
||||
}
|
||||
stages.request ||= intercept.phases.includes("beforeRequestSent" /* Network.InterceptPhase.BeforeRequestSent */);
|
||||
stages.response ||= intercept.phases.includes("responseStarted" /* Network.InterceptPhase.ResponseStarted */);
|
||||
stages.auth ||= intercept.phases.includes("authRequired" /* Network.InterceptPhase.AuthRequired */);
|
||||
}
|
||||
return stages;
|
||||
}
|
||||
getInterceptsForPhase(request, phase) {
|
||||
if (request.url === NetworkRequest_js_1.NetworkRequest.unknownParameter) {
|
||||
return new Set();
|
||||
}
|
||||
const intercepts = new Set();
|
||||
for (const [interceptId, intercept] of this.#intercepts.entries()) {
|
||||
if (!intercept.phases.includes(phase) ||
|
||||
(intercept.contexts &&
|
||||
!intercept.contexts.includes(request.cdpTarget.topLevelId))) {
|
||||
continue;
|
||||
}
|
||||
if (intercept.urlPatterns.length === 0) {
|
||||
intercepts.add(interceptId);
|
||||
continue;
|
||||
}
|
||||
for (const pattern of intercept.urlPatterns) {
|
||||
if ((0, NetworkUtils_js_1.matchUrlPattern)(pattern, request.url)) {
|
||||
intercepts.add(interceptId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return intercepts;
|
||||
}
|
||||
disposeRequestMap(sessionId) {
|
||||
for (const request of this.#requests.values()) {
|
||||
if (request.cdpClient.sessionId === sessionId) {
|
||||
this.#requests.delete(request.id);
|
||||
request.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 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.#intercepts.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.#intercepts.has(intercept)) {
|
||||
throw new protocol_js_1.NoSuchInterceptException(`Intercept '${intercept}' does not exist.`);
|
||||
}
|
||||
this.#intercepts.delete(intercept);
|
||||
}
|
||||
getRequestsByTarget(target) {
|
||||
const requests = [];
|
||||
for (const request of this.#requests.values()) {
|
||||
if (request.cdpTarget === target) {
|
||||
requests.push(request);
|
||||
}
|
||||
}
|
||||
return requests;
|
||||
}
|
||||
getRequestById(id) {
|
||||
return this.#requests.get(id);
|
||||
}
|
||||
getRequestByFetchId(fetchId) {
|
||||
for (const request of this.#requests.values()) {
|
||||
if (request.fetchId === fetchId) {
|
||||
return request;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
addRequest(request) {
|
||||
this.#requests.set(request.id, request);
|
||||
}
|
||||
/**
|
||||
* Disposes the given request, if no collectors targeting it are left.
|
||||
*/
|
||||
disposeRequest(id) {
|
||||
if (this.#collectorsStorage.isCollected(id)) {
|
||||
// Keep request, as it's data can be accessed later.
|
||||
return;
|
||||
}
|
||||
// TODO: dispose Network data from Chromium once there is a CDP command for that.
|
||||
this.#requests.delete(id);
|
||||
}
|
||||
/**
|
||||
* Gets the virtual navigation ID for the given navigable ID.
|
||||
*/
|
||||
getNavigationId(contextId) {
|
||||
if (contextId === undefined) {
|
||||
return null;
|
||||
}
|
||||
return (this.#browsingContextStorage.findContext(contextId)?.navigationId ?? null);
|
||||
}
|
||||
set defaultCacheBehavior(behavior) {
|
||||
this.#defaultCacheBehavior = behavior;
|
||||
}
|
||||
get defaultCacheBehavior() {
|
||||
return this.#defaultCacheBehavior;
|
||||
}
|
||||
addDataCollector(params) {
|
||||
return this.#collectorsStorage.addDataCollector(params);
|
||||
}
|
||||
removeDataCollector(params) {
|
||||
const releasedRequests = this.#collectorsStorage.removeDataCollector(params.collector);
|
||||
releasedRequests.map((request) => this.disposeRequest(request));
|
||||
}
|
||||
disownData(params) {
|
||||
if (!this.#collectorsStorage.isCollected(params.request, params.dataType, params.collector)) {
|
||||
throw new protocol_js_1.NoSuchNetworkDataException(`Collector ${params.collector} didn't collect ${params.dataType} data`);
|
||||
}
|
||||
this.#collectorsStorage.disownData(params.request, params.dataType, params.collector);
|
||||
// `disposeRequest` disposes request only if no other collectors for it are left.
|
||||
this.disposeRequest(params.request);
|
||||
}
|
||||
}
|
||||
exports.NetworkStorage = NetworkStorage;
|
||||
//# sourceMappingURL=NetworkStorage.js.map
|
||||
1
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/network/NetworkStorage.js.map
generated
vendored
Normal file
1
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/network/NetworkStorage.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
59
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/network/NetworkUtils.d.ts
generated
vendored
Normal file
59
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/network/NetworkUtils.d.ts
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* @fileoverview Utility functions for the Network module.
|
||||
*/
|
||||
import type { Protocol } from 'devtools-protocol';
|
||||
import { Network, type Storage } from '../../../protocol/protocol.js';
|
||||
export declare function computeHeadersSize(headers: Network.Header[]): number;
|
||||
export declare function stringToBase64(str: string): string;
|
||||
/** Converts from CDP Network domain headers to BiDi network headers. */
|
||||
export declare function bidiNetworkHeadersFromCdpNetworkHeaders(headers?: Protocol.Network.Headers): Network.Header[];
|
||||
/** Converts from CDP Fetch domain headers to BiDi network headers. */
|
||||
export declare function bidiNetworkHeadersFromCdpNetworkHeadersEntries(headers?: Protocol.Fetch.HeaderEntry[]): 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;
|
||||
export declare function networkHeaderFromCookieHeaders(headers?: Network.CookieHeader[]): Network.Header | 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;
|
||||
/**
|
||||
* Decodes a byte value to a string.
|
||||
* @param {Network.BytesValue} value
|
||||
* @return {string}
|
||||
*/
|
||||
export declare function deserializeByteValue(value: Network.BytesValue): string;
|
||||
/**
|
||||
* 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;
|
||||
export declare function sameSiteBiDiToCdp(sameSite: Network.SameSite): Protocol.Network.CookieSameSite;
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
export declare function isSpecialScheme(protocol: string): boolean;
|
||||
export interface ParsedUrlPattern {
|
||||
protocol?: string;
|
||||
hostname?: string;
|
||||
port?: string;
|
||||
pathname?: string;
|
||||
search?: string;
|
||||
}
|
||||
/** Matches the given URLPattern against the given URL. */
|
||||
export declare function matchUrlPattern(pattern: ParsedUrlPattern, url: string): boolean;
|
||||
export declare function bidiBodySizeFromCdpPostDataEntries(entries: Protocol.Network.PostDataEntry[]): number;
|
||||
export declare function getTiming(timing: number | undefined, offset?: number): number;
|
||||
322
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/network/NetworkUtils.js
generated
vendored
Normal file
322
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/network/NetworkUtils.js
generated
vendored
Normal file
@@ -0,0 +1,322 @@
|
||||
"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.computeHeadersSize = computeHeadersSize;
|
||||
exports.stringToBase64 = stringToBase64;
|
||||
exports.bidiNetworkHeadersFromCdpNetworkHeaders = bidiNetworkHeadersFromCdpNetworkHeaders;
|
||||
exports.bidiNetworkHeadersFromCdpNetworkHeadersEntries = bidiNetworkHeadersFromCdpNetworkHeadersEntries;
|
||||
exports.cdpNetworkHeadersFromBidiNetworkHeaders = cdpNetworkHeadersFromBidiNetworkHeaders;
|
||||
exports.bidiNetworkHeadersFromCdpFetchHeaders = bidiNetworkHeadersFromCdpFetchHeaders;
|
||||
exports.cdpFetchHeadersFromBidiNetworkHeaders = cdpFetchHeadersFromBidiNetworkHeaders;
|
||||
exports.networkHeaderFromCookieHeaders = networkHeaderFromCookieHeaders;
|
||||
exports.cdpAuthChallengeResponseFromBidiAuthContinueWithAuthAction = cdpAuthChallengeResponseFromBidiAuthContinueWithAuthAction;
|
||||
exports.cdpToBiDiCookie = cdpToBiDiCookie;
|
||||
exports.deserializeByteValue = deserializeByteValue;
|
||||
exports.bidiToCdpCookie = bidiToCdpCookie;
|
||||
exports.sameSiteBiDiToCdp = sameSiteBiDiToCdp;
|
||||
exports.isSpecialScheme = isSpecialScheme;
|
||||
exports.matchUrlPattern = matchUrlPattern;
|
||||
exports.bidiBodySizeFromCdpPostDataEntries = bidiBodySizeFromCdpPostDataEntries;
|
||||
exports.getTiming = getTiming;
|
||||
const ErrorResponse_js_1 = require("../../../protocol/ErrorResponse.js");
|
||||
const base64_js_1 = require("../../../utils/base64.js");
|
||||
function computeHeadersSize(headers) {
|
||||
const requestHeaders = headers.reduce((acc, header) => {
|
||||
return `${acc}${header.name}: ${header.value.value}\r\n`;
|
||||
}, '');
|
||||
return new TextEncoder().encode(requestHeaders).length;
|
||||
}
|
||||
function stringToBase64(str) {
|
||||
return typedArrayToBase64(new TextEncoder().encode(str));
|
||||
}
|
||||
function typedArrayToBase64(typedArray) {
|
||||
// chunkSize should be less V8 limit on number of arguments!
|
||||
// https://github.com/v8/v8/blob/d3de848bea727518aee94dd2fd42ba0b62037a27/src/objects/code.h#L444
|
||||
const chunkSize = 65534;
|
||||
const chunks = [];
|
||||
for (let i = 0; i < typedArray.length; i += chunkSize) {
|
||||
const chunk = typedArray.subarray(i, i + chunkSize);
|
||||
chunks.push(String.fromCodePoint.apply(null, chunk));
|
||||
}
|
||||
const binaryString = chunks.join('');
|
||||
return btoa(binaryString);
|
||||
}
|
||||
/** 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,
|
||||
},
|
||||
}));
|
||||
}
|
||||
/** Converts from CDP Fetch domain headers to BiDi network headers. */
|
||||
function bidiNetworkHeadersFromCdpNetworkHeadersEntries(headers) {
|
||||
if (!headers) {
|
||||
return [];
|
||||
}
|
||||
return headers.map(({ name, value }) => ({
|
||||
name,
|
||||
value: {
|
||||
type: 'string',
|
||||
value,
|
||||
},
|
||||
}));
|
||||
}
|
||||
/** 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;
|
||||
}, {});
|
||||
}
|
||||
/** 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,
|
||||
},
|
||||
}));
|
||||
}
|
||||
/** 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,
|
||||
}));
|
||||
}
|
||||
function networkHeaderFromCookieHeaders(headers) {
|
||||
if (headers === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
const value = headers.reduce((acc, value, index) => {
|
||||
if (index > 0) {
|
||||
acc += ';';
|
||||
}
|
||||
const cookieValue = value.value.type === 'base64'
|
||||
? btoa(value.value.value)
|
||||
: value.value.value;
|
||||
acc += `${value.name}=${cookieValue}`;
|
||||
return acc;
|
||||
}, '');
|
||||
return {
|
||||
name: 'Cookie',
|
||||
value: {
|
||||
type: 'string',
|
||||
value,
|
||||
},
|
||||
};
|
||||
}
|
||||
/** 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';
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 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: Math.round(cookie.expires) } : undefined),
|
||||
};
|
||||
// Extending with CDP-specific properties with `goog:` prefix.
|
||||
result[`goog:session`] = cookie.session;
|
||||
result[`goog:priority`] = cookie.priority;
|
||||
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;
|
||||
}
|
||||
/**
|
||||
* Decodes a byte value to a string.
|
||||
* @param {Network.BytesValue} value
|
||||
* @return {string}
|
||||
*/
|
||||
function deserializeByteValue(value) {
|
||||
if (value.type === 'base64') {
|
||||
return (0, base64_js_1.base64ToString)(value.value);
|
||||
}
|
||||
return value.value;
|
||||
}
|
||||
/**
|
||||
* 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) {
|
||||
const deserializedValue = deserializeByteValue(params.cookie.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,
|
||||
...(partitionKey.sourceOrigin !== undefined && {
|
||||
partitionKey: {
|
||||
hasCrossSiteAncestor: false,
|
||||
// CDP's `partitionKey.topLevelSite` is the BiDi's `partition.sourceOrigin`.
|
||||
topLevelSite: 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:sourceScheme`] !== undefined) {
|
||||
result.sourceScheme = params.cookie[`goog:sourceScheme`];
|
||||
}
|
||||
if (params.cookie[`goog:sourcePort`] !== undefined) {
|
||||
result.sourcePort = params.cookie[`goog:sourcePort`];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
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 "none" /* Network.SameSite.None */:
|
||||
return 'None';
|
||||
case "strict" /* Network.SameSite.Strict */:
|
||||
return 'Strict';
|
||||
// Defaults to `Lax`:
|
||||
// https://web.dev/articles/samesite-cookies-explained#samesitelax_by_default
|
||||
case "default" /* Network.SameSite.Default */:
|
||||
case "lax" /* Network.SameSite.Lax */:
|
||||
return 'Lax';
|
||||
}
|
||||
throw new ErrorResponse_js_1.InvalidArgumentException(`Unknown 'sameSite' value ${sameSite}`);
|
||||
}
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
function isSpecialScheme(protocol) {
|
||||
return ['ftp', 'file', 'http', 'https', 'ws', 'wss'].includes(protocol.replace(/:$/, ''));
|
||||
}
|
||||
function getScheme(url) {
|
||||
return url.protocol.replace(/:$/, '');
|
||||
}
|
||||
/** Matches the given URLPattern against the given URL. */
|
||||
function matchUrlPattern(pattern, url) {
|
||||
// Roughly https://w3c.github.io/webdriver-bidi/#match-url-pattern
|
||||
// plus some differences based on the URL parsing methods.
|
||||
const parsedUrl = new URL(url);
|
||||
if (pattern.protocol !== undefined &&
|
||||
pattern.protocol !== getScheme(parsedUrl)) {
|
||||
return false;
|
||||
}
|
||||
if (pattern.hostname !== undefined &&
|
||||
pattern.hostname !== parsedUrl.hostname) {
|
||||
return false;
|
||||
}
|
||||
if (pattern.port !== undefined && pattern.port !== parsedUrl.port) {
|
||||
return false;
|
||||
}
|
||||
if (pattern.pathname !== undefined &&
|
||||
pattern.pathname !== parsedUrl.pathname) {
|
||||
return false;
|
||||
}
|
||||
if (pattern.search !== undefined && pattern.search !== parsedUrl.search) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function bidiBodySizeFromCdpPostDataEntries(entries) {
|
||||
let size = 0;
|
||||
for (const entry of entries) {
|
||||
size += atob(entry.bytes ?? '').length;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
function getTiming(timing, offset = 0) {
|
||||
if (!timing) {
|
||||
return 0;
|
||||
}
|
||||
if (timing <= 0 || timing + offset <= 0) {
|
||||
return 0;
|
||||
}
|
||||
return timing + offset;
|
||||
}
|
||||
//# sourceMappingURL=NetworkUtils.js.map
|
||||
1
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/network/NetworkUtils.js.map
generated
vendored
Normal file
1
node_modules/chromium-bidi/lib/cjs/bidiMapper/modules/network/NetworkUtils.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user