chore: introduce internal protocol
This commit is contained in:
parent
492304be41
commit
d68f57dba8
116
src/protocol/browser.ts
Normal file
116
src/protocol/browser.ts
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2017 Google Inc. All rights reserved.
|
||||||
|
* Modifications copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* 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 { helper } from '../helper';
|
||||||
|
import { ClientConnection } from './connection';
|
||||||
|
import * as platform from '../platform';
|
||||||
|
import { ConnectionTransport } from '../transport';
|
||||||
|
import { Events } from '../events';
|
||||||
|
import * as types from '../types';
|
||||||
|
|
||||||
|
export type BrowserContextOptions = {
|
||||||
|
viewport?: types.Viewport | null,
|
||||||
|
// ignoreHTTPSErrors?: boolean,
|
||||||
|
// javaScriptEnabled?: boolean,
|
||||||
|
// bypassCSP?: boolean,
|
||||||
|
// userAgent?: string,
|
||||||
|
// timezoneId?: string,
|
||||||
|
geolocation?: types.Geolocation,
|
||||||
|
// permissions?: { [key: string]: string[] };
|
||||||
|
};
|
||||||
|
|
||||||
|
export class Browser extends platform.EventEmitter {
|
||||||
|
readonly _connection: ClientConnection;
|
||||||
|
readonly _browserContexts = new Map<string, BrowserContext>();
|
||||||
|
|
||||||
|
constructor(transport: ConnectionTransport) {
|
||||||
|
super();
|
||||||
|
this._connection = new ClientConnection(transport, () => this.emit(Events.Browser.Disconnected));
|
||||||
|
}
|
||||||
|
|
||||||
|
async newContext(options: BrowserContextOptions = {}): Promise<BrowserContext> {
|
||||||
|
options = { ...options };
|
||||||
|
if (!options.viewport && options.viewport !== null)
|
||||||
|
options.viewport = { width: 800, height: 600 };
|
||||||
|
if (options.viewport)
|
||||||
|
options.viewport = { ...options.viewport };
|
||||||
|
if (options.geolocation)
|
||||||
|
options.geolocation = verifyGeolocation(options.geolocation);
|
||||||
|
|
||||||
|
const { contextId } = await this._connection.send('BrowserContext.create', {
|
||||||
|
viewportSize: options.viewport ? { width: options.viewport.width, height: options.viewport.height } : undefined,
|
||||||
|
geolocation: options.geolocation,
|
||||||
|
});
|
||||||
|
return new BrowserContext(this, contextId, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
async disconnect() {
|
||||||
|
const disconnected = new Promise(f => this.once(Events.Browser.Disconnected, f));
|
||||||
|
this._connection.close();
|
||||||
|
await disconnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
isConnected(): boolean {
|
||||||
|
return !this._connection.isClosed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BrowserContext {
|
||||||
|
readonly _browser: Browser;
|
||||||
|
readonly _browserContextId: string;
|
||||||
|
readonly _options: BrowserContextOptions;
|
||||||
|
|
||||||
|
private _closed = false;
|
||||||
|
|
||||||
|
constructor(browser: Browser, browserContextId: string, options: BrowserContextOptions) {
|
||||||
|
this._browser = browser;
|
||||||
|
this._browserContextId = browserContextId;
|
||||||
|
this._options = options;
|
||||||
|
this._browser._browserContexts.set(this._browserContextId, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
async setGeolocation(geolocation: types.Geolocation | null): Promise<void> {
|
||||||
|
if (geolocation)
|
||||||
|
geolocation = verifyGeolocation(geolocation);
|
||||||
|
this._options.geolocation = geolocation || undefined;
|
||||||
|
await this._browser._connection.send('BrowserContext.setGeolocation', {
|
||||||
|
contextId: this._browserContextId,
|
||||||
|
geolocation: this._options.geolocation,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async close() {
|
||||||
|
if (this._closed)
|
||||||
|
return;
|
||||||
|
this._closed = true;
|
||||||
|
this._browser._browserContexts.delete(this._browserContextId);
|
||||||
|
await this._browser._connection.send('BrowserContext.destroy', { contextId: this._browserContextId });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function verifyGeolocation(geolocation: types.Geolocation): types.Geolocation {
|
||||||
|
const result = { ...geolocation };
|
||||||
|
result.accuracy = result.accuracy || 0;
|
||||||
|
const { longitude, latitude, accuracy } = result;
|
||||||
|
if (!helper.isNumber(longitude) || longitude < -180 || longitude > 180)
|
||||||
|
throw new Error(`Invalid longitude "${longitude}": precondition -180 <= LONGITUDE <= 180 failed.`);
|
||||||
|
if (!helper.isNumber(latitude) || latitude < -90 || latitude > 90)
|
||||||
|
throw new Error(`Invalid latitude "${latitude}": precondition -90 <= LATITUDE <= 90 failed.`);
|
||||||
|
if (!helper.isNumber(accuracy) || accuracy < 0)
|
||||||
|
throw new Error(`Invalid accuracy "${accuracy}": precondition 0 <= ACCURACY failed.`);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
205
src/protocol/connection.ts
Normal file
205
src/protocol/connection.ts
Normal file
|
|
@ -0,0 +1,205 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Microsoft Corporation.
|
||||||
|
*
|
||||||
|
* 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 { assert } from '../helper';
|
||||||
|
import * as platform from '../platform';
|
||||||
|
import { ConnectionTransport } from '../transport';
|
||||||
|
import { Protocol } from './protocol';
|
||||||
|
|
||||||
|
const debugClient = platform.debug('pw:client');
|
||||||
|
const debugServer = platform.debug('pw:server');
|
||||||
|
|
||||||
|
export interface ObjectTransport {
|
||||||
|
send(message: any): void;
|
||||||
|
close(): void; // Note: calling close is expected to issue onclose at some point.
|
||||||
|
onmessage?: (message: any) => void,
|
||||||
|
onclose?: () => void,
|
||||||
|
}
|
||||||
|
|
||||||
|
export class JSONTransport implements ObjectTransport {
|
||||||
|
private readonly _transport: ConnectionTransport;
|
||||||
|
|
||||||
|
onmessage?: (message: string) => void;
|
||||||
|
onclose?: () => void;
|
||||||
|
|
||||||
|
constructor(transport: ConnectionTransport) {
|
||||||
|
this._transport = transport;
|
||||||
|
this._transport.onmessage = message => {
|
||||||
|
if (this.onmessage)
|
||||||
|
this.onmessage(JSON.parse(message));
|
||||||
|
};
|
||||||
|
this._transport.onclose = () => {
|
||||||
|
if (this.onclose)
|
||||||
|
this.onclose();
|
||||||
|
};
|
||||||
|
this.onmessage = undefined;
|
||||||
|
this.onclose = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
send(message: any) {
|
||||||
|
this._transport.send(JSON.stringify(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this._transport.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ClientConnection extends platform.EventEmitter {
|
||||||
|
private _lastId = 0;
|
||||||
|
private readonly _transport: ObjectTransport;
|
||||||
|
private _closed = false;
|
||||||
|
private _onDisconnect: () => void;
|
||||||
|
private readonly _callbacks = new Map<number, {resolve:(o: any) => void, reject: (e: Error) => void, error: Error, method: string}>();
|
||||||
|
|
||||||
|
on: <T extends keyof Protocol.Events>(event: T, listener: (payload: Protocol.Events[T]) => void) => this;
|
||||||
|
addListener: <T extends keyof Protocol.Events>(event: T, listener: (payload: Protocol.Events[T]) => void) => this;
|
||||||
|
off: <T extends keyof Protocol.Events>(event: T, listener: (payload: Protocol.Events[T]) => void) => this;
|
||||||
|
removeListener: <T extends keyof Protocol.Events>(event: T, listener: (payload: Protocol.Events[T]) => void) => this;
|
||||||
|
once: <T extends keyof Protocol.Events>(event: T, listener: (payload: Protocol.Events[T]) => void) => this;
|
||||||
|
|
||||||
|
constructor(transport: ObjectTransport, onDisconnect: () => void) {
|
||||||
|
super();
|
||||||
|
this._transport = transport;
|
||||||
|
this._transport.onmessage = this._dispatchMessage.bind(this);
|
||||||
|
this._transport.onclose = this._onClose.bind(this);
|
||||||
|
this._onDisconnect = onDisconnect;
|
||||||
|
this.on = super.on;
|
||||||
|
this.off = super.removeListener;
|
||||||
|
this.addListener = super.addListener;
|
||||||
|
this.removeListener = super.removeListener;
|
||||||
|
this.once = super.once;
|
||||||
|
}
|
||||||
|
|
||||||
|
send<T extends keyof Protocol.CommandParameters>(
|
||||||
|
method: T,
|
||||||
|
params?: Protocol.CommandParameters[T]
|
||||||
|
): Promise<Protocol.CommandReturnValues[T]> {
|
||||||
|
if (this._closed)
|
||||||
|
return Promise.reject(new Error(`Internal error (${method}): Connection has been closed.`));
|
||||||
|
const id = ++this._lastId;
|
||||||
|
const message = { id, method, params };
|
||||||
|
const result = new Promise<Protocol.CommandReturnValues[T]>((resolve, reject) => {
|
||||||
|
this._callbacks.set(id, {resolve, reject, error: new Error(), method});
|
||||||
|
});
|
||||||
|
debugClient('SEND ► %o', message);
|
||||||
|
this._transport.send(message);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _dispatchMessage(message: any) {
|
||||||
|
debugClient('◀ RECV %o', message);
|
||||||
|
if (message.id && this._callbacks.has(message.id)) {
|
||||||
|
const callback = this._callbacks.get(message.id)!;
|
||||||
|
this._callbacks.delete(message.id);
|
||||||
|
if (message.error)
|
||||||
|
callback.reject(createInternalError(callback.error, callback.method, message));
|
||||||
|
else
|
||||||
|
callback.resolve(message.result);
|
||||||
|
} else {
|
||||||
|
assert(!message.id);
|
||||||
|
Promise.resolve().then(() => this.emit(message.method, message.params));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_onClose() {
|
||||||
|
this._closed = true;
|
||||||
|
this._transport.onmessage = undefined;
|
||||||
|
this._transport.onclose = undefined;
|
||||||
|
for (const callback of this._callbacks.values())
|
||||||
|
callback.reject(rewriteError(callback.error, `Internal error (${callback.method}): Connection has been closed.`));
|
||||||
|
this._callbacks.clear();
|
||||||
|
this._onDisconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
isClosed() {
|
||||||
|
return this._closed;
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
if (!this._closed)
|
||||||
|
this._transport.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ServerConnection {
|
||||||
|
private readonly _transport: ObjectTransport;
|
||||||
|
private _closed = false;
|
||||||
|
private _onDisconnect: () => void;
|
||||||
|
private readonly _handlers = new Map<string, (arg: any) => Promise<any>>();
|
||||||
|
|
||||||
|
constructor(transport: ObjectTransport, onDisconnect: () => void) {
|
||||||
|
this._transport = transport;
|
||||||
|
this._transport.onmessage = this._dispatchMessage.bind(this);
|
||||||
|
this._transport.onclose = this._onClose.bind(this);
|
||||||
|
this._onDisconnect = onDisconnect;
|
||||||
|
}
|
||||||
|
|
||||||
|
on<T extends keyof Protocol.CommandParameters>(command: T, handler: (payload: Protocol.CommandParameters[T]) => Promise<Protocol.CommandReturnValues[T]>) {
|
||||||
|
this._handlers.set(command, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
send<T extends keyof Protocol.Events>(event: T, params?: Protocol.Events[T]) {
|
||||||
|
if (this._closed)
|
||||||
|
return Promise.reject(new Error(`Internal error (${event}): Connection has been closed.`));
|
||||||
|
const message = { event, params };
|
||||||
|
debugServer('SEND ► %o', message);
|
||||||
|
this._transport.send(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _dispatchMessage(message: any) {
|
||||||
|
debugServer('◀ RECV %o', message);
|
||||||
|
assert(message.id);
|
||||||
|
let result;
|
||||||
|
try {
|
||||||
|
const handler = this._handlers.get(message.method);
|
||||||
|
if (!handler)
|
||||||
|
throw new Error(`Unknown command "${message.method}"`);
|
||||||
|
result = { id: message.id, result: await handler(message.params) };
|
||||||
|
} catch (e) {
|
||||||
|
result = { id: message.id, error: { message: e.message } };
|
||||||
|
}
|
||||||
|
this._transport.send(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
_onClose() {
|
||||||
|
this._closed = true;
|
||||||
|
this._transport.onmessage = undefined;
|
||||||
|
this._transport.onclose = undefined;
|
||||||
|
this._onDisconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
isClosed() {
|
||||||
|
return this._closed;
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
if (!this._closed)
|
||||||
|
this._transport.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createInternalError(error: Error, method: string, object: { error: { message: string; data: any; }; }): Error {
|
||||||
|
let message = `Internal error (${method}): ${object.error.message}`;
|
||||||
|
if ('data' in object.error)
|
||||||
|
message += ` ${object.error.data}`;
|
||||||
|
return rewriteError(error, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
function rewriteError(error: Error, message: string): Error {
|
||||||
|
error.message = message;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
231
src/protocol/protocol.pdl
Normal file
231
src/protocol/protocol.pdl
Normal file
|
|
@ -0,0 +1,231 @@
|
||||||
|
# Copyright (c) Microsoft Corporation.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
domain Types
|
||||||
|
type Size extends object
|
||||||
|
properties
|
||||||
|
integer width
|
||||||
|
integer height
|
||||||
|
|
||||||
|
domain BrowserContext
|
||||||
|
type BrowserContextId extends string
|
||||||
|
|
||||||
|
type Geolocation extends object
|
||||||
|
properties
|
||||||
|
number longitude
|
||||||
|
number latitude
|
||||||
|
optional number accuracy
|
||||||
|
|
||||||
|
command create
|
||||||
|
parameters
|
||||||
|
optional Types.Size viewportSize
|
||||||
|
optional Geolocation geolocation
|
||||||
|
returns
|
||||||
|
BrowserContextId contextId
|
||||||
|
|
||||||
|
command destroy
|
||||||
|
parameters
|
||||||
|
BrowserContextId contextId
|
||||||
|
|
||||||
|
command setGeolocation
|
||||||
|
parameters
|
||||||
|
BrowserContextId contextId
|
||||||
|
optional Geolocation geolocation
|
||||||
|
|
||||||
|
domain Frame
|
||||||
|
type FrameId extends string
|
||||||
|
|
||||||
|
type FrameTree extends object
|
||||||
|
properties
|
||||||
|
FrameId frameId
|
||||||
|
string url
|
||||||
|
optional array of FrameTree childFrames
|
||||||
|
|
||||||
|
type Lifecycle extends string
|
||||||
|
enum
|
||||||
|
domcontentloaded
|
||||||
|
load
|
||||||
|
networkidle0
|
||||||
|
networkidle2
|
||||||
|
|
||||||
|
command createPage
|
||||||
|
parameters
|
||||||
|
BrowserContext.BrowserContextId contextId
|
||||||
|
optional string url
|
||||||
|
returns
|
||||||
|
FrameId mainFrameId
|
||||||
|
|
||||||
|
command destroyPage
|
||||||
|
parameters
|
||||||
|
FrameId mainFrameId
|
||||||
|
|
||||||
|
event pageCreated
|
||||||
|
parameters
|
||||||
|
BrowserContext.BrowserContextId contextId
|
||||||
|
FrameTree frameTree
|
||||||
|
|
||||||
|
event pageDestroyed
|
||||||
|
parameters
|
||||||
|
FrameId mainFrameId
|
||||||
|
|
||||||
|
event attached
|
||||||
|
parameters
|
||||||
|
FrameId frameId
|
||||||
|
FrameId parentFrameId
|
||||||
|
|
||||||
|
event navigated
|
||||||
|
parameters
|
||||||
|
FrameId frameId
|
||||||
|
string url
|
||||||
|
|
||||||
|
event detached
|
||||||
|
parameters
|
||||||
|
FrameId frameId
|
||||||
|
|
||||||
|
command navigate
|
||||||
|
parameters
|
||||||
|
FrameId frameId
|
||||||
|
string url
|
||||||
|
array of Lifecycle waitUntil
|
||||||
|
optional string referrer
|
||||||
|
returns
|
||||||
|
optional Network.RequestId requestId
|
||||||
|
|
||||||
|
domain JS
|
||||||
|
type HandleId extends string
|
||||||
|
|
||||||
|
type Handle extends object
|
||||||
|
properties
|
||||||
|
HandleId handleId
|
||||||
|
string asString
|
||||||
|
boolean isDOMElement
|
||||||
|
|
||||||
|
type Value extends object
|
||||||
|
properties
|
||||||
|
optional string string
|
||||||
|
optional number number
|
||||||
|
optional boolean null
|
||||||
|
optional boolean undefined
|
||||||
|
optional boolean boolean
|
||||||
|
optional array of Value values
|
||||||
|
optional array of string keys
|
||||||
|
|
||||||
|
type CallArgument extends object
|
||||||
|
properties
|
||||||
|
optional Value value
|
||||||
|
optional HandleId handleId
|
||||||
|
|
||||||
|
command evaluateHandle
|
||||||
|
parameters
|
||||||
|
string functionExpression
|
||||||
|
array of CallArgument args
|
||||||
|
optional Frame.FrameId frameId
|
||||||
|
optional HandleId handleId
|
||||||
|
returns
|
||||||
|
Handle handle
|
||||||
|
|
||||||
|
command evaluate
|
||||||
|
parameters
|
||||||
|
string functionExpression
|
||||||
|
array of CallArgument args
|
||||||
|
optional Frame.FrameId frameId
|
||||||
|
optional HandleId handleId
|
||||||
|
returns
|
||||||
|
Value value
|
||||||
|
|
||||||
|
command handleAsValue
|
||||||
|
parameters
|
||||||
|
HandleId handleId
|
||||||
|
returns
|
||||||
|
Value value
|
||||||
|
|
||||||
|
command disposeHandle
|
||||||
|
parameters
|
||||||
|
HandleId handleId
|
||||||
|
|
||||||
|
domain DOM
|
||||||
|
command ownerFrame
|
||||||
|
parameters
|
||||||
|
JS.HandleId handleId
|
||||||
|
returns
|
||||||
|
optional Frame.FrameId frameId
|
||||||
|
|
||||||
|
command contentFrame
|
||||||
|
parameters
|
||||||
|
JS.HandleId handleId
|
||||||
|
returns
|
||||||
|
optional Frame.FrameId frameId
|
||||||
|
|
||||||
|
command scrollIntoViewIfNeeded
|
||||||
|
parameters
|
||||||
|
JS.HandleId handleId
|
||||||
|
|
||||||
|
domain Network
|
||||||
|
type RequestId extends string
|
||||||
|
|
||||||
|
type ResourceType extends string
|
||||||
|
enum
|
||||||
|
document
|
||||||
|
|
||||||
|
type Header extends object
|
||||||
|
properties
|
||||||
|
string name
|
||||||
|
string value
|
||||||
|
|
||||||
|
type Headers extends array of Header
|
||||||
|
|
||||||
|
event requestStarted
|
||||||
|
parameters
|
||||||
|
RequestId requestId
|
||||||
|
Frame.FrameId frameId
|
||||||
|
string url
|
||||||
|
string method
|
||||||
|
ResourceType resourceType
|
||||||
|
Headers headers
|
||||||
|
boolean isIntercepted
|
||||||
|
boolean isNavigation
|
||||||
|
optional RequestId redirectedFrom
|
||||||
|
optional string postData
|
||||||
|
|
||||||
|
event responseReceived
|
||||||
|
parameters
|
||||||
|
RequestId requestId
|
||||||
|
|
||||||
|
event requestFailed
|
||||||
|
parameters
|
||||||
|
RequestId requestId
|
||||||
|
string errorText
|
||||||
|
|
||||||
|
event requestFinished
|
||||||
|
parameters
|
||||||
|
RequestId requestId
|
||||||
|
|
||||||
|
command abortRequest
|
||||||
|
parameters
|
||||||
|
RequestId requestId
|
||||||
|
string errorCode
|
||||||
|
|
||||||
|
command fulfillRequest
|
||||||
|
parameters
|
||||||
|
RequestId requestId
|
||||||
|
integer status
|
||||||
|
Headers headers
|
||||||
|
string contentType
|
||||||
|
string base64body
|
||||||
|
|
||||||
|
command continueRequest
|
||||||
|
parameters
|
||||||
|
RequestId requestId
|
||||||
|
optional Headers headers
|
||||||
|
|
||||||
270
src/protocol/protocol.ts
Normal file
270
src/protocol/protocol.ts
Normal file
|
|
@ -0,0 +1,270 @@
|
||||||
|
// This is generated from /utils/protocol-types-generator/index.js
|
||||||
|
type binary = string;
|
||||||
|
export module Protocol {
|
||||||
|
export module Types {
|
||||||
|
export interface Size {
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export module BrowserContext {
|
||||||
|
export type BrowserContextId = string;
|
||||||
|
export interface Geolocation {
|
||||||
|
longitude: number;
|
||||||
|
latitude: number;
|
||||||
|
accuracy?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export type createParameters = {
|
||||||
|
viewportSize?: Types.Size;
|
||||||
|
geolocation?: Geolocation;
|
||||||
|
}
|
||||||
|
export type createReturnValue = {
|
||||||
|
contextId: BrowserContextId;
|
||||||
|
}
|
||||||
|
export type destroyParameters = {
|
||||||
|
contextId: BrowserContextId;
|
||||||
|
}
|
||||||
|
export type destroyReturnValue = {
|
||||||
|
}
|
||||||
|
export type setGeolocationParameters = {
|
||||||
|
contextId: BrowserContextId;
|
||||||
|
geolocation?: Geolocation;
|
||||||
|
}
|
||||||
|
export type setGeolocationReturnValue = {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export module Frame {
|
||||||
|
export type FrameId = string;
|
||||||
|
export interface FrameTree {
|
||||||
|
frameId: FrameId;
|
||||||
|
url: string;
|
||||||
|
childFrames?: FrameTree[];
|
||||||
|
}
|
||||||
|
export type Lifecycle = "domcontentloaded"|"load"|"networkidle0"|"networkidle2";
|
||||||
|
|
||||||
|
export type pageCreatedPayload = {
|
||||||
|
contextId: BrowserContext.BrowserContextId;
|
||||||
|
frameTree: FrameTree;
|
||||||
|
}
|
||||||
|
export type pageDestroyedPayload = {
|
||||||
|
mainFrameId: FrameId;
|
||||||
|
}
|
||||||
|
export type attachedPayload = {
|
||||||
|
frameId: FrameId;
|
||||||
|
parentFrameId: FrameId;
|
||||||
|
}
|
||||||
|
export type navigatedPayload = {
|
||||||
|
frameId: FrameId;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
export type detachedPayload = {
|
||||||
|
frameId: FrameId;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type createPageParameters = {
|
||||||
|
contextId: BrowserContext.BrowserContextId;
|
||||||
|
url?: string;
|
||||||
|
}
|
||||||
|
export type createPageReturnValue = {
|
||||||
|
mainFrameId: FrameId;
|
||||||
|
}
|
||||||
|
export type destroyPageParameters = {
|
||||||
|
mainFrameId: FrameId;
|
||||||
|
}
|
||||||
|
export type destroyPageReturnValue = {
|
||||||
|
}
|
||||||
|
export type navigateParameters = {
|
||||||
|
frameId: FrameId;
|
||||||
|
url: string;
|
||||||
|
waitUntil: Lifecycle[];
|
||||||
|
referrer?: string;
|
||||||
|
}
|
||||||
|
export type navigateReturnValue = {
|
||||||
|
requestId?: Network.RequestId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export module JS {
|
||||||
|
export type HandleId = string;
|
||||||
|
export interface Handle {
|
||||||
|
handleId: HandleId;
|
||||||
|
asString: string;
|
||||||
|
isDOMElement: boolean;
|
||||||
|
}
|
||||||
|
export interface Value {
|
||||||
|
string?: string;
|
||||||
|
number?: number;
|
||||||
|
null?: boolean;
|
||||||
|
undefined?: boolean;
|
||||||
|
boolean?: boolean;
|
||||||
|
values?: Value[];
|
||||||
|
keys?: string[];
|
||||||
|
}
|
||||||
|
export interface CallArgument {
|
||||||
|
value?: Value;
|
||||||
|
handleId?: HandleId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export type evaluateHandleParameters = {
|
||||||
|
functionExpression: string;
|
||||||
|
args: CallArgument[];
|
||||||
|
frameId?: Frame.FrameId;
|
||||||
|
handleId?: HandleId;
|
||||||
|
}
|
||||||
|
export type evaluateHandleReturnValue = {
|
||||||
|
handle: Handle;
|
||||||
|
}
|
||||||
|
export type evaluateParameters = {
|
||||||
|
functionExpression: string;
|
||||||
|
args: CallArgument[];
|
||||||
|
frameId?: Frame.FrameId;
|
||||||
|
handleId?: HandleId;
|
||||||
|
}
|
||||||
|
export type evaluateReturnValue = {
|
||||||
|
value: Value;
|
||||||
|
}
|
||||||
|
export type handleAsValueParameters = {
|
||||||
|
handleId: HandleId;
|
||||||
|
}
|
||||||
|
export type handleAsValueReturnValue = {
|
||||||
|
value: Value;
|
||||||
|
}
|
||||||
|
export type disposeHandleParameters = {
|
||||||
|
handleId: HandleId;
|
||||||
|
}
|
||||||
|
export type disposeHandleReturnValue = {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export module DOM {
|
||||||
|
|
||||||
|
|
||||||
|
export type ownerFrameParameters = {
|
||||||
|
handleId: JS.HandleId;
|
||||||
|
}
|
||||||
|
export type ownerFrameReturnValue = {
|
||||||
|
frameId?: Frame.FrameId;
|
||||||
|
}
|
||||||
|
export type contentFrameParameters = {
|
||||||
|
handleId: JS.HandleId;
|
||||||
|
}
|
||||||
|
export type contentFrameReturnValue = {
|
||||||
|
frameId?: Frame.FrameId;
|
||||||
|
}
|
||||||
|
export type scrollIntoViewIfNeededParameters = {
|
||||||
|
handleId: JS.HandleId;
|
||||||
|
}
|
||||||
|
export type scrollIntoViewIfNeededReturnValue = {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export module Network {
|
||||||
|
export type RequestId = string;
|
||||||
|
export type ResourceType = "document";
|
||||||
|
export interface Header {
|
||||||
|
name: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
export type Headers = Header[];
|
||||||
|
|
||||||
|
export type requestStartedPayload = {
|
||||||
|
requestId: RequestId;
|
||||||
|
frameId: Frame.FrameId;
|
||||||
|
url: string;
|
||||||
|
method: string;
|
||||||
|
resourceType: ResourceType;
|
||||||
|
headers: Headers;
|
||||||
|
isIntercepted: boolean;
|
||||||
|
isNavigation: boolean;
|
||||||
|
redirectedFrom?: RequestId;
|
||||||
|
postData?: string;
|
||||||
|
}
|
||||||
|
export type responseReceivedPayload = {
|
||||||
|
requestId: RequestId;
|
||||||
|
}
|
||||||
|
export type requestFailedPayload = {
|
||||||
|
requestId: RequestId;
|
||||||
|
errorText: string;
|
||||||
|
}
|
||||||
|
export type requestFinishedPayload = {
|
||||||
|
requestId: RequestId;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type abortRequestParameters = {
|
||||||
|
requestId: RequestId;
|
||||||
|
errorCode: string;
|
||||||
|
}
|
||||||
|
export type abortRequestReturnValue = {
|
||||||
|
}
|
||||||
|
export type fulfillRequestParameters = {
|
||||||
|
requestId: RequestId;
|
||||||
|
status: number;
|
||||||
|
headers: Headers;
|
||||||
|
contentType: string;
|
||||||
|
base64body: string;
|
||||||
|
}
|
||||||
|
export type fulfillRequestReturnValue = {
|
||||||
|
}
|
||||||
|
export type continueRequestParameters = {
|
||||||
|
requestId: RequestId;
|
||||||
|
headers?: Headers;
|
||||||
|
}
|
||||||
|
export type continueRequestReturnValue = {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Events {
|
||||||
|
"Frame.pageCreated": Frame.pageCreatedPayload;
|
||||||
|
"Frame.pageDestroyed": Frame.pageDestroyedPayload;
|
||||||
|
"Frame.attached": Frame.attachedPayload;
|
||||||
|
"Frame.navigated": Frame.navigatedPayload;
|
||||||
|
"Frame.detached": Frame.detachedPayload;
|
||||||
|
"Network.requestStarted": Network.requestStartedPayload;
|
||||||
|
"Network.responseReceived": Network.responseReceivedPayload;
|
||||||
|
"Network.requestFailed": Network.requestFailedPayload;
|
||||||
|
"Network.requestFinished": Network.requestFinishedPayload;
|
||||||
|
}
|
||||||
|
export interface CommandParameters {
|
||||||
|
"BrowserContext.create": BrowserContext.createParameters;
|
||||||
|
"BrowserContext.destroy": BrowserContext.destroyParameters;
|
||||||
|
"BrowserContext.setGeolocation": BrowserContext.setGeolocationParameters;
|
||||||
|
"Frame.createPage": Frame.createPageParameters;
|
||||||
|
"Frame.destroyPage": Frame.destroyPageParameters;
|
||||||
|
"Frame.navigate": Frame.navigateParameters;
|
||||||
|
"JS.evaluateHandle": JS.evaluateHandleParameters;
|
||||||
|
"JS.evaluate": JS.evaluateParameters;
|
||||||
|
"JS.handleAsValue": JS.handleAsValueParameters;
|
||||||
|
"JS.disposeHandle": JS.disposeHandleParameters;
|
||||||
|
"DOM.ownerFrame": DOM.ownerFrameParameters;
|
||||||
|
"DOM.contentFrame": DOM.contentFrameParameters;
|
||||||
|
"DOM.scrollIntoViewIfNeeded": DOM.scrollIntoViewIfNeededParameters;
|
||||||
|
"Network.abortRequest": Network.abortRequestParameters;
|
||||||
|
"Network.fulfillRequest": Network.fulfillRequestParameters;
|
||||||
|
"Network.continueRequest": Network.continueRequestParameters;
|
||||||
|
}
|
||||||
|
export interface CommandReturnValues {
|
||||||
|
"BrowserContext.create": BrowserContext.createReturnValue;
|
||||||
|
"BrowserContext.destroy": BrowserContext.destroyReturnValue;
|
||||||
|
"BrowserContext.setGeolocation": BrowserContext.setGeolocationReturnValue;
|
||||||
|
"Frame.createPage": Frame.createPageReturnValue;
|
||||||
|
"Frame.destroyPage": Frame.destroyPageReturnValue;
|
||||||
|
"Frame.navigate": Frame.navigateReturnValue;
|
||||||
|
"JS.evaluateHandle": JS.evaluateHandleReturnValue;
|
||||||
|
"JS.evaluate": JS.evaluateReturnValue;
|
||||||
|
"JS.handleAsValue": JS.handleAsValueReturnValue;
|
||||||
|
"JS.disposeHandle": JS.disposeHandleReturnValue;
|
||||||
|
"DOM.ownerFrame": DOM.ownerFrameReturnValue;
|
||||||
|
"DOM.contentFrame": DOM.contentFrameReturnValue;
|
||||||
|
"DOM.scrollIntoViewIfNeeded": DOM.scrollIntoViewIfNeededReturnValue;
|
||||||
|
"Network.abortRequest": Network.abortRequestReturnValue;
|
||||||
|
"Network.fulfillRequest": Network.fulfillRequestReturnValue;
|
||||||
|
"Network.continueRequest": Network.continueRequestReturnValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
2
utils/generateInternalProtocol.js
Normal file
2
utils/generateInternalProtocol.js
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
const { generateInternalProtocol } = require('./protocol-types-generator');
|
||||||
|
generateInternalProtocol();
|
||||||
|
|
@ -216,4 +216,196 @@ function firefoxTypeToString(type, indent=' ') {
|
||||||
return type['$type'];
|
return type['$type'];
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {generateChromiunProtocol, generateFirefoxProtocol, generateWebKitProtocol};
|
function generateInternalProtocol() {
|
||||||
|
const pdlPath = path.join(__dirname, '..', '..', 'src', 'protocol', 'protocol.pdl');
|
||||||
|
const tsPath = path.join(__dirname, '..', '..', 'src', 'protocol', 'protocol.ts');
|
||||||
|
const json = pdlToJSON(fs.readFileSync(pdlPath, 'utf-8'));
|
||||||
|
fs.writeFileSync(tsPath, jsonToTS(json));
|
||||||
|
console.log(`Wrote protocol.ts to ${path.relative(process.cwd(), tsPath)}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function pdlToJSON(pdl) {
|
||||||
|
const lines = pdl.split('\n');
|
||||||
|
let lineIndex = 0;
|
||||||
|
let current;
|
||||||
|
|
||||||
|
function next() {
|
||||||
|
while (lineIndex < lines.length) {
|
||||||
|
let line = lines[lineIndex++];
|
||||||
|
let indent = 0;
|
||||||
|
while (line[indent] === ' ')
|
||||||
|
indent++;
|
||||||
|
let commentIndex = line.indexOf('#');
|
||||||
|
if (commentIndex !== -1)
|
||||||
|
line = line.substring(0, commentIndex);
|
||||||
|
line = line.trim();
|
||||||
|
if (line) {
|
||||||
|
current = { line, indent };
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current = { indent: -1, line: '' };
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readDomains() {
|
||||||
|
const domains = [];
|
||||||
|
while (current.indent !== -1)
|
||||||
|
domains.push(readDomain());
|
||||||
|
return domains;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readDomain() {
|
||||||
|
const indent = current.indent;
|
||||||
|
const match = current.line.match(/^domain\s+([a-zA-Z0-9_]+)$/);
|
||||||
|
next();
|
||||||
|
const domain = {
|
||||||
|
domain: match[1],
|
||||||
|
commands: [],
|
||||||
|
events: [],
|
||||||
|
types: [],
|
||||||
|
};
|
||||||
|
while (current.indent > indent) {
|
||||||
|
if (current.line.startsWith('command'))
|
||||||
|
domain.commands.push(readCommand());
|
||||||
|
else if (current.line.startsWith('event'))
|
||||||
|
domain.events.push(readEvent());
|
||||||
|
else if (current.line.startsWith('type'))
|
||||||
|
domain.types.push(readType());
|
||||||
|
else
|
||||||
|
throw new Error(`Cannot parse: ${current.line}`);
|
||||||
|
}
|
||||||
|
return domain;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readCommand() {
|
||||||
|
const indent = current.indent;
|
||||||
|
const match = current.line.match(/^command\s+([a-zA-Z0-9_]+)$/);
|
||||||
|
next();
|
||||||
|
const command = {
|
||||||
|
name: match[1],
|
||||||
|
parameters: [],
|
||||||
|
returns: [],
|
||||||
|
};
|
||||||
|
while (current.indent > indent) {
|
||||||
|
if (current.line === 'parameters') {
|
||||||
|
const i = current.indent;
|
||||||
|
next();
|
||||||
|
while (current.indent > i)
|
||||||
|
command.parameters.push(readProperty());
|
||||||
|
} else if (current.line === 'returns') {
|
||||||
|
const i = current.indent;
|
||||||
|
next();
|
||||||
|
while (current.indent > i)
|
||||||
|
command.returns.push(readProperty());
|
||||||
|
} else {
|
||||||
|
throw new Error(`Cannot parse: ${current.line}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return command;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readEvent() {
|
||||||
|
const indent = current.indent;
|
||||||
|
const match = current.line.match(/^event\s+([a-zA-Z0-9_]+)$/);
|
||||||
|
next();
|
||||||
|
const event = {
|
||||||
|
name: match[1],
|
||||||
|
parameters: [],
|
||||||
|
returns: [],
|
||||||
|
};
|
||||||
|
while (current.indent > indent) {
|
||||||
|
if (current.line === 'parameters') {
|
||||||
|
const i = current.indent;
|
||||||
|
next();
|
||||||
|
while (current.indent > i)
|
||||||
|
event.parameters.push(readProperty());
|
||||||
|
} else {
|
||||||
|
throw new Error(`Cannot parse: ${current.line}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readType() {
|
||||||
|
const match = current.line.match(/^type\s+([a-zA-Z0-9_]+)\s+extends(.*)$/);
|
||||||
|
const type = {
|
||||||
|
id: match[1],
|
||||||
|
};
|
||||||
|
let t = type;
|
||||||
|
const words = match[2].split(' ').filter(s => !!s);
|
||||||
|
while (words[0] === 'array' && words[1] === 'of') {
|
||||||
|
t.type = 'array';
|
||||||
|
const items = {};
|
||||||
|
t.items = items;
|
||||||
|
t = items;
|
||||||
|
words.shift();
|
||||||
|
words.shift();
|
||||||
|
}
|
||||||
|
if (words.length !== 1)
|
||||||
|
throw new Error(`Cannot parse: ${current.line}`);
|
||||||
|
if (['boolean', 'integer', 'number'].includes(words[0])) {
|
||||||
|
t.type = words[0];
|
||||||
|
next();
|
||||||
|
} else if (words[0] === 'object') {
|
||||||
|
t.type = 'object';
|
||||||
|
next();
|
||||||
|
if (current.line !== 'properties')
|
||||||
|
throw new Error(`Cannot parse: ${current.line}`);
|
||||||
|
const indent = current.indent;
|
||||||
|
next();
|
||||||
|
t.properties = [];
|
||||||
|
while (current.indent > indent)
|
||||||
|
t.properties.push(readProperty());
|
||||||
|
} else if (words[0] === 'string') {
|
||||||
|
t.type = 'string';
|
||||||
|
next();
|
||||||
|
if (current.line === 'enum') {
|
||||||
|
const indent = current.indent;
|
||||||
|
next();
|
||||||
|
t.enum = [];
|
||||||
|
while (current.indent > indent) {
|
||||||
|
if (!current.line.match(/^[a-zA-Z0-9_]+$/))
|
||||||
|
throw new Error(`Cannot parse: ${current.line}`);
|
||||||
|
t.enum.push(current.line);
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.$ref = words[0];
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readProperty() {
|
||||||
|
const match = current.line.match(/^(optional)?\s*(.*)\s+([a-zA-Z0-9_]+)$/);
|
||||||
|
const prop = {
|
||||||
|
optional: !!match[1],
|
||||||
|
name: match[3],
|
||||||
|
};
|
||||||
|
let t = prop;
|
||||||
|
const words = match[2].split(' ').filter(s => !!s);
|
||||||
|
while (words[0] === 'array' && words[1] === 'of') {
|
||||||
|
t.type = 'array';
|
||||||
|
const items = {};
|
||||||
|
t.items = items;
|
||||||
|
t = items;
|
||||||
|
words.shift();
|
||||||
|
words.shift();
|
||||||
|
}
|
||||||
|
if (words.length !== 1)
|
||||||
|
throw new Error(`Cannot parse: ${current.line}`);
|
||||||
|
if (['string', 'boolean', 'integer', 'number'].includes(words[0]))
|
||||||
|
t.type = words[0];
|
||||||
|
else
|
||||||
|
t.$ref = words[0];
|
||||||
|
next();
|
||||||
|
return prop;
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
return { domains: readDomains() };
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {generateChromiunProtocol, generateFirefoxProtocol, generateWebKitProtocol, generateInternalProtocol};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue