chore: split a cycle in BiDi (#11229)

This commit is contained in:
Alex Rudenko 2023-10-23 12:27:23 +02:00 committed by GitHub
parent 525f13cd18
commit 20eb38d61b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 144 additions and 113 deletions

View File

@ -0,0 +1,133 @@
/**
* Copyright 2023 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import type * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js';
import {debugError} from '../common/util.js';
/**
* @internal
*/
class UnsupportedTypeError extends Error {}
/**
* @internal
*/
export class BidiDeserializer {
static deserializeNumber(value: Bidi.Script.SpecialNumber | number): number {
switch (value) {
case '-0':
return -0;
case 'NaN':
return NaN;
case 'Infinity':
return Infinity;
case '-Infinity':
return -Infinity;
default:
return value;
}
}
static deserializeLocalValue(result: Bidi.Script.RemoteValue): unknown {
switch (result.type) {
case 'array':
if (result.value) {
return result.value.map(value => {
return BidiDeserializer.deserializeLocalValue(value);
});
}
break;
case 'set':
if (result.value) {
return result.value.reduce((acc: Set<unknown>, value) => {
return acc.add(BidiDeserializer.deserializeLocalValue(value));
}, new Set());
}
break;
case 'object':
if (result.value) {
return result.value.reduce((acc: Record<any, unknown>, tuple) => {
const {key, value} = BidiDeserializer.deserializeTuple(tuple);
acc[key as any] = value;
return acc;
}, {});
}
break;
case 'map':
if (result.value) {
return result.value?.reduce((acc: Map<unknown, unknown>, tuple) => {
const {key, value} = BidiDeserializer.deserializeTuple(tuple);
return acc.set(key, value);
}, new Map());
}
break;
case 'promise':
return {};
case 'regexp':
return new RegExp(result.value.pattern, result.value.flags);
case 'date':
return new Date(result.value);
case 'undefined':
return undefined;
case 'null':
return null;
case 'number':
return BidiDeserializer.deserializeNumber(result.value);
case 'bigint':
return BigInt(result.value);
case 'boolean':
return Boolean(result.value);
case 'string':
return result.value;
}
throw new UnsupportedTypeError(
`Deserialization of type ${result.type} not supported.`
);
}
static deserializeTuple([serializedKey, serializedValue]: [
Bidi.Script.RemoteValue | string,
Bidi.Script.RemoteValue,
]): {key: unknown; value: unknown} {
const key =
typeof serializedKey === 'string'
? serializedKey
: BidiDeserializer.deserializeLocalValue(serializedKey);
const value = BidiDeserializer.deserializeLocalValue(serializedValue);
return {key, value};
}
static deserialize(result: Bidi.Script.RemoteValue): any {
if (!result) {
debugError('Service did not produce a result.');
return undefined;
}
try {
return BidiDeserializer.deserializeLocalValue(result);
} catch (error) {
if (error instanceof UnsupportedTypeError) {
debugError(error.message);
return undefined;
}
throw error;
}
}
}

View File

@ -23,6 +23,7 @@ import {Deferred} from '../util/Deferred.js';
import {interpolateFunction, stringifyFunction} from '../util/Function.js'; import {interpolateFunction, stringifyFunction} from '../util/Function.js';
import type {BidiConnection} from './Connection.js'; import type {BidiConnection} from './Connection.js';
import {BidiDeserializer} from './Deserializer.js';
import type {BidiFrame} from './Frame.js'; import type {BidiFrame} from './Frame.js';
import {BidiSerializer} from './Serializer.js'; import {BidiSerializer} from './Serializer.js';
@ -142,7 +143,7 @@ export class ExposeableFunction<Args extends unknown[], Ret> {
const args = remoteValue.value?.[1]; const args = remoteValue.value?.[1];
assert(args); assert(args);
try { try {
const result = await this.#apply(...BidiSerializer.deserialize(args)); const result = await this.#apply(...BidiDeserializer.deserialize(args));
await connection.send('script.callFunction', { await connection.send('script.callFunction', {
functionDeclaration: stringifyFunction(([_, resolve]: any, result) => { functionDeclaration: stringifyFunction(([_, resolve]: any, result) => {
resolve(result); resolve(result);

View File

@ -19,9 +19,9 @@ import type * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js';
import type {ElementHandle} from '../api/ElementHandle.js'; import type {ElementHandle} from '../api/ElementHandle.js';
import {JSHandle} from '../api/JSHandle.js'; import {JSHandle} from '../api/JSHandle.js';
import {BidiDeserializer} from './Deserializer.js';
import type {BidiRealm} from './Realm.js'; import type {BidiRealm} from './Realm.js';
import type {Sandbox} from './Sandbox.js'; import type {Sandbox} from './Sandbox.js';
import {BidiSerializer} from './Serializer.js';
import {releaseReference} from './util.js'; import {releaseReference} from './util.js';
/** /**
@ -90,7 +90,7 @@ export class BidiJSHandle<T = unknown> extends JSHandle<T> {
override toString(): string { override toString(): string {
if (this.isPrimitiveValue) { if (this.isPrimitiveValue) {
return 'JSHandle:' + BidiSerializer.deserialize(this.#remoteValue); return 'JSHandle:' + BidiDeserializer.deserialize(this.#remoteValue);
} }
return 'JSHandle@' + this.#remoteValue.type; return 'JSHandle@' + this.#remoteValue.type;

View File

@ -70,6 +70,7 @@ import {
type BrowsingContext, type BrowsingContext,
} from './BrowsingContext.js'; } from './BrowsingContext.js';
import type {BidiConnection} from './Connection.js'; import type {BidiConnection} from './Connection.js';
import {BidiDeserializer} from './Deserializer.js';
import {BidiDialog} from './Dialog.js'; import {BidiDialog} from './Dialog.js';
import {BidiElementHandle} from './ElementHandle.js'; import {BidiElementHandle} from './ElementHandle.js';
import {EmulationManager} from './EmulationManager.js'; import {EmulationManager} from './EmulationManager.js';
@ -80,7 +81,6 @@ import {BidiKeyboard, BidiMouse, BidiTouchscreen} from './Input.js';
import type {BidiJSHandle} from './JSHandle.js'; import type {BidiJSHandle} from './JSHandle.js';
import {BidiNetworkManager} from './NetworkManager.js'; import {BidiNetworkManager} from './NetworkManager.js';
import {createBidiHandle} from './Realm.js'; import {createBidiHandle} from './Realm.js';
import {BidiSerializer} from './Serializer.js';
/** /**
* @internal * @internal
@ -399,7 +399,7 @@ export class BidiPage extends Page {
const text = args const text = args
.reduce((value, arg) => { .reduce((value, arg) => {
const parsedValue = arg.isPrimitiveValue const parsedValue = arg.isPrimitiveValue
? BidiSerializer.deserialize(arg.remoteValue()) ? BidiDeserializer.deserialize(arg.remoteValue())
: arg.toString(); : arg.toString();
return `${value} ${parsedValue}`; return `${value} ${parsedValue}`;
}, '') }, '')

View File

@ -15,6 +15,7 @@ import {disposeSymbol} from '../util/disposable.js';
import {stringifyFunction} from '../util/Function.js'; import {stringifyFunction} from '../util/Function.js';
import type {BidiConnection} from './Connection.js'; import type {BidiConnection} from './Connection.js';
import {BidiDeserializer} from './Deserializer.js';
import {BidiElementHandle} from './ElementHandle.js'; import {BidiElementHandle} from './ElementHandle.js';
import {BidiJSHandle} from './JSHandle.js'; import {BidiJSHandle} from './JSHandle.js';
import type {Sandbox} from './Sandbox.js'; import type {Sandbox} from './Sandbox.js';
@ -195,7 +196,7 @@ export class BidiRealm extends EventEmitter<Record<EventType, any>> {
} }
return returnByValue return returnByValue
? BidiSerializer.deserialize(result.result) ? BidiDeserializer.deserialize(result.result)
: createBidiHandle(sandbox, result.result); : createBidiHandle(sandbox, result.result);
} }

View File

@ -17,7 +17,7 @@
import type * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js'; import type * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js';
import {LazyArg} from '../common/LazyArg.js'; import {LazyArg} from '../common/LazyArg.js';
import {debugError, isDate, isPlainObject, isRegExp} from '../common/util.js'; import {isDate, isPlainObject, isRegExp} from '../common/util.js';
import {BidiElementHandle} from './ElementHandle.js'; import {BidiElementHandle} from './ElementHandle.js';
import {BidiJSHandle} from './JSHandle.js'; import {BidiJSHandle} from './JSHandle.js';
@ -172,108 +172,4 @@ export class BidiSerializer {
return BidiSerializer.serializeRemoteValue(arg); return BidiSerializer.serializeRemoteValue(arg);
} }
static deserializeNumber(value: Bidi.Script.SpecialNumber | number): number {
switch (value) {
case '-0':
return -0;
case 'NaN':
return NaN;
case 'Infinity':
return Infinity;
case '-Infinity':
return -Infinity;
default:
return value;
}
}
static deserializeLocalValue(result: Bidi.Script.RemoteValue): unknown {
switch (result.type) {
case 'array':
if (result.value) {
return result.value.map(value => {
return BidiSerializer.deserializeLocalValue(value);
});
}
break;
case 'set':
if (result.value) {
return result.value.reduce((acc: Set<unknown>, value) => {
return acc.add(BidiSerializer.deserializeLocalValue(value));
}, new Set());
}
break;
case 'object':
if (result.value) {
return result.value.reduce((acc: Record<any, unknown>, tuple) => {
const {key, value} = BidiSerializer.deserializeTuple(tuple);
acc[key as any] = value;
return acc;
}, {});
}
break;
case 'map':
if (result.value) {
return result.value?.reduce((acc: Map<unknown, unknown>, tuple) => {
const {key, value} = BidiSerializer.deserializeTuple(tuple);
return acc.set(key, value);
}, new Map());
}
break;
case 'promise':
return {};
case 'regexp':
return new RegExp(result.value.pattern, result.value.flags);
case 'date':
return new Date(result.value);
case 'undefined':
return undefined;
case 'null':
return null;
case 'number':
return BidiSerializer.deserializeNumber(result.value);
case 'bigint':
return BigInt(result.value);
case 'boolean':
return Boolean(result.value);
case 'string':
return result.value;
}
throw new UnserializableError(
`Deserialization of type ${result.type} not supported.`
);
}
static deserializeTuple([serializedKey, serializedValue]: [
Bidi.Script.RemoteValue | string,
Bidi.Script.RemoteValue,
]): {key: unknown; value: unknown} {
const key =
typeof serializedKey === 'string'
? serializedKey
: BidiSerializer.deserializeLocalValue(serializedKey);
const value = BidiSerializer.deserializeLocalValue(serializedValue);
return {key, value};
}
static deserialize(result: Bidi.Script.RemoteValue): any {
if (!result) {
debugError('Service did not produce a result.');
return undefined;
}
try {
return BidiSerializer.deserializeLocalValue(result);
} catch (error) {
if (error instanceof UnserializableError) {
debugError(error.message);
return undefined;
}
throw error;
}
}
} }

View File

@ -18,8 +18,8 @@ import type * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js';
import {PuppeteerURL, debugError} from '../common/util.js'; import {PuppeteerURL, debugError} from '../common/util.js';
import {BidiDeserializer} from './Deserializer.js';
import type {BidiRealm} from './Realm.js'; import type {BidiRealm} from './Realm.js';
import {BidiSerializer} from './Serializer.js';
/** /**
* @internal * @internal
@ -50,7 +50,7 @@ export function createEvaluationError(
details: Bidi.Script.ExceptionDetails details: Bidi.Script.ExceptionDetails
): unknown { ): unknown {
if (details.exception.type !== 'error') { if (details.exception.type !== 'error') {
return BidiSerializer.deserialize(details.exception); return BidiDeserializer.deserialize(details.exception);
} }
const [name = '', ...parts] = details.text.split(': '); const [name = '', ...parts] = details.text.split(': ');
const message = parts.join(': '); const message = parts.join(': ');