From ac7a174a9d8016695ddfabb4224551ee53a38a4d Mon Sep 17 00:00:00 2001 From: Alex Rudenko Date: Mon, 23 Oct 2023 11:39:33 +0200 Subject: [PATCH] chore: split a cycle in BiDi --- .../puppeteer-core/src/bidi/Deserializer.ts | 133 ++++++++++++++++++ .../src/bidi/ExposedFunction.ts | 3 +- packages/puppeteer-core/src/bidi/JSHandle.ts | 4 +- packages/puppeteer-core/src/bidi/Page.ts | 4 +- packages/puppeteer-core/src/bidi/Realm.ts | 3 +- .../puppeteer-core/src/bidi/Serializer.ts | 106 +------------- packages/puppeteer-core/src/bidi/util.ts | 4 +- 7 files changed, 144 insertions(+), 113 deletions(-) create mode 100644 packages/puppeteer-core/src/bidi/Deserializer.ts diff --git a/packages/puppeteer-core/src/bidi/Deserializer.ts b/packages/puppeteer-core/src/bidi/Deserializer.ts new file mode 100644 index 0000000000000..7f2c4c0474e45 --- /dev/null +++ b/packages/puppeteer-core/src/bidi/Deserializer.ts @@ -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, value) => { + return acc.add(BidiDeserializer.deserializeLocalValue(value)); + }, new Set()); + } + break; + case 'object': + if (result.value) { + return result.value.reduce((acc: Record, 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, 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; + } + } +} diff --git a/packages/puppeteer-core/src/bidi/ExposedFunction.ts b/packages/puppeteer-core/src/bidi/ExposedFunction.ts index 7e81f3ee81707..396cc46ea2235 100644 --- a/packages/puppeteer-core/src/bidi/ExposedFunction.ts +++ b/packages/puppeteer-core/src/bidi/ExposedFunction.ts @@ -23,6 +23,7 @@ import {Deferred} from '../util/Deferred.js'; import {interpolateFunction, stringifyFunction} from '../util/Function.js'; import type {BidiConnection} from './Connection.js'; +import {BidiDeserializer} from './Deserializer.js'; import type {BidiFrame} from './Frame.js'; import {BidiSerializer} from './Serializer.js'; @@ -142,7 +143,7 @@ export class ExposeableFunction { const args = remoteValue.value?.[1]; assert(args); try { - const result = await this.#apply(...BidiSerializer.deserialize(args)); + const result = await this.#apply(...BidiDeserializer.deserialize(args)); await connection.send('script.callFunction', { functionDeclaration: stringifyFunction(([_, resolve]: any, result) => { resolve(result); diff --git a/packages/puppeteer-core/src/bidi/JSHandle.ts b/packages/puppeteer-core/src/bidi/JSHandle.ts index 4dd9030525f73..9ce4154442785 100644 --- a/packages/puppeteer-core/src/bidi/JSHandle.ts +++ b/packages/puppeteer-core/src/bidi/JSHandle.ts @@ -19,9 +19,9 @@ import type * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js'; import type {ElementHandle} from '../api/ElementHandle.js'; import {JSHandle} from '../api/JSHandle.js'; +import {BidiDeserializer} from './Deserializer.js'; import type {BidiRealm} from './Realm.js'; import type {Sandbox} from './Sandbox.js'; -import {BidiSerializer} from './Serializer.js'; import {releaseReference} from './util.js'; /** @@ -90,7 +90,7 @@ export class BidiJSHandle extends JSHandle { override toString(): string { if (this.isPrimitiveValue) { - return 'JSHandle:' + BidiSerializer.deserialize(this.#remoteValue); + return 'JSHandle:' + BidiDeserializer.deserialize(this.#remoteValue); } return 'JSHandle@' + this.#remoteValue.type; diff --git a/packages/puppeteer-core/src/bidi/Page.ts b/packages/puppeteer-core/src/bidi/Page.ts index fb2321945e35e..8bf1e4f265798 100644 --- a/packages/puppeteer-core/src/bidi/Page.ts +++ b/packages/puppeteer-core/src/bidi/Page.ts @@ -70,6 +70,7 @@ import { type BrowsingContext, } from './BrowsingContext.js'; import type {BidiConnection} from './Connection.js'; +import {BidiDeserializer} from './Deserializer.js'; import {BidiDialog} from './Dialog.js'; import {BidiElementHandle} from './ElementHandle.js'; import {EmulationManager} from './EmulationManager.js'; @@ -80,7 +81,6 @@ import {BidiKeyboard, BidiMouse, BidiTouchscreen} from './Input.js'; import type {BidiJSHandle} from './JSHandle.js'; import {BidiNetworkManager} from './NetworkManager.js'; import {createBidiHandle} from './Realm.js'; -import {BidiSerializer} from './Serializer.js'; /** * @internal @@ -399,7 +399,7 @@ export class BidiPage extends Page { const text = args .reduce((value, arg) => { const parsedValue = arg.isPrimitiveValue - ? BidiSerializer.deserialize(arg.remoteValue()) + ? BidiDeserializer.deserialize(arg.remoteValue()) : arg.toString(); return `${value} ${parsedValue}`; }, '') diff --git a/packages/puppeteer-core/src/bidi/Realm.ts b/packages/puppeteer-core/src/bidi/Realm.ts index 302ad4269b9e6..94f25efc7abbe 100644 --- a/packages/puppeteer-core/src/bidi/Realm.ts +++ b/packages/puppeteer-core/src/bidi/Realm.ts @@ -15,6 +15,7 @@ import {disposeSymbol} from '../util/disposable.js'; import {stringifyFunction} from '../util/Function.js'; import type {BidiConnection} from './Connection.js'; +import {BidiDeserializer} from './Deserializer.js'; import {BidiElementHandle} from './ElementHandle.js'; import {BidiJSHandle} from './JSHandle.js'; import type {Sandbox} from './Sandbox.js'; @@ -195,7 +196,7 @@ export class BidiRealm extends EventEmitter> { } return returnByValue - ? BidiSerializer.deserialize(result.result) + ? BidiDeserializer.deserialize(result.result) : createBidiHandle(sandbox, result.result); } diff --git a/packages/puppeteer-core/src/bidi/Serializer.ts b/packages/puppeteer-core/src/bidi/Serializer.ts index 9e7e1b6fc4395..643e8405fbc4e 100644 --- a/packages/puppeteer-core/src/bidi/Serializer.ts +++ b/packages/puppeteer-core/src/bidi/Serializer.ts @@ -17,7 +17,7 @@ import type * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.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 {BidiJSHandle} from './JSHandle.js'; @@ -172,108 +172,4 @@ export class BidiSerializer { 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, value) => { - return acc.add(BidiSerializer.deserializeLocalValue(value)); - }, new Set()); - } - break; - case 'object': - if (result.value) { - return result.value.reduce((acc: Record, 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, 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; - } - } } diff --git a/packages/puppeteer-core/src/bidi/util.ts b/packages/puppeteer-core/src/bidi/util.ts index 8eb42578bb470..be15436416b44 100644 --- a/packages/puppeteer-core/src/bidi/util.ts +++ b/packages/puppeteer-core/src/bidi/util.ts @@ -18,8 +18,8 @@ import type * as Bidi from 'chromium-bidi/lib/cjs/protocol/protocol.js'; import {PuppeteerURL, debugError} from '../common/util.js'; +import {BidiDeserializer} from './Deserializer.js'; import type {BidiRealm} from './Realm.js'; -import {BidiSerializer} from './Serializer.js'; /** * @internal @@ -50,7 +50,7 @@ export function createEvaluationError( details: Bidi.Script.ExceptionDetails ): unknown { if (details.exception.type !== 'error') { - return BidiSerializer.deserialize(details.exception); + return BidiDeserializer.deserialize(details.exception); } const [name = '', ...parts] = details.text.split(': '); const message = parts.join(': ');