feat(rpc): remove last union types from the protocol (#3059)

This commit is contained in:
Dmitry Gozman 2020-07-21 12:44:30 -07:00 committed by GitHub
parent de9570ee3a
commit 3dead4c8d1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 170 additions and 62 deletions

View file

@ -23,9 +23,10 @@ export type Binary = string;
export interface Channel extends EventEmitter {
}
export type SerializedValue = undefined | boolean | number | string | ComplexSerializedValue;
export type ComplexSerializedValue = {
export type SerializedValue = {
n?: number,
b?: boolean,
s?: string,
v?: 'null' | 'undefined' | 'NaN' | 'Infinity' | '-Infinity' | '-0',
d?: string,
r?: {
@ -863,7 +864,7 @@ export type FrameEvalOnSelectorParams = {
arg: SerializedArgument,
};
export type FrameEvalOnSelectorResult = {
value?: SerializedValue,
value: SerializedValue,
};
export type FrameEvalOnSelectorAllParams = {
selector: string,
@ -872,7 +873,7 @@ export type FrameEvalOnSelectorAllParams = {
arg: SerializedArgument,
};
export type FrameEvalOnSelectorAllResult = {
value?: SerializedValue,
value: SerializedValue,
};
export type FrameAddScriptTagParams = {
url?: string,
@ -941,7 +942,7 @@ export type FrameEvaluateExpressionParams = {
arg: SerializedArgument,
};
export type FrameEvaluateExpressionResult = {
value?: SerializedValue,
value: SerializedValue,
};
export type FrameEvaluateExpressionHandleParams = {
expression: string,
@ -1119,7 +1120,7 @@ export type WorkerEvaluateExpressionParams = {
arg: SerializedArgument,
};
export type WorkerEvaluateExpressionResult = {
value?: SerializedValue,
value: SerializedValue,
};
export type WorkerEvaluateExpressionHandleParams = {
expression: string,
@ -1154,7 +1155,7 @@ export type JSHandleEvaluateExpressionParams = {
arg: SerializedArgument,
};
export type JSHandleEvaluateExpressionResult = {
value?: SerializedValue,
value: SerializedValue,
};
export type JSHandleEvaluateExpressionHandleParams = {
expression: string,
@ -1179,7 +1180,7 @@ export type JSHandleGetPropertyResult = {
};
export type JSHandleJsonValueParams = {};
export type JSHandleJsonValueResult = {
value?: SerializedValue,
value: SerializedValue,
};
// ----------- ElementHandle -----------
@ -1219,7 +1220,7 @@ export type ElementHandleEvalOnSelectorParams = {
arg: SerializedArgument,
};
export type ElementHandleEvalOnSelectorResult = {
value?: SerializedValue,
value: SerializedValue,
};
export type ElementHandleEvalOnSelectorAllParams = {
selector: string,
@ -1228,7 +1229,7 @@ export type ElementHandleEvalOnSelectorAllParams = {
arg: SerializedArgument,
};
export type ElementHandleEvalOnSelectorAllResult = {
value?: SerializedValue,
value: SerializedValue,
};
export type ElementHandleBoundingBoxParams = {};
export type ElementHandleBoundingBoxResult = {
@ -1642,7 +1643,7 @@ export type ElectronApplicationEvaluateExpressionParams = {
arg: SerializedArgument,
};
export type ElectronApplicationEvaluateExpressionResult = {
value?: SerializedValue,
value: SerializedValue,
};
export type ElectronApplicationEvaluateExpressionHandleParams = {
expression: string,

View file

@ -17,7 +17,7 @@
import { JSHandleChannel, JSHandleInitializer, SerializedArgument, SerializedValue, Channel } from '../channels';
import { ElementHandle } from './elementHandle';
import { ChannelOwner } from './channelOwner';
import { serializeAsCallArgument, parseEvaluationResultValue } from '../../common/utilityScriptSerializers';
import { parseSerializedValue, serializeValue } from '../serializers';
type NoHandles<Arg> = Arg extends JSHandle ? never : (Arg extends object ? { [Key in keyof Arg]: NoHandles<Arg[Key]> } : Arg);
type Unboxed<Arg> =
@ -99,14 +99,14 @@ export function serializeArgument(arg: any): SerializedArgument {
handles.push(channel);
return handles.length - 1;
};
const value = serializeAsCallArgument(arg, value => {
const value = serializeValue(arg, value => {
if (value instanceof JSHandle)
return { h: pushHandle(value._channel) };
return { fallThrough: value };
});
}, new Set());
return { value, handles };
}
export function parseResult(arg: SerializedValue): any {
return parseEvaluationResultValue(arg as any, []);
export function parseResult(value: SerializedValue): any {
return parseSerializedValue(value, undefined);
}

View file

@ -12,14 +12,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
union SerializedValue
undefined
boolean
number
string
ComplexSerializedValue
type ComplexSerializedValue
# Exactly one of the optional fields must be present.
type SerializedValue
n?: number
b?: boolean
s?: string
v?: enum
null
undefined
@ -27,16 +24,21 @@ type ComplexSerializedValue
Infinity
-Infinity
-0
# String representation of the Date.
d?: string
# Regular expression pattern and flags.
r?: object
p: string
f: string
a?: SerializedValue[]
# Object with keys and values.
o?: object[]
k: string
v: SerializedValue
# An index in the handles array from SerializedArgument.
h?: number
# Represents a value with handle references.
type SerializedArgument
value: SerializedValue
handles: Channel[]
@ -756,7 +758,7 @@ interface Frame
isFunction: boolean
arg: SerializedArgument
returns
value?: SerializedValue
value: SerializedValue
command evalOnSelectorAll
parameters
@ -765,7 +767,7 @@ interface Frame
isFunction: boolean
arg: SerializedArgument
returns
value?: SerializedValue
value: SerializedValue
command addScriptTag
parameters
@ -846,7 +848,7 @@ interface Frame
isFunction: boolean
arg: SerializedArgument
returns
value?: SerializedValue
value: SerializedValue
command evaluateExpressionHandle
parameters
@ -1031,7 +1033,7 @@ interface Worker
isFunction: boolean
arg: SerializedArgument
returns
value?: SerializedValue
value: SerializedValue
command evaluateExpressionHandle
parameters
@ -1057,7 +1059,7 @@ interface JSHandle
isFunction: boolean
arg: SerializedArgument
returns
value?: SerializedValue
value: SerializedValue
command evaluateExpressionHandle
parameters
@ -1081,7 +1083,7 @@ interface JSHandle
command jsonValue
returns
value?: SerializedValue
value: SerializedValue
interface ElementHandle extends JSHandle
command evalOnSelector
@ -1091,7 +1093,7 @@ interface ElementHandle extends JSHandle
isFunction: boolean
arg: SerializedArgument
returns
value?: SerializedValue
value: SerializedValue
command evalOnSelectorAll
parameters
@ -1100,7 +1102,7 @@ interface ElementHandle extends JSHandle
isFunction: boolean
arg: SerializedArgument
returns
value?: SerializedValue
value: SerializedValue
command boundingBox
returns
@ -1458,7 +1460,7 @@ interface ElectronApplication
isFunction: boolean
arg: SerializedArgument
returns
value?: SerializedValue
value: SerializedValue
command evaluateExpressionHandle
parameters

View file

@ -21,18 +21,20 @@ import * as util from 'util';
import { TimeoutError } from '../errors';
import * as types from '../types';
import { helper, assert } from '../helper';
import { SerializedError, AXNode } from './channels';
import { serializeAsCallArgument, parseEvaluationResultValue } from '../common/utilityScriptSerializers';
import { SerializedError, AXNode, SerializedValue } from './channels';
export function serializeError(e: any): SerializedError {
if (helper.isError(e))
return { error: { message: e.message, stack: e.stack, name: e.name } };
return { value: serializeAsCallArgument(e, value => ({ fallThrough: value })) };
return { value: serializeValue(e, value => ({ fallThrough: value }), new Set()) };
}
export function parseError(error: SerializedError): Error {
if (!error.error)
return parseEvaluationResultValue(error.value as any, []);
if (!error.error) {
if (error.value === undefined)
throw new Error('Serialized error must have either an error or a value');
return parseSerializedValue(error.value, undefined);
}
if (error.error.name === 'TimeoutError') {
const e = new TimeoutError(error.error.message);
e.stack = error.error.stack || '';
@ -169,3 +171,117 @@ export function axNodeFromProtocol(axNode: AXNode): types.SerializedAXNode {
delete (result as any).valueString;
return result;
}
export function parseSerializedValue(value: SerializedValue, handles: any[] | undefined): any {
if (value.n !== undefined)
return value.n;
if (value.s !== undefined)
return value.s;
if (value.b !== undefined)
return value.b;
if (value.v !== undefined) {
if (value.v === 'undefined')
return undefined;
if (value.v === 'null')
return null;
if (value.v === 'NaN')
return NaN;
if (value.v === 'Infinity')
return Infinity;
if (value.v === '-Infinity')
return -Infinity;
if (value.v === '-0')
return -0;
}
if (value.d !== undefined)
return new Date(value.d);
if (value.r !== undefined)
return new RegExp(value.r.p, value.r.f);
if (value.a !== undefined)
return value.a.map((a: any) => parseSerializedValue(a, handles));
if (value.o !== undefined) {
const result: any = {};
for (const { k, v } of value.o)
result[k] = parseSerializedValue(v, handles);
return result;
}
if (value.h !== undefined) {
if (handles === undefined)
throw new Error('Unexpected handle');
return handles[value.h];
}
throw new Error('Unexpected value');
}
export type HandleOrValue = { h: number } | { fallThrough: any };
export function serializeValue(value: any, handleSerializer: (value: any) => HandleOrValue, visited: Set<any>): SerializedValue {
const handle = handleSerializer(value);
if ('fallThrough' in handle)
value = handle.fallThrough;
else
return handle;
if (visited.has(value))
throw new Error('Argument is a circular structure');
if (typeof value === 'symbol')
return { v: 'undefined' };
if (Object.is(value, undefined))
return { v: 'undefined' };
if (Object.is(value, null))
return { v: 'null' };
if (Object.is(value, NaN))
return { v: 'NaN' };
if (Object.is(value, Infinity))
return { v: 'Infinity' };
if (Object.is(value, -Infinity))
return { v: '-Infinity' };
if (Object.is(value, -0))
return { v: '-0' };
if (typeof value === 'boolean')
return { b: value };
if (typeof value === 'number')
return { n: value };
if (typeof value === 'string')
return { s: value };
if (isError(value)) {
const error = value;
if ('captureStackTrace' in global.Error) {
// v8
return { s: error.stack || '' };
}
return { s: `${error.name}: ${error.message}\n${error.stack}` };
}
if (isDate(value))
return { d: value.toJSON() };
if (isRegExp(value))
return { r: { p: value.source, f: value.flags } };
if (Array.isArray(value)) {
const a = [];
visited.add(value);
for (let i = 0; i < value.length; ++i)
a.push(serializeValue(value[i], handleSerializer, visited));
visited.delete(value);
return { a };
}
if (typeof value === 'object') {
const o: { k: string, v: SerializedValue }[] = [];
visited.add(value);
for (const name of Object.keys(value))
o.push({ k: name, v: serializeValue(value[name], handleSerializer, visited) });
visited.delete(value);
return { o };
}
throw new Error('Unexpected value');
}
function isRegExp(obj: any): obj is RegExp {
return obj instanceof RegExp || Object.prototype.toString.call(obj) === '[object RegExp]';
}
function isDate(obj: any): obj is Date {
return obj instanceof Date || Object.prototype.toString.call(obj) === '[object Date]';
}
function isError(obj: any): obj is Error {
return obj instanceof Error || (obj && obj.__proto__ && obj.__proto__.name === 'Error');
}

View file

@ -16,13 +16,12 @@
import { Dispatcher, DispatcherScope, lookupDispatcher } from './dispatcher';
import { Electron, ElectronApplication, ElectronEvents, ElectronPage } from '../../server/electron';
import { ElectronApplicationChannel, ElectronApplicationInitializer, PageChannel, JSHandleChannel, ElectronInitializer, ElectronChannel, SerializedArgument, ElectronLaunchParams } from '../channels';
import { ElectronApplicationChannel, ElectronApplicationInitializer, PageChannel, JSHandleChannel, ElectronInitializer, ElectronChannel, SerializedArgument, ElectronLaunchParams, SerializedValue } from '../channels';
import { BrowserContextDispatcher } from './browserContextDispatcher';
import { BrowserContextBase } from '../../browserContext';
import { PageDispatcher } from './pageDispatcher';
import { parseArgument, serializeResult } from './jsHandleDispatcher';
import { createHandle } from './elementHandlerDispatcher';
import { SerializedValue } from '../../common/utilityScriptSerializers';
import { envArrayToObject } from '../serializers';
export class ElectronDispatcher extends Dispatcher<Electron, ElectronInitializer> implements ElectronChannel {

View file

@ -17,11 +17,10 @@
import { ElementHandle } from '../../dom';
import * as js from '../../javascript';
import * as types from '../../types';
import { ElementHandleChannel, FrameChannel, Binary, SerializedArgument } from '../channels';
import { ElementHandleChannel, FrameChannel, Binary, SerializedArgument, SerializedValue } from '../channels';
import { DispatcherScope, lookupNullableDispatcher } from './dispatcher';
import { JSHandleDispatcher, serializeResult, parseArgument } from './jsHandleDispatcher';
import { FrameDispatcher } from './frameDispatcher';
import { SerializedValue } from '../../common/utilityScriptSerializers';
export function createHandle(scope: DispatcherScope, handle: js.JSHandle): JSHandleDispatcher {
return handle.asElement() ? new ElementHandleDispatcher(scope, handle.asElement()!) : new JSHandleDispatcher(scope, handle);

View file

@ -16,12 +16,11 @@
import { Frame, kAddLifecycleEvent, kRemoveLifecycleEvent, kNavigationEvent, NavigationEvent } from '../../frames';
import * as types from '../../types';
import { ElementHandleChannel, FrameChannel, FrameInitializer, JSHandleChannel, ResponseChannel, SerializedArgument, FrameWaitForFunctionParams } from '../channels';
import { ElementHandleChannel, FrameChannel, FrameInitializer, JSHandleChannel, ResponseChannel, SerializedArgument, FrameWaitForFunctionParams, SerializedValue } from '../channels';
import { Dispatcher, DispatcherScope, lookupNullableDispatcher, existingDispatcher } from './dispatcher';
import { convertSelectOptionValues, ElementHandleDispatcher, createHandle, convertInputFiles } from './elementHandlerDispatcher';
import { parseArgument, serializeResult } from './jsHandleDispatcher';
import { ResponseDispatcher, RequestDispatcher } from './networkDispatchers';
import { SerializedValue } from '../../common/utilityScriptSerializers';
export class FrameDispatcher extends Dispatcher<Frame, FrameInitializer> implements FrameChannel {
private _frame: Frame;

View file

@ -15,10 +15,10 @@
*/
import * as js from '../../javascript';
import { JSHandleChannel, JSHandleInitializer, SerializedArgument } from '../channels';
import { JSHandleChannel, JSHandleInitializer, SerializedArgument, SerializedValue } from '../channels';
import { Dispatcher, DispatcherScope } from './dispatcher';
import { parseEvaluationResultValue, serializeAsCallArgument, SerializedValue } from '../../common/utilityScriptSerializers';
import { createHandle } from './elementHandlerDispatcher';
import { parseSerializedValue, serializeValue } from '../serializers';
export class JSHandleDispatcher extends Dispatcher<js.JSHandle, JSHandleInitializer> implements JSHandleChannel {
@ -63,9 +63,9 @@ export class JSHandleDispatcher extends Dispatcher<js.JSHandle, JSHandleInitiali
// Generic channel parser converts guids to JSHandleDispatchers,
// and this function takes care of coverting them into underlying JSHandles.
export function parseArgument(arg: SerializedArgument): any {
return parseEvaluationResultValue(arg.value as any, arg.handles.map(arg => (arg as JSHandleDispatcher)._object));
return parseSerializedValue(arg.value, arg.handles.map(a => (a as JSHandleDispatcher)._object));
}
export function serializeResult(arg: any): SerializedValue {
return serializeAsCallArgument(arg, value => ({ fallThrough: value }));
return serializeValue(arg, value => ({ fallThrough: value }), new Set());
}

View file

@ -20,7 +20,7 @@ import { Frame } from '../../frames';
import { Request } from '../../network';
import { Page, Worker } from '../../page';
import * as types from '../../types';
import { BindingCallChannel, BindingCallInitializer, ElementHandleChannel, PageChannel, PageInitializer, ResponseChannel, WorkerInitializer, WorkerChannel, JSHandleChannel, Binary, SerializedArgument, PagePdfParams, SerializedError, PageAccessibilitySnapshotResult } from '../channels';
import { BindingCallChannel, BindingCallInitializer, ElementHandleChannel, PageChannel, PageInitializer, ResponseChannel, WorkerInitializer, WorkerChannel, JSHandleChannel, Binary, SerializedArgument, PagePdfParams, SerializedError, PageAccessibilitySnapshotResult, SerializedValue } from '../channels';
import { Dispatcher, DispatcherScope, lookupDispatcher, lookupNullableDispatcher } from './dispatcher';
import { parseError, serializeError, headersArrayToObject, axNodeToProtocol } from '../serializers';
import { ConsoleMessageDispatcher } from './consoleMessageDispatcher';
@ -32,7 +32,6 @@ import { serializeResult, parseArgument } from './jsHandleDispatcher';
import { ElementHandleDispatcher, createHandle } from './elementHandlerDispatcher';
import { FileChooser } from '../../fileChooser';
import { CRCoverage } from '../../chromium/crCoverage';
import { SerializedValue } from '../../common/utilityScriptSerializers';
export class PageDispatcher extends Dispatcher<Page, PageInitializer> implements PageChannel {
private _page: Page;

View file

@ -51,7 +51,8 @@ function titleCase(name) {
return name[0].toUpperCase() + name.substring(1);
}
function inlineType(type, item, indent) {
function inlineType(item, indent) {
let type = item.words[1];
const array = type.endsWith('[]');
if (array)
type = type.substring(0, type.length - 2);
@ -93,7 +94,7 @@ function properties(item, indent) {
const optional = name.endsWith('?');
if (optional)
name = name.substring(0, name.length - 1);
result.push(`${indent}${name}${optional ? '?' : ''}: ${inlineType(prop.words[1], prop, indent)},`);
result.push(`${indent}${name}${optional ? '?' : ''}: ${inlineType(prop, indent)},`);
}
return result.join('\n');
}
@ -140,15 +141,7 @@ for (const item of list) {
}
for (const item of list) {
if (item.words[0] === 'union') {
if (item.words.length !== 2)
raise(item);
result.push(`export type ${item.words[1]} = ${item.list.map(clause => {
if (clause.words.length !== 1)
raise(clause);
return inlineType(clause.words[0], clause, ' ');
}).join(' | ')};`);
} else if (item.words[0] === 'type') {
if (item.words[0] === 'type') {
if (item.words.length !== 2)
raise(item);
result.push(`export type ${item.words[1]} = {`);