diff --git a/src/common/utilityScriptSerializers.ts b/src/common/utilityScriptSerializers.ts index 31d98c8477..6c81cce3ff 100644 --- a/src/common/utilityScriptSerializers.ts +++ b/src/common/utilityScriptSerializers.ts @@ -18,9 +18,9 @@ export type SerializedValue = undefined | boolean | number | string | { v: 'null' | 'undefined' | 'NaN' | 'Infinity' | '-Infinity' | '-0' } | { d: string } | - { r: [string, string] } | + { r: { p: string, f: string} } | { a: SerializedValue[] } | - { o: { [key: string]: SerializedValue } } | + { o: { k: string, v: SerializedValue }[] } | { h: number }; function isRegExp(obj: any): obj is RegExp { @@ -36,9 +36,9 @@ function isError(obj: any): obj is Error { } export function parseEvaluationResultValue(value: SerializedValue, handles: any[] = []): any { - if (value === undefined) + if (Object.is(value, undefined)) return undefined; - if (typeof value === 'object') { + if (typeof value === 'object' && value) { if ('v' in value) { if (value.v === 'undefined') return undefined; @@ -52,17 +52,18 @@ export function parseEvaluationResultValue(value: SerializedValue, handles: any[ return -Infinity; if (value.v === '-0') return -0; + return undefined; } if ('d' in value) return new Date(value.d); if ('r' in value) - return new RegExp(value.r[0], value.r[1]); + return new RegExp(value.r.p, value.r.f); if ('a' in value) return value.a.map((a: any) => parseEvaluationResultValue(a, handles)); if ('o' in value) { const result: any = {}; - for (const name of Object.keys(value.o)) - result[name] = parseEvaluationResultValue(value.o[name], handles); + for (const { k, v } of value.o) + result[k] = parseEvaluationResultValue(v, handles); return result; } if ('h' in value) @@ -72,12 +73,12 @@ export function parseEvaluationResultValue(value: SerializedValue, handles: any[ } export type HandleOrValue = { h: number } | { fallThrough: any }; -export function serializeAsCallArgument(value: any, jsHandleSerializer: (value: any) => HandleOrValue): SerializedValue { - return serialize(value, jsHandleSerializer, new Set()); +export function serializeAsCallArgument(value: any, handleSerializer: (value: any) => HandleOrValue): SerializedValue { + return serialize(value, handleSerializer, new Set()); } -function serialize(value: any, jsHandleSerializer: (value: any) => HandleOrValue, visited: Set): SerializedValue { - const result = jsHandleSerializer(value); +function serialize(value: any, handleSerializer: (value: any) => HandleOrValue, visited: Set): SerializedValue { + const result = handleSerializer(value); if ('fallThrough' in result) value = result.fallThrough; else @@ -111,26 +112,26 @@ function serialize(value: any, jsHandleSerializer: (value: any) => HandleOrValue const error = value; if ('captureStackTrace' in global.Error) { // v8 - return error.stack; + return error.stack || ''; } return `${error.name}: ${error.message}\n${error.stack}`; } if (isDate(value)) return { d: value.toJSON() }; if (isRegExp(value)) - return { r: [ value.source, value.flags ] }; + return { r: { p: value.source, f: value.flags } }; if (Array.isArray(value)) { - const result = []; + const a = []; visited.add(value); for (let i = 0; i < value.length; ++i) - result.push(serialize(value[i], jsHandleSerializer, visited)); + a.push(serialize(value[i], handleSerializer, visited)); visited.delete(value); - return { a: result }; + return { a }; } if (typeof value === 'object') { - const result: any = {}; + const o: { k: string, v: SerializedValue }[] = []; visited.add(value); for (const name of Object.keys(value)) { let item; @@ -140,11 +141,11 @@ function serialize(value: any, jsHandleSerializer: (value: any) => HandleOrValue continue; // native bindings will throw sometimes } if (name === 'toJSON' && typeof item === 'function') - result[name] = {}; + o.push({ k: name, v: { o: [] } }); else - result[name] = serialize(item, jsHandleSerializer, visited); + o.push({ k: name, v: serialize(item, handleSerializer, visited) }); } visited.delete(value); - return { o: result }; + return { o }; } } diff --git a/src/injected/utilityScript.ts b/src/injected/utilityScript.ts index eae4d33ac0..d04951ea27 100644 --- a/src/injected/utilityScript.ts +++ b/src/injected/utilityScript.ts @@ -22,10 +22,10 @@ export default class UtilityScript { return returnByValue ? this._promiseAwareJsonValueNoThrow(result) : result; } - callFunction(returnByValue: boolean, functionText: string, ...args: any[]) { - const argCount = args[0] as number; - const handles = args.slice(argCount + 1); - const parameters = args.slice(1, argCount + 1).map(a => parseEvaluationResultValue(a, handles)); + callFunction(returnByValue: boolean, functionText: string, argCount: number, ...argsAndHandles: any[]) { + const args = argsAndHandles.slice(0, argCount); + const handles = argsAndHandles.slice(argCount); + const parameters = args.map(a => parseEvaluationResultValue(a, handles)); const func = global.eval('(' + functionText + ')'); const result = func(...parameters); return returnByValue ? this._promiseAwareJsonValueNoThrow(result) : result; diff --git a/src/rpc/channels.ts b/src/rpc/channels.ts index b1e9951973..362843f321 100644 --- a/src/rpc/channels.ts +++ b/src/rpc/channels.ts @@ -367,8 +367,7 @@ export interface BindingCallChannel extends Channel { export type BindingCallInitializer = { frame: FrameChannel, name: string, - // TODO: migrate this to SerializedArgument. - args: any[] + args: SerializedValue[], }; diff --git a/src/rpc/client/page.ts b/src/rpc/client/page.ts index e49d7e4d6f..16c223d971 100644 --- a/src/rpc/client/page.ts +++ b/src/rpc/client/page.ts @@ -32,7 +32,7 @@ import { ElementHandle } from './elementHandle'; import { Worker } from './worker'; import { Frame, FunctionWithSource, GotoOptions } from './frame'; import { Keyboard, Mouse } from './input'; -import { Func1, FuncOn, SmartHandle, serializeArgument } from './jsHandle'; +import { Func1, FuncOn, SmartHandle, serializeArgument, parseResult } from './jsHandle'; import { Request, Response, Route, RouteHandler } from './network'; import { FileChooser } from './fileChooser'; import { Buffer } from 'buffer'; @@ -557,7 +557,7 @@ export class BindingCall extends ChannelOwner { const handle = this._object._nodeElectronHandle!; - return { value: await handle._evaluateExpression(params.expression, params.isFunction, true /* returnByValue */, parseArgument(params.arg)) }; + return { value: serializeResult(await handle._evaluateExpression(params.expression, params.isFunction, true /* returnByValue */, parseArgument(params.arg))) }; } async evaluateExpressionHandle(params: { expression: string, isFunction: boolean, arg: SerializedArgument }): Promise<{ handle: JSHandleChannel }> { diff --git a/src/rpc/server/pageDispatcher.ts b/src/rpc/server/pageDispatcher.ts index ce31b34b74..19e3de6089 100644 --- a/src/rpc/server/pageDispatcher.ts +++ b/src/rpc/server/pageDispatcher.ts @@ -251,7 +251,7 @@ export class BindingCallDispatcher extends Dispatcher<{}, BindingCallInitializer super(scope, {}, 'bindingCall', { frame: lookupDispatcher(source.frame), name, - args + args: args.map(serializeResult), }); this._promise = new Promise((resolve, reject) => { this._resolve = resolve; diff --git a/test/evaluation.jest.js b/test/evaluation.jest.js index f4787f50fb..ca2417f6b9 100644 --- a/test/evaluation.jest.js +++ b/test/evaluation.jest.js @@ -17,7 +17,7 @@ const utils = require('./utils'); const path = require('path'); -const {FFOX, CHROMIUM, WEBKIT, USES_HOOKS, CHANNEL} = testOptions; +const {FFOX, CHROMIUM, WEBKIT, USES_HOOKS} = testOptions; describe('Page.evaluate', function() { it('should work', async({page, server}) => { @@ -373,7 +373,8 @@ describe('Page.evaluate', function() { }); expect(result).toBe(undefined); }); - it.skip(CHANNEL)('should transfer 100Mb of data from page to node.js', async({page}) => { + it.fail(USES_HOOKS)('should transfer 100Mb of data from page to node.js', async({page}) => { + // This does not use hooks, but is slow in wire channel. const a = await page.evaluate(() => Array(100 * 1024 * 1024 + 1).join('a')); expect(a.length).toBe(100 * 1024 * 1024); });