chore: do not serialize buffers into base64 in local mode (#15316)

This commit is contained in:
Dmitry Gozman 2022-07-05 08:58:34 -07:00 committed by GitHub
parent ef5a56ce18
commit 4eccb89a79
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 121 additions and 117 deletions

View File

@ -166,10 +166,9 @@ export class AndroidDevice extends ChannelOwner<channels.AndroidDeviceChannel> i
async screenshot(options: { path?: string } = {}): Promise<Buffer> {
const { binary } = await this._channel.screenshot();
const buffer = Buffer.from(binary, 'base64');
if (options.path)
await fs.promises.writeFile(options.path, buffer);
return buffer;
await fs.promises.writeFile(options.path, binary);
return binary;
}
async close() {
@ -179,7 +178,7 @@ export class AndroidDevice extends ChannelOwner<channels.AndroidDeviceChannel> i
async shell(command: string): Promise<Buffer> {
const { result } = await this._channel.shell({ command });
return Buffer.from(result, 'base64');
return result;
}
async open(command: string): Promise<AndroidSocket> {
@ -222,12 +221,12 @@ export class AndroidSocket extends ChannelOwner<channels.AndroidSocketChannel> i
constructor(parent: ChannelOwner, type: string, guid: string, initializer: channels.AndroidSocketInitializer) {
super(parent, type, guid, initializer);
this._channel.on('data', ({ data }) => this.emit(Events.AndroidSocket.Data, Buffer.from(data, 'base64')));
this._channel.on('data', ({ data }) => this.emit(Events.AndroidSocket.Data, data));
this._channel.on('close', () => this.emit(Events.AndroidSocket.Close));
}
async write(data: Buffer): Promise<void> {
await this._channel.write({ data: data.toString('base64') });
await this._channel.write({ data });
}
async close(): Promise<void> {
@ -235,10 +234,10 @@ export class AndroidSocket extends ChannelOwner<channels.AndroidSocketChannel> i
}
}
async function loadFile(file: string | Buffer): Promise<string> {
async function loadFile(file: string | Buffer): Promise<Buffer> {
if (isString(file))
return fs.promises.readFile(file, { encoding: 'base64' }).toString();
return file.toString('base64');
return fs.promises.readFile(file);
return file;
}
export class AndroidInput implements api.AndroidInput {

View File

@ -99,7 +99,7 @@ export class Browser extends ChannelOwner<channels.BrowserChannel> implements ap
}
async stopTracing(): Promise<Buffer> {
return Buffer.from((await this._channel.stopTracing()).binary, 'base64');
return (await this._channel.stopTracing()).binary;
}
async close(): Promise<void> {

View File

@ -89,7 +89,7 @@ export abstract class ChannelOwner<T extends channels.Channel = channels.Channel
apiZone.reported = true;
if (csi && stackTrace && stackTrace.apiName)
csi.onApiCallBegin(renderCallWithParams(stackTrace.apiName, params), stackTrace, callCookie);
return this._connection.sendMessageToServer(this, this._type, prop, validator(params, '', { tChannelImpl: tChannelImplToWire }), stackTrace);
return this._connection.sendMessageToServer(this, this._type, prop, validator(params, '', { tChannelImpl: tChannelImplToWire, binary: this._connection.isRemote() ? 'toBase64' : 'buffer' }), stackTrace);
});
};
}

View File

@ -133,7 +133,7 @@ export class Connection extends EventEmitter {
callback.reject(parseError(error));
} else {
const validator = findValidator(callback.type, callback.method, 'Result');
callback.resolve(validator(result, '', { tChannelImpl: this._tChannelImplFromWire.bind(this) }));
callback.resolve(validator(result, '', { tChannelImpl: this._tChannelImplFromWire.bind(this), binary: this.isRemote() ? 'fromBase64' : 'buffer' }));
}
return;
}
@ -154,7 +154,7 @@ export class Connection extends EventEmitter {
if (!object)
throw new Error(`Cannot find object to emit "${method}": ${guid}`);
const validator = findValidator(object._type, method, 'Event');
(object._channel as any).emit(method, validator(params, '', { tChannelImpl: this._tChannelImplFromWire.bind(this) }));
(object._channel as any).emit(method, validator(params, '', { tChannelImpl: this._tChannelImplFromWire.bind(this), binary: this.isRemote() ? 'fromBase64' : 'buffer' }));
}
close(errorMessage: string = 'Connection closed') {
@ -181,7 +181,7 @@ export class Connection extends EventEmitter {
throw new Error(`Cannot find parent object ${parentGuid} to create ${guid}`);
let result: ChannelOwner<any>;
const validator = findValidator(type, '', 'Initializer');
initializer = validator(initializer, '', { tChannelImpl: this._tChannelImplFromWire.bind(this) });
initializer = validator(initializer, '', { tChannelImpl: this._tChannelImplFromWire.bind(this), binary: this.isRemote() ? 'fromBase64' : 'buffer' });
switch (type) {
case 'Android':
result = new Android(parent, type, guid, initializer);

View File

@ -202,12 +202,11 @@ export class ElementHandle<T extends Node = Node> extends JSHandle<T> implements
}));
}
const result = await this._elementChannel.screenshot(copy);
const buffer = Buffer.from(result.binary, 'base64');
if (options.path) {
await mkdirIfNeeded(options.path);
await fs.promises.writeFile(options.path, buffer);
await fs.promises.writeFile(options.path, result.binary);
}
return buffer;
return result.binary;
}
async $(selector: string): Promise<ElementHandle<SVGElement | HTMLElement> | null> {
@ -291,13 +290,13 @@ export async function convertInputFiles(files: string | FilePayload | string[] |
if (typeof item === 'string') {
return {
name: path.basename(item),
buffer: (await fs.promises.readFile(item)).toString('base64')
buffer: await fs.promises.readFile(item)
};
} else {
return {
name: item.name,
mimeType: item.mimeType,
buffer: item.buffer.toString('base64'),
buffer: item.buffer,
};
}
}));

View File

@ -189,13 +189,12 @@ export class APIRequestContext extends ChannelOwner<channels.APIRequestContextCh
}
if (postDataBuffer === undefined && jsonData === undefined && formData === undefined && multipartData === undefined)
postDataBuffer = request?.postDataBuffer() || undefined;
const postData = (postDataBuffer ? postDataBuffer.toString('base64') : undefined);
const result = await this._channel.fetch({
url,
params,
method,
headers,
postData,
postData: postDataBuffer,
jsonData,
formData,
multipartData,
@ -257,7 +256,7 @@ export class APIResponse implements api.APIResponse {
const result = await this._request._channel.fetchResponseBody({ fetchUid: this._fetchUid() });
if (result.binary === undefined)
throw new Error('Response has been disposed');
return Buffer.from(result.binary!, 'base64');
return result.binary;
} catch (e) {
if (e.message.includes(kBrowserOrContextClosedError))
throw new Error('Response has been disposed');
@ -300,7 +299,7 @@ function filePayloadToJson(payload: FilePayload): ServerFilePayload {
return {
name: payload.name,
mimeType: payload.mimeType,
buffer: payload.buffer.toString('base64'),
buffer: payload.buffer,
};
}
@ -314,7 +313,7 @@ async function readStreamToJson(stream: fs.ReadStream): Promise<ServerFilePayloa
const streamPath: string = Buffer.isBuffer(stream.path) ? stream.path.toString('utf8') : stream.path;
return {
name: path.basename(streamPath),
buffer: buffer.toString('base64'),
buffer,
};
}

View File

@ -52,7 +52,7 @@ export class HarRouter {
url: request.url(),
method: request.method(),
headers: (await request.headersArray()),
postData: request.postDataBuffer()?.toString('base64'),
postData: request.postDataBuffer() || undefined,
isNavigationRequest: request.isNavigationRequest()
});
@ -66,7 +66,7 @@ export class HarRouter {
await route.fulfill({
status: response.status,
headers: Object.fromEntries(response.headers!.map(h => [h.name, h.value])),
body: Buffer.from(response.body!, 'base64')
body: response.body!
});
return;
}

View File

@ -87,7 +87,7 @@ export class Request extends ChannelOwner<channels.RequestChannel> implements ap
if (this._redirectedFrom)
this._redirectedFrom._redirectedTo = this;
this._provisionalHeaders = new RawHeaders(initializer.headers);
this._postData = initializer.postData !== undefined ? Buffer.from(initializer.postData, 'base64') : null;
this._postData = initializer.postData ?? null;
this._timing = {
startTime: 0,
domainLookupStart: -1,
@ -385,7 +385,7 @@ export class Route extends ChannelOwner<channels.RouteChannel> implements api.Ro
url: options.url,
method: options.method,
headers: options.headers ? headersObjectToArray(options.headers) : undefined,
postData: postDataBuffer ? postDataBuffer.toString('base64') : undefined,
postData: postDataBuffer,
}));
}, !!internal);
}
@ -491,7 +491,7 @@ export class Response extends ChannelOwner<channels.ResponseChannel> implements
}
async body(): Promise<Buffer> {
return Buffer.from((await this._channel.body()).binary, 'base64');
return (await this._channel.body()).binary;
}
async text(): Promise<string> {
@ -533,13 +533,13 @@ export class WebSocket extends ChannelOwner<channels.WebSocketChannel> implement
super(parent, type, guid, initializer);
this._isClosed = false;
this._page = parent as Page;
this._channel.on('frameSent', (event: { opcode: number, data: string }) => {
this._channel.on('frameSent', event => {
if (event.opcode === 1)
this.emit(Events.WebSocket.FrameSent, { payload: event.data });
else if (event.opcode === 2)
this.emit(Events.WebSocket.FrameSent, { payload: Buffer.from(event.data, 'base64') });
});
this._channel.on('frameReceived', (event: { opcode: number, data: string }) => {
this._channel.on('frameReceived', event => {
if (event.opcode === 1)
this.emit(Events.WebSocket.FrameReceived, { payload: event.data });
else if (event.opcode === 2)

View File

@ -15,7 +15,6 @@
* limitations under the License.
*/
import { Buffer } from 'buffer';
import fs from 'fs';
import path from 'path';
import type * as structs from '../../types/structs';
@ -503,12 +502,11 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page
}));
}
const result = await this._channel.screenshot(copy);
const buffer = Buffer.from(result.binary, 'base64');
if (options.path) {
await mkdirIfNeeded(options.path);
await fs.promises.writeFile(options.path, buffer);
await fs.promises.writeFile(options.path, result.binary);
}
return buffer;
return result.binary;
}
async _expectScreenshot(customStackTrace: ParsedStackTrace, options: ExpectScreenshotOptions): Promise<{ actual?: Buffer, previous?: Buffer, diff?: Buffer, errorMessage?: string, log?: string[]}> {
@ -521,25 +519,15 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page
frame: options.locator._frame._channel,
selector: options.locator._selector,
} : undefined;
const expected = options.expected ? options.expected.toString('base64') : undefined;
const result = await this._channel.expectScreenshot({
return await this._channel.expectScreenshot({
...options,
isNot: !!options.isNot,
expected,
locator,
screenshotOptions: {
...options.screenshotOptions,
mask,
}
});
return {
log: result.log,
actual: result.actual ? Buffer.from(result.actual, 'base64') : undefined,
previous: result.previous ? Buffer.from(result.previous, 'base64') : undefined,
diff: result.diff ? Buffer.from(result.diff, 'base64') : undefined,
errorMessage: result.errorMessage,
};
}, false /* isInternal */, customStackTrace);
}
@ -742,12 +730,11 @@ export class Page extends ChannelOwner<channels.PageChannel> implements api.Page
transportOptions.margin![index] = transportOptions.margin![index] + 'px';
}
const result = await this._channel.pdf(transportOptions);
const buffer = Buffer.from(result.pdf, 'base64');
if (options.path) {
await fs.promises.mkdir(path.dirname(options.path), { recursive: true });
await fs.promises.writeFile(options.path, buffer);
await fs.promises.writeFile(options.path, result.pdf);
}
return buffer;
return result.pdf;
}
async _resetForReuse() {

View File

@ -42,8 +42,8 @@ class StreamImpl extends Readable {
override async _read(size: number) {
const result = await this._channel.read({ size });
if (result.binary)
this.push(Buffer.from(result.binary, 'base64'));
if (result.binary.byteLength)
this.push(result.binary);
else
this.push(null);
}

View File

@ -40,8 +40,8 @@ class WritableStreamImpl extends Writable {
this._channel = channel;
}
override async _write(chunk: any, encoding: BufferEncoding, callback: (error?: Error | null) => void) {
const error = await this._channel.write({ binary: chunk.toString('base64') }).catch(e => e);
override async _write(chunk: Buffer | string, encoding: BufferEncoding, callback: (error?: Error | null) => void) {
const error = await this._channel.write({ binary: typeof chunk === 'string' ? Buffer.from(chunk) : chunk }).catch(e => e);
callback(error || null);
}

View File

@ -23,7 +23,7 @@ export function createInProcessPlaywright(): PlaywrightAPI {
const playwright = createPlaywright('javascript');
const clientConnection = new Connection();
const dispatcherConnection = new DispatcherConnection();
const dispatcherConnection = new DispatcherConnection(true /* local */);
// Dispatch synchronously at first.
dispatcherConnection.onmessage = message => clientConnection.dispatch(message);

View File

@ -16,7 +16,7 @@
// This file is generated by generate_channels.js, do not edit manually.
export type Binary = string;
export type Binary = Buffer;
export interface Channel {
}
@ -408,11 +408,11 @@ export type LocalUtilsHarLookupParams = {
url: string,
method: string,
headers: NameValue[],
postData?: string,
postData?: Binary,
isNavigationRequest: boolean,
};
export type LocalUtilsHarLookupOptions = {
postData?: string,
postData?: Binary,
};
export type LocalUtilsHarLookupResult = {
action: 'error' | 'redirect' | 'fulfill' | 'noentry',
@ -420,7 +420,7 @@ export type LocalUtilsHarLookupResult = {
redirectURL?: string,
status?: number,
headers?: NameValue[],
body?: string,
body?: Binary,
};
export type LocalUtilsHarCloseParams = {
harId: string,

View File

@ -500,7 +500,7 @@ LocalUtils:
headers:
type: array
items: NameValue
postData: string?
postData: binary?
isNavigationRequest: boolean
returns:
action:
@ -516,7 +516,7 @@ LocalUtils:
headers:
type: array?
items: NameValue
body: string?
body: binary?
harClose:
parameters:

View File

@ -224,7 +224,7 @@ scheme.LocalUtilsHarLookupParams = tObject({
url: tString,
method: tString,
headers: tArray(tType('NameValue')),
postData: tOptional(tString),
postData: tOptional(tBinary),
isNavigationRequest: tBoolean,
});
scheme.LocalUtilsHarLookupResult = tObject({
@ -233,7 +233,7 @@ scheme.LocalUtilsHarLookupResult = tObject({
redirectURL: tOptional(tString),
status: tOptional(tNumber),
headers: tOptional(tArray(tType('NameValue'))),
body: tOptional(tString),
body: tOptional(tBinary),
});
scheme.LocalUtilsHarCloseParams = tObject({
harId: tString,

View File

@ -20,6 +20,7 @@ export class ValidationError extends Error {}
export type Validator = (arg: any, path: string, context: ValidatorContext) => any;
export type ValidatorContext = {
tChannelImpl: (names: '*' | string[], arg: any, path: string, context: ValidatorContext) => any,
binary: 'toBase64' | 'fromBase64' | 'buffer',
};
export const scheme: { [key: string]: Validator } = {};
@ -59,11 +60,24 @@ export const tString: Validator = (arg: any, path: string, context: ValidatorCon
throw new ValidationError(`${path}: expected string, got ${typeof arg}`);
};
export const tBinary: Validator = (arg: any, path: string, context: ValidatorContext) => {
if (arg instanceof String)
return arg.valueOf();
if (typeof arg === 'string')
if (context.binary === 'fromBase64') {
if (arg instanceof String)
return Buffer.from(arg.valueOf(), 'base64');
if (typeof arg === 'string')
return Buffer.from(arg, 'base64');
throw new ValidationError(`${path}: expected base64-encoded buffer, got ${typeof arg}`);
}
if (context.binary === 'toBase64') {
if (!(arg instanceof Buffer))
throw new ValidationError(`${path}: expected Buffer, got ${typeof arg}`);
return (arg as Buffer).toString('base64');
}
if (context.binary === 'buffer') {
if (!(arg instanceof Buffer))
throw new ValidationError(`${path}: expected Buffer, got ${typeof arg}`);
return arg;
throw new ValidationError(`${path}: expected base64-encoded buffer, got ${typeof arg}`);
}
throw new ValidationError(`Unsupported binary behavior "${context.binary}"`);
};
export const tUndefined: Validator = (arg: any, path: string, context: ValidatorContext) => {
if (Object.is(arg, undefined))

View File

@ -136,11 +136,11 @@ export class AndroidDeviceDispatcher extends Dispatcher<AndroidDevice, channels.
}
async screenshot(params: channels.AndroidDeviceScreenshotParams): Promise<channels.AndroidDeviceScreenshotResult> {
return { binary: (await this._object.screenshot()).toString('base64') };
return { binary: await this._object.screenshot() };
}
async shell(params: channels.AndroidDeviceShellParams): Promise<channels.AndroidDeviceShellResult> {
return { result: (await this._object.shell(params.command)).toString('base64') };
return { result: await this._object.shell(params.command) };
}
async open(params: channels.AndroidDeviceOpenParams, metadata: CallMetadata): Promise<channels.AndroidDeviceOpenResult> {
@ -149,11 +149,11 @@ export class AndroidDeviceDispatcher extends Dispatcher<AndroidDevice, channels.
}
async installApk(params: channels.AndroidDeviceInstallApkParams) {
await this._object.installApk(Buffer.from(params.file, 'base64'), { args: params.args });
await this._object.installApk(params.file, { args: params.args });
}
async push(params: channels.AndroidDevicePushParams) {
await this._object.push(Buffer.from(params.file, 'base64'), params.path, params.mode);
await this._object.push(params.file, params.path, params.mode);
}
async launchBrowser(params: channels.AndroidDeviceLaunchBrowserParams): Promise<channels.AndroidDeviceLaunchBrowserResult> {
@ -179,7 +179,7 @@ export class AndroidSocketDispatcher extends Dispatcher<SocketBackend, channels.
constructor(scope: DispatcherScope, socket: SocketBackend) {
super(scope, socket, 'AndroidSocket', {}, true);
socket.on('data', (data: Buffer) => this._dispatchEvent('data', { data: data.toString('base64') }));
socket.on('data', (data: Buffer) => this._dispatchEvent('data', { data }));
socket.on('close', () => {
this._dispatchEvent('close');
this._dispose();
@ -187,7 +187,7 @@ export class AndroidSocketDispatcher extends Dispatcher<SocketBackend, channels.
}
async write(params: channels.AndroidSocketWriteParams, metadata: CallMetadata): Promise<void> {
await this._object.write(Buffer.from(params.data, 'base64'));
await this._object.write(params.data);
}
async close(params: channels.AndroidSocketCloseParams, metadata: CallMetadata): Promise<void> {

View File

@ -70,8 +70,7 @@ export class BrowserDispatcher extends Dispatcher<Browser, channels.BrowserChann
if (!this._object.options.isChromium)
throw new Error(`Tracing is only available in Chromium`);
const crBrowser = this._object as CRBrowser;
const buffer = await crBrowser.stopTracing();
return { binary: buffer.toString('base64') };
return { binary: await crBrowser.stopTracing() };
}
}
@ -124,8 +123,7 @@ export class ConnectedBrowserDispatcher extends Dispatcher<Browser, channels.Bro
if (!this._object.options.isChromium)
throw new Error(`Tracing is only available in Chromium`);
const crBrowser = this._object as CRBrowser;
const buffer = await crBrowser.stopTracing();
return { binary: buffer.toString('base64') };
return { binary: await crBrowser.stopTracing() };
}
async cleanupContexts() {

View File

@ -27,6 +27,7 @@ import * as socks from '../../common/socksProxy';
import EventEmitter from 'events';
import { ProgressController } from '../progress';
import { WebSocketTransport } from '../transport';
import { findValidator, ValidationError, type ValidatorContext } from '../../protocol/validator';
export class BrowserTypeDispatcher extends Dispatcher<BrowserType, channels.BrowserTypeChannel> implements channels.BrowserTypeChannel {
_type_BrowserType = true;
@ -113,6 +114,8 @@ class SocksInterceptor {
try {
const id = --lastId;
this._ids.add(id);
const validator = findValidator('SocksSupport', prop, 'Params');
params = validator(params, '', { tChannelImpl: tChannelForSocks, binary: 'toBase64' });
transport.send({ id, guid: socksSupportObjectGuid, method: prop, params, metadata: { stack: [], apiName: '', internal: true } } as any);
} catch (e) {
}
@ -120,13 +123,13 @@ class SocksInterceptor {
},
}) as channels.SocksSupportChannel & EventEmitter;
this._handler.on(socks.SocksProxyHandler.Events.SocksConnected, (payload: socks.SocksSocketConnectedPayload) => this._channel.socksConnected(payload));
this._handler.on(socks.SocksProxyHandler.Events.SocksData, (payload: socks.SocksSocketDataPayload) => this._channel.socksData({ uid: payload.uid, data: payload.data.toString('base64') }));
this._handler.on(socks.SocksProxyHandler.Events.SocksData, (payload: socks.SocksSocketDataPayload) => this._channel.socksData(payload));
this._handler.on(socks.SocksProxyHandler.Events.SocksError, (payload: socks.SocksSocketErrorPayload) => this._channel.socksError(payload));
this._handler.on(socks.SocksProxyHandler.Events.SocksFailed, (payload: socks.SocksSocketFailedPayload) => this._channel.socksFailed(payload));
this._handler.on(socks.SocksProxyHandler.Events.SocksEnd, (payload: socks.SocksSocketEndPayload) => this._channel.socksEnd(payload));
this._channel.on('socksRequested', payload => this._handler.socketRequested(payload));
this._channel.on('socksClosed', payload => this._handler.socketClosed(payload));
this._channel.on('socksData', payload => this._handler.sendSocketData({ uid: payload.uid, data: Buffer.from(payload.data, 'base64') }));
this._channel.on('socksData', payload => this._handler.sendSocketData(payload));
}
cleanup() {
@ -139,9 +142,15 @@ class SocksInterceptor {
return true;
}
if (message.guid === this._socksSupportObjectGuid) {
this._channel.emit(message.method, message.params);
const validator = findValidator('SocksSupport', message.method, 'Event');
const params = validator(message.params, '', { tChannelImpl: tChannelForSocks, binary: 'fromBase64' });
this._channel.emit(message.method, params);
return true;
}
return false;
}
}
function tChannelForSocks(names: '*' | string[], arg: any, path: string, context: ValidatorContext) {
throw new ValidationError(`${path}: channels are not expected in SocksSupport`);
}

View File

@ -144,16 +144,21 @@ export class DispatcherConnection {
readonly _dispatchers = new Map<string, Dispatcher<any, any>>();
onmessage = (message: object) => {};
private _waitOperations = new Map<string, CallMetadata>();
private _isLocal: boolean;
constructor(isLocal?: boolean) {
this._isLocal = !!isLocal;
}
sendEvent(dispatcher: Dispatcher<any, any>, event: string, params: any, sdkObject?: SdkObject) {
const validator = findValidator(dispatcher._type, event, 'Event');
params = validator(params, '', { tChannelImpl: this._tChannelImplToWire.bind(this) });
params = validator(params, '', { tChannelImpl: this._tChannelImplToWire.bind(this), binary: this._isLocal ? 'buffer' : 'toBase64' });
this._sendMessageToClient(dispatcher._guid, dispatcher._type, event, params, sdkObject);
}
sendCreate(parent: Dispatcher<any, any>, type: string, guid: string, initializer: any, sdkObject?: SdkObject) {
const validator = findValidator(type, '', 'Initializer');
initializer = validator(initializer, '', { tChannelImpl: this._tChannelImplToWire.bind(this) });
initializer = validator(initializer, '', { tChannelImpl: this._tChannelImplToWire.bind(this), binary: this._isLocal ? 'buffer' : 'toBase64' });
this._sendMessageToClient(parent._guid, type, '__create__', { type, initializer, guid }, sdkObject);
}
@ -216,8 +221,8 @@ export class DispatcherConnection {
let validMetadata: channels.Metadata;
try {
const validator = findValidator(dispatcher._type, method, 'Params');
validParams = validator(params, '', { tChannelImpl: this._tChannelImplFromWire.bind(this) });
validMetadata = metadataValidator(metadata, '', { tChannelImpl: this._tChannelImplFromWire.bind(this) });
validParams = validator(params, '', { tChannelImpl: this._tChannelImplFromWire.bind(this), binary: this._isLocal ? 'buffer' : 'fromBase64' });
validMetadata = metadataValidator(metadata, '', { tChannelImpl: this._tChannelImplFromWire.bind(this), binary: this._isLocal ? 'buffer' : 'fromBase64' });
if (typeof (dispatcher as any)[method] !== 'function')
throw new Error(`Mismatching dispatcher: "${dispatcher._type}" does not implement "${method}"`);
} catch (e) {
@ -277,7 +282,7 @@ export class DispatcherConnection {
try {
const result = await (dispatcher as any)[method](validParams, callMetadata);
const validator = findValidator(dispatcher._type, method, 'Result');
callMetadata.result = validator(result, '', { tChannelImpl: this._tChannelImplToWire.bind(this) });
callMetadata.result = validator(result, '', { tChannelImpl: this._tChannelImplToWire.bind(this), binary: this._isLocal ? 'buffer' : 'toBase64' });
} catch (e) {
// Dispatching error
// We want original, unmodified error in metadata.

View File

@ -191,7 +191,7 @@ export class ElementHandleDispatcher extends JSHandleDispatcher implements chann
frame: (frame as FrameDispatcher)._object,
selector,
}));
return { binary: (await this._elementHandle.screenshot(metadata, { ...params, mask })).toString('base64') };
return { binary: await this._elementHandle.screenshot(metadata, { ...params, mask }) };
}
async querySelector(params: channels.ElementHandleQuerySelectorParams, metadata: CallMetadata): Promise<channels.ElementHandleQuerySelectorResult> {

View File

@ -114,7 +114,7 @@ export class LocalUtilsDispatcher extends Dispatcher<{ guid: string }, channels.
const harBackend = this._harBakends.get(params.harId);
if (!harBackend)
return { action: 'error', message: `Internal error: har was not opened` };
return await harBackend.lookup(params.url, params.method, params.headers, params.postData ? Buffer.from(params.postData, 'base64') : undefined, params.isNavigationRequest);
return await harBackend.lookup(params.url, params.method, params.headers, params.postData, params.isNavigationRequest);
}
async harClose(params: channels.LocalUtilsHarCloseParams, metadata?: channels.Metadata): Promise<void> {
@ -160,8 +160,7 @@ class HarBackend {
redirectURL?: string,
status?: number,
headers?: HeadersArray,
body?: string,
base64Encoded?: boolean }> {
body?: Buffer }> {
let entry;
try {
entry = await this._harFindResponse(url, method, headers, postData);
@ -183,7 +182,7 @@ class HarBackend {
action: 'fulfill',
status: response.status,
headers: response.headers,
body: buffer.toString('base64'),
body: buffer,
};
} catch (e) {
return { action: 'error', message: e.message };

View File

@ -45,7 +45,7 @@ export class RequestDispatcher extends Dispatcher<Request, channels.RequestChann
url: request.url(),
resourceType: request.resourceType(),
method: request.method(),
postData: postData === null ? undefined : postData.toString('base64'),
postData: postData === null ? undefined : postData,
headers: request.headers(),
isNavigationRequest: request.isNavigationRequest(),
redirectedFrom: RequestDispatcher.fromNullable(scope, request.redirectedFrom()),
@ -88,7 +88,7 @@ export class ResponseDispatcher extends Dispatcher<Response, channels.ResponseCh
}
async body(): Promise<channels.ResponseBodyResult> {
return { binary: (await this._object.body()).toString('base64') };
return { binary: await this._object.body() };
}
async securityDetails(): Promise<channels.ResponseSecurityDetailsResult> {
@ -128,7 +128,7 @@ export class RouteDispatcher extends Dispatcher<Route, channels.RouteChannel> im
url: params.url,
method: params.method,
headers: params.headers,
postData: params.postData !== undefined ? Buffer.from(params.postData, 'base64') : undefined,
postData: params.postData,
});
}
@ -204,8 +204,7 @@ export class APIRequestContextDispatcher extends Dispatcher<APIRequestContext, c
}
async fetchResponseBody(params: channels.APIRequestContextFetchResponseBodyParams, metadata?: channels.Metadata): Promise<channels.APIRequestContextFetchResponseBodyResult> {
const buffer = this._object.fetchResponses.get(params.fetchUid);
return { binary: buffer ? buffer.toString('base64') : undefined };
return { binary: this._object.fetchResponses.get(params.fetchUid) };
}
async fetchLog(params: channels.APIRequestContextFetchLogParams, metadata?: channels.Metadata): Promise<channels.APIRequestContextFetchLogResult> {

View File

@ -167,23 +167,14 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageChannel> imple
frame: (params.locator.frame as FrameDispatcher)._object,
selector: params.locator.selector,
} : undefined;
const expected = params.expected ? Buffer.from(params.expected, 'base64') : undefined;
const result = await this._page.expectScreenshot(metadata, {
return await this._page.expectScreenshot(metadata, {
...params,
expected,
locator,
screenshotOptions: {
...params.screenshotOptions,
mask,
},
});
return {
diff: result.diff?.toString('base64'),
errorMessage: result.errorMessage,
actual: result.actual?.toString('base64'),
previous: result.previous?.toString('base64'),
log: result.log,
};
}
async screenshot(params: channels.PageScreenshotParams, metadata: CallMetadata): Promise<channels.PageScreenshotResult> {
@ -191,7 +182,7 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageChannel> imple
frame: (frame as FrameDispatcher)._object,
selector,
}));
return { binary: (await this._page.screenshot(metadata, { ...params, mask })).toString('base64') };
return { binary: await this._page.screenshot(metadata, { ...params, mask }) };
}
async close(params: channels.PageCloseParams, metadata: CallMetadata): Promise<void> {
@ -258,7 +249,7 @@ export class PageDispatcher extends Dispatcher<Page, channels.PageChannel> imple
if (!this._page.pdf)
throw new Error('PDF generation is only supported for Headless Chromium');
const buffer = await this._page.pdf(params);
return { pdf: buffer.toString('base64') };
return { pdf: buffer };
}
async bringToFront(params: channels.PageBringToFrontParams, metadata: CallMetadata): Promise<void> {

View File

@ -81,7 +81,7 @@ class SocksSupportDispatcher extends Dispatcher<{ guid: string }, channels.Socks
this._type_SocksSupport = true;
this._socksProxy = socksProxy;
socksProxy.on(SocksProxy.Events.SocksRequested, (payload: SocksSocketRequestedPayload) => this._dispatchEvent('socksRequested', payload));
socksProxy.on(SocksProxy.Events.SocksData, (payload: SocksSocketDataPayload) => this._dispatchEvent('socksData', { uid: payload.uid, data: payload.data.toString('base64') }));
socksProxy.on(SocksProxy.Events.SocksData, (payload: SocksSocketDataPayload) => this._dispatchEvent('socksData', payload));
socksProxy.on(SocksProxy.Events.SocksClosed, (payload: SocksSocketClosedPayload) => this._dispatchEvent('socksClosed', payload));
}
@ -94,7 +94,7 @@ class SocksSupportDispatcher extends Dispatcher<{ guid: string }, channels.Socks
}
async socksData(params: channels.SocksSupportSocksDataParams): Promise<void> {
this._socksProxy?.sendSocketData({ uid: params.uid, data: Buffer.from(params.data, 'base64') });
this._socksProxy?.sendSocketData(params);
}
async socksError(params: channels.SocksSupportSocksErrorParams): Promise<void> {

View File

@ -33,7 +33,7 @@ export class StreamDispatcher extends Dispatcher<{ guid: string, stream: stream.
async read(params: channels.StreamReadParams): Promise<channels.StreamReadResult> {
const stream = this._object.stream;
if (this._ended)
return { binary: '' };
return { binary: Buffer.from('') };
if (!stream.readableLength) {
await new Promise((fulfill, reject) => {
stream.once('readable', fulfill);
@ -42,7 +42,7 @@ export class StreamDispatcher extends Dispatcher<{ guid: string, stream: stream.
});
}
const buffer = stream.read(Math.min(stream.readableLength, params.size || stream.readableLength));
return { binary: buffer ? buffer.toString('base64') : '' };
return { binary: buffer || Buffer.from('') };
}
async close() {

View File

@ -29,7 +29,7 @@ export class WritableStreamDispatcher extends Dispatcher<{ guid: string, stream:
async write(params: channels.WritableStreamWriteParams): Promise<channels.WritableStreamWriteResult> {
const stream = this._object.stream;
await new Promise<void>((fulfill, reject) => {
stream.write(Buffer.from(params.binary, 'base64'), error => {
stream.write(params.binary, error => {
if (error)
reject(error);
else

View File

@ -615,10 +615,15 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
async _setInputFiles(progress: Progress, items: InputFilesItems, options: types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> {
const { files, localPaths } = items;
let filePayloads: types.FilePayload[] | undefined;
if (files) {
filePayloads = [];
for (const payload of files) {
if (!payload.mimeType)
payload.mimeType = mime.getType(payload.name) || 'application/octet-stream';
filePayloads.push({
name: payload.name,
mimeType: payload.mimeType || mime.getType(payload.name) || 'application/octet-stream',
buffer: payload.buffer.toString('base64'),
});
}
}
const multiple = files && files.length > 1 || localPaths && localPaths.length > 1;
@ -641,7 +646,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
if (localPaths)
await this._page._delegate.setInputFilePaths(retargeted, localPaths);
else
await this._page._delegate.setInputFiles(retargeted, files as types.FilePayload[]);
await this._page._delegate.setInputFiles(retargeted, filePayloads!);
});
await this._page._doSlowMo();
return 'done';

View File

@ -643,7 +643,7 @@ function serializePostData(params: channels.APIRequestContextFetchParams, header
return formData.finish();
} else if (params.postData !== undefined) {
headers['content-type'] ??= 'application/octet-stream';
return Buffer.from(params.postData, 'base64');
return params.postData;
}
return undefined;
}

View File

@ -41,7 +41,7 @@ export class MultipartFormData {
this._chunks.push(Buffer.from(`; filename="${value.name}"`));
this._chunks.push(Buffer.from(`\r\ncontent-type: ${value.mimeType || mime.getType(value.name) || 'application/octet-stream'}`));
this._finishMultiPartHeader();
this._chunks.push(Buffer.from(value.buffer, 'base64'));
this._chunks.push(value.buffer);
this._finishMultiPartField();
}

View File

@ -126,7 +126,7 @@ const channels_ts = [
// This file is generated by ${path.basename(__filename).split(path.sep).join(path.posix.sep)}, do not edit manually.
export type Binary = string;
export type Binary = Buffer;
export interface Channel {
}