chore: more strict type checking (#466)
This commit is contained in:
parent
d19f10ef42
commit
a1d1f26fb7
|
|
@ -51,6 +51,7 @@
|
||||||
"@types/mime": "^2.0.0",
|
"@types/mime": "^2.0.0",
|
||||||
"@types/node": "^8.10.34",
|
"@types/node": "^8.10.34",
|
||||||
"@types/pngjs": "^3.4.0",
|
"@types/pngjs": "^3.4.0",
|
||||||
|
"@types/proxy-from-env": "^1.0.0",
|
||||||
"@types/rimraf": "^2.0.2",
|
"@types/rimraf": "^2.0.2",
|
||||||
"@types/ws": "^6.0.1",
|
"@types/ws": "^6.0.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^2.6.1",
|
"@typescript-eslint/eslint-plugin": "^2.6.1",
|
||||||
|
|
|
||||||
|
|
@ -68,13 +68,13 @@ export class Accessibility {
|
||||||
async snapshot(options: {
|
async snapshot(options: {
|
||||||
interestingOnly?: boolean;
|
interestingOnly?: boolean;
|
||||||
root?: dom.ElementHandle | null;
|
root?: dom.ElementHandle | null;
|
||||||
} = {}): Promise<SerializedAXNode> {
|
} = {}): Promise<SerializedAXNode | null> {
|
||||||
const {
|
const {
|
||||||
interestingOnly = true,
|
interestingOnly = true,
|
||||||
root = null,
|
root = null,
|
||||||
} = options;
|
} = options;
|
||||||
const defaultRoot = await this._getAXTree();
|
const defaultRoot = await this._getAXTree();
|
||||||
let needle = defaultRoot;
|
let needle: AXNode | null = defaultRoot;
|
||||||
if (root) {
|
if (root) {
|
||||||
needle = await defaultRoot.findElement(root);
|
needle = await defaultRoot.findElement(root);
|
||||||
if (!needle)
|
if (!needle)
|
||||||
|
|
|
||||||
|
|
@ -103,14 +103,14 @@ export class BrowserContext {
|
||||||
if (geolocation) {
|
if (geolocation) {
|
||||||
geolocation.accuracy = geolocation.accuracy || 0;
|
geolocation.accuracy = geolocation.accuracy || 0;
|
||||||
const { longitude, latitude, accuracy } = geolocation;
|
const { longitude, latitude, accuracy } = geolocation;
|
||||||
if (longitude < -180 || longitude > 180)
|
if (longitude !== undefined && (longitude < -180 || longitude > 180))
|
||||||
throw new Error(`Invalid longitude "${longitude}": precondition -180 <= LONGITUDE <= 180 failed.`);
|
throw new Error(`Invalid longitude "${longitude}": precondition -180 <= LONGITUDE <= 180 failed.`);
|
||||||
if (latitude < -90 || latitude > 90)
|
if (latitude !== undefined && (latitude < -90 || latitude > 90))
|
||||||
throw new Error(`Invalid latitude "${latitude}": precondition -90 <= LATITUDE <= 90 failed.`);
|
throw new Error(`Invalid latitude "${latitude}": precondition -90 <= LATITUDE <= 90 failed.`);
|
||||||
if (accuracy < 0)
|
if (accuracy < 0)
|
||||||
throw new Error(`Invalid accuracy "${accuracy}": precondition 0 <= ACCURACY failed.`);
|
throw new Error(`Invalid accuracy "${accuracy}": precondition 0 <= ACCURACY failed.`);
|
||||||
}
|
}
|
||||||
this._options.geolocation = geolocation;
|
this._options.geolocation = geolocation || undefined;
|
||||||
await this._delegate.setGeolocation(geolocation);
|
await this._delegate.setGeolocation(geolocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -207,7 +207,7 @@ class CRAXNode implements accessibility.AXNode {
|
||||||
|
|
||||||
const node: {[x in keyof accessibility.SerializedAXNode]: any} = {
|
const node: {[x in keyof accessibility.SerializedAXNode]: any} = {
|
||||||
role: this._role,
|
role: this._role,
|
||||||
name: this._payload.name.value || ''
|
name: this._payload.name ? (this._payload.name.value || '') : ''
|
||||||
};
|
};
|
||||||
|
|
||||||
const userStringProperties: Array<keyof accessibility.SerializedAXNode> = [
|
const userStringProperties: Array<keyof accessibility.SerializedAXNode> = [
|
||||||
|
|
@ -286,7 +286,7 @@ class CRAXNode implements accessibility.AXNode {
|
||||||
nodeById.set(payload.nodeId, new CRAXNode(client, payload));
|
nodeById.set(payload.nodeId, new CRAXNode(client, payload));
|
||||||
for (const node of nodeById.values()) {
|
for (const node of nodeById.values()) {
|
||||||
for (const childId of node._payload.childIds || [])
|
for (const childId of node._payload.childIds || [])
|
||||||
node._children.push(nodeById.get(childId));
|
node._children.push(nodeById.get(childId)!);
|
||||||
}
|
}
|
||||||
return nodeById.values().next().value;
|
return nodeById.values().next().value;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ export class CRBrowser extends platform.EventEmitter implements Browser {
|
||||||
_targets = new Map<string, CRTarget>();
|
_targets = new Map<string, CRTarget>();
|
||||||
|
|
||||||
private _tracingRecording = false;
|
private _tracingRecording = false;
|
||||||
private _tracingPath = '';
|
private _tracingPath: string | null = '';
|
||||||
private _tracingClient: CRSession | undefined;
|
private _tracingClient: CRSession | undefined;
|
||||||
|
|
||||||
static async connect(options: CRConnectOptions): Promise<CRBrowser> {
|
static async connect(options: CRConnectOptions): Promise<CRBrowser> {
|
||||||
|
|
@ -79,20 +79,21 @@ export class CRBrowser extends platform.EventEmitter implements Browser {
|
||||||
pages: async (): Promise<Page[]> => {
|
pages: async (): Promise<Page[]> => {
|
||||||
const targets = this._allTargets().filter(target => target.browserContext() === context && target.type() === 'page');
|
const targets = this._allTargets().filter(target => target.browserContext() === context && target.type() === 'page');
|
||||||
const pages = await Promise.all(targets.map(target => target.page()));
|
const pages = await Promise.all(targets.map(target => target.page()));
|
||||||
return pages.filter(page => !!page);
|
return pages.filter(page => !!page) as Page[];
|
||||||
},
|
},
|
||||||
|
|
||||||
newPage: async (): Promise<Page> => {
|
newPage: async (): Promise<Page> => {
|
||||||
const { targetId } = await this._client.send('Target.createTarget', { url: 'about:blank', browserContextId: contextId || undefined });
|
const { targetId } = await this._client.send('Target.createTarget', { url: 'about:blank', browserContextId: contextId || undefined });
|
||||||
const target = this._targets.get(targetId);
|
const target = this._targets.get(targetId)!;
|
||||||
assert(await target._initializedPromise, 'Failed to create target for page');
|
assert(await target._initializedPromise, 'Failed to create target for page');
|
||||||
return target.page();
|
const page = await target.page();
|
||||||
|
return page!;
|
||||||
},
|
},
|
||||||
|
|
||||||
close: async (): Promise<void> => {
|
close: async (): Promise<void> => {
|
||||||
assert(contextId, 'Non-incognito profiles cannot be closed!');
|
assert(contextId, 'Non-incognito profiles cannot be closed!');
|
||||||
await this._client.send('Target.disposeBrowserContext', {browserContextId: contextId || undefined});
|
await this._client.send('Target.disposeBrowserContext', { browserContextId: contextId! });
|
||||||
this._contexts.delete(contextId);
|
this._contexts.delete(contextId!);
|
||||||
},
|
},
|
||||||
|
|
||||||
cookies: async (): Promise<network.NetworkCookie[]> => {
|
cookies: async (): Promise<network.NetworkCookie[]> => {
|
||||||
|
|
@ -172,7 +173,7 @@ export class CRBrowser extends platform.EventEmitter implements Browser {
|
||||||
async _targetCreated(event: Protocol.Target.targetCreatedPayload) {
|
async _targetCreated(event: Protocol.Target.targetCreatedPayload) {
|
||||||
const targetInfo = event.targetInfo;
|
const targetInfo = event.targetInfo;
|
||||||
const {browserContextId} = targetInfo;
|
const {browserContextId} = targetInfo;
|
||||||
const context = (browserContextId && this._contexts.has(browserContextId)) ? this._contexts.get(browserContextId) : this._defaultContext;
|
const context = (browserContextId && this._contexts.has(browserContextId)) ? this._contexts.get(browserContextId)! : this._defaultContext;
|
||||||
|
|
||||||
const target = new CRTarget(this, targetInfo, context, () => this._connection.createSession(targetInfo));
|
const target = new CRTarget(this, targetInfo, context, () => this._connection.createSession(targetInfo));
|
||||||
assert(!this._targets.has(event.targetInfo.targetId), 'Target should not exist before targetCreated');
|
assert(!this._targets.has(event.targetInfo.targetId), 'Target should not exist before targetCreated');
|
||||||
|
|
@ -183,7 +184,7 @@ export class CRBrowser extends platform.EventEmitter implements Browser {
|
||||||
}
|
}
|
||||||
|
|
||||||
async _targetDestroyed(event: { targetId: string; }) {
|
async _targetDestroyed(event: { targetId: string; }) {
|
||||||
const target = this._targets.get(event.targetId);
|
const target = this._targets.get(event.targetId)!;
|
||||||
target._initializedCallback(false);
|
target._initializedCallback(false);
|
||||||
this._targets.delete(event.targetId);
|
this._targets.delete(event.targetId);
|
||||||
target._didClose();
|
target._didClose();
|
||||||
|
|
@ -192,7 +193,7 @@ export class CRBrowser extends platform.EventEmitter implements Browser {
|
||||||
}
|
}
|
||||||
|
|
||||||
_targetInfoChanged(event: Protocol.Target.targetInfoChangedPayload) {
|
_targetInfoChanged(event: Protocol.Target.targetInfoChangedPayload) {
|
||||||
const target = this._targets.get(event.targetInfo.targetId);
|
const target = this._targets.get(event.targetInfo.targetId)!;
|
||||||
assert(target, 'target should exist before targetInfoChanged');
|
assert(target, 'target should exist before targetInfoChanged');
|
||||||
const previousURL = target.url();
|
const previousURL = target.url();
|
||||||
const wasInitialized = target._isInitialized;
|
const wasInitialized = target._isInitialized;
|
||||||
|
|
@ -242,7 +243,7 @@ export class CRBrowser extends platform.EventEmitter implements Browser {
|
||||||
}
|
}
|
||||||
|
|
||||||
browserTarget(): CRTarget {
|
browserTarget(): CRTarget {
|
||||||
return [...this._targets.values()].find(t => t.type() === 'browser');
|
return [...this._targets.values()].find(t => t.type() === 'browser')!;
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceWorker(target: CRTarget): Promise<Worker | null> {
|
serviceWorker(target: CRTarget): Promise<Worker | null> {
|
||||||
|
|
@ -280,10 +281,10 @@ export class CRBrowser extends platform.EventEmitter implements Browser {
|
||||||
assert(this._tracingClient, 'Tracing was not started.');
|
assert(this._tracingClient, 'Tracing was not started.');
|
||||||
let fulfill: (buffer: platform.BufferType) => void;
|
let fulfill: (buffer: platform.BufferType) => void;
|
||||||
const contentPromise = new Promise<platform.BufferType>(x => fulfill = x);
|
const contentPromise = new Promise<platform.BufferType>(x => fulfill = x);
|
||||||
this._tracingClient.once('Tracing.tracingComplete', event => {
|
this._tracingClient!.once('Tracing.tracingComplete', event => {
|
||||||
readProtocolStream(this._tracingClient, event.stream, this._tracingPath).then(fulfill);
|
readProtocolStream(this._tracingClient!, event.stream!, this._tracingPath).then(fulfill);
|
||||||
});
|
});
|
||||||
await this._tracingClient.send('Tracing.end');
|
await this._tracingClient!.send('Tracing.end');
|
||||||
this._tracingRecording = false;
|
this._tracingRecording = false;
|
||||||
return contentPromise;
|
return contentPromise;
|
||||||
}
|
}
|
||||||
|
|
@ -324,5 +325,5 @@ export async function createTransport(options: CRConnectOptions): Promise<Connec
|
||||||
}
|
}
|
||||||
transport = await platform.createWebSocketTransport(connectionURL);
|
transport = await platform.createWebSocketTransport(connectionURL);
|
||||||
}
|
}
|
||||||
return SlowMoTransport.wrap(transport, options.slowMo);
|
return SlowMoTransport.wrap(transport!, options.slowMo);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ export class CRConnection extends platform.EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromSession(session: CRSession): CRConnection {
|
static fromSession(session: CRSession): CRConnection {
|
||||||
return session._connection;
|
return session._connection!;
|
||||||
}
|
}
|
||||||
|
|
||||||
session(sessionId: string): CRSession | null {
|
session(sessionId: string): CRSession | null {
|
||||||
|
|
@ -84,8 +84,8 @@ export class CRConnection extends platform.EventEmitter {
|
||||||
if (this._closed)
|
if (this._closed)
|
||||||
return;
|
return;
|
||||||
this._closed = true;
|
this._closed = true;
|
||||||
this._transport.onmessage = null;
|
this._transport.onmessage = undefined;
|
||||||
this._transport.onclose = null;
|
this._transport.onclose = undefined;
|
||||||
for (const session of this._sessions.values())
|
for (const session of this._sessions.values())
|
||||||
session._onClosed();
|
session._onClosed();
|
||||||
this._sessions.clear();
|
this._sessions.clear();
|
||||||
|
|
@ -99,12 +99,12 @@ export class CRConnection extends platform.EventEmitter {
|
||||||
|
|
||||||
async createSession(targetInfo: Protocol.Target.TargetInfo): Promise<CRSession> {
|
async createSession(targetInfo: Protocol.Target.TargetInfo): Promise<CRSession> {
|
||||||
const { sessionId } = await this.rootSession.send('Target.attachToTarget', { targetId: targetInfo.targetId, flatten: true });
|
const { sessionId } = await this.rootSession.send('Target.attachToTarget', { targetId: targetInfo.targetId, flatten: true });
|
||||||
return this._sessions.get(sessionId);
|
return this._sessions.get(sessionId)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
async createBrowserSession(): Promise<CRSession> {
|
async createBrowserSession(): Promise<CRSession> {
|
||||||
const { sessionId } = await this.rootSession.send('Target.attachToBrowserTarget');
|
const { sessionId } = await this.rootSession.send('Target.attachToBrowserTarget');
|
||||||
return this._sessions.get(sessionId);
|
return this._sessions.get(sessionId)!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -113,7 +113,7 @@ export const CRSessionEvents = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export class CRSession extends platform.EventEmitter {
|
export class CRSession extends platform.EventEmitter {
|
||||||
_connection: CRConnection;
|
_connection: CRConnection | null;
|
||||||
private _callbacks = new Map<number, {resolve:(o: any) => void, reject: (e: Error) => void, error: Error, method: string}>();
|
private _callbacks = new Map<number, {resolve:(o: any) => void, reject: (e: Error) => void, error: Error, method: string}>();
|
||||||
private _targetType: string;
|
private _targetType: string;
|
||||||
private _sessionId: string;
|
private _sessionId: string;
|
||||||
|
|
@ -128,6 +128,12 @@ export class CRSession extends platform.EventEmitter {
|
||||||
this._connection = connection;
|
this._connection = connection;
|
||||||
this._targetType = targetType;
|
this._targetType = targetType;
|
||||||
this._sessionId = sessionId;
|
this._sessionId = sessionId;
|
||||||
|
|
||||||
|
this.on = super.on;
|
||||||
|
this.addListener = super.addListener;
|
||||||
|
this.off = super.removeListener;
|
||||||
|
this.removeListener = super.removeListener;
|
||||||
|
this.once = super.once;
|
||||||
}
|
}
|
||||||
|
|
||||||
send<T extends keyof Protocol.CommandParameters>(
|
send<T extends keyof Protocol.CommandParameters>(
|
||||||
|
|
@ -144,7 +150,7 @@ export class CRSession extends platform.EventEmitter {
|
||||||
|
|
||||||
_onMessage(object: { id?: number; method: string; params: any; error: { message: string; data: any; }; result?: any; }) {
|
_onMessage(object: { id?: number; method: string; params: any; error: { message: string; data: any; }; result?: any; }) {
|
||||||
if (object.id && this._callbacks.has(object.id)) {
|
if (object.id && this._callbacks.has(object.id)) {
|
||||||
const callback = this._callbacks.get(object.id);
|
const callback = this._callbacks.get(object.id)!;
|
||||||
this._callbacks.delete(object.id);
|
this._callbacks.delete(object.id);
|
||||||
if (object.error)
|
if (object.error)
|
||||||
callback.reject(createProtocolError(callback.error, callback.method, object));
|
callback.reject(createProtocolError(callback.error, callback.method, object));
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ class JSCoverage {
|
||||||
_scriptSources: Map<string, string>;
|
_scriptSources: Map<string, string>;
|
||||||
_eventListeners: RegisteredListener[];
|
_eventListeners: RegisteredListener[];
|
||||||
_resetOnNavigation: boolean;
|
_resetOnNavigation: boolean;
|
||||||
_reportAnonymousScripts: boolean;
|
_reportAnonymousScripts = false;
|
||||||
|
|
||||||
constructor(client: CRSession) {
|
constructor(client: CRSession) {
|
||||||
this._client = client;
|
this._client = client;
|
||||||
|
|
@ -238,12 +238,12 @@ class CSSCoverage {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const coverage = [];
|
const coverage: CoverageEntry[] = [];
|
||||||
for (const styleSheetId of this._stylesheetURLs.keys()) {
|
for (const styleSheetId of this._stylesheetURLs.keys()) {
|
||||||
const url = this._stylesheetURLs.get(styleSheetId);
|
const url = this._stylesheetURLs.get(styleSheetId)!;
|
||||||
const text = this._stylesheetSources.get(styleSheetId);
|
const text = this._stylesheetSources.get(styleSheetId)!;
|
||||||
const ranges = convertToDisjointRanges(styleSheetIdToCoverage.get(styleSheetId) || []);
|
const ranges = convertToDisjointRanges(styleSheetIdToCoverage.get(styleSheetId) || []);
|
||||||
coverage.push({url, ranges, text});
|
coverage.push({ url, ranges, text });
|
||||||
}
|
}
|
||||||
|
|
||||||
return coverage;
|
return coverage;
|
||||||
|
|
@ -277,7 +277,7 @@ function convertToDisjointRanges(nestedRanges: {
|
||||||
});
|
});
|
||||||
|
|
||||||
const hitCountStack = [];
|
const hitCountStack = [];
|
||||||
const results = [];
|
const results: { start: number; end: number; }[] = [];
|
||||||
let lastOffset = 0;
|
let lastOffset = 0;
|
||||||
// Run scanning line to intersect all ranges.
|
// Run scanning line to intersect all ranges.
|
||||||
for (const point of points) {
|
for (const point of points) {
|
||||||
|
|
|
||||||
|
|
@ -132,8 +132,11 @@ export class CRExecutionContext implements js.ExecutionContextDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getProperties(handle: js.JSHandle): Promise<Map<string, js.JSHandle>> {
|
async getProperties(handle: js.JSHandle): Promise<Map<string, js.JSHandle>> {
|
||||||
|
const objectId = toRemoteObject(handle).objectId;
|
||||||
|
if (!objectId)
|
||||||
|
return new Map();
|
||||||
const response = await this._client.send('Runtime.getProperties', {
|
const response = await this._client.send('Runtime.getProperties', {
|
||||||
objectId: toRemoteObject(handle).objectId,
|
objectId,
|
||||||
ownProperties: true
|
ownProperties: true
|
||||||
});
|
});
|
||||||
const result = new Map();
|
const result = new Map();
|
||||||
|
|
|
||||||
|
|
@ -154,11 +154,13 @@ export class CRNetworkManager {
|
||||||
requestId: event.requestId
|
requestId: event.requestId
|
||||||
}).catch(debugError);
|
}).catch(debugError);
|
||||||
}
|
}
|
||||||
|
if (!event.networkId)
|
||||||
|
return;
|
||||||
|
|
||||||
const requestId = event.networkId;
|
const requestId = event.networkId;
|
||||||
const interceptionId = event.requestId;
|
const interceptionId = event.requestId;
|
||||||
if (requestId && this._requestIdToRequestWillBeSentEvent.has(requestId)) {
|
|
||||||
const requestWillBeSentEvent = this._requestIdToRequestWillBeSentEvent.get(requestId);
|
const requestWillBeSentEvent = this._requestIdToRequestWillBeSentEvent.get(requestId);
|
||||||
|
if (requestWillBeSentEvent) {
|
||||||
this._onRequest(requestWillBeSentEvent, interceptionId);
|
this._onRequest(requestWillBeSentEvent, interceptionId);
|
||||||
this._requestIdToRequestWillBeSentEvent.delete(requestId);
|
this._requestIdToRequestWillBeSentEvent.delete(requestId);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -186,7 +188,7 @@ export class CRNetworkManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
_createResponse(request: InterceptableRequest, responsePayload: Protocol.Network.Response): network.Response {
|
_createResponse(request: InterceptableRequest, responsePayload: Protocol.Network.Response): network.Response {
|
||||||
const remoteAddress: network.RemoteAddress = { ip: responsePayload.remoteIPAddress, port: responsePayload.remotePort };
|
const remoteAddress: network.RemoteAddress = { ip: responsePayload.remoteIPAddress || '', port: responsePayload.remotePort || 0 };
|
||||||
const getResponseBody = async () => {
|
const getResponseBody = async () => {
|
||||||
const response = await this._client.send('Network.getResponseBody', { requestId: request._requestId });
|
const response = await this._client.send('Network.getResponseBody', { requestId: request._requestId });
|
||||||
return platform.Buffer.from(response.body, response.base64Encoded ? 'base64' : 'utf8');
|
return platform.Buffer.from(response.body, response.base64Encoded ? 'base64' : 'utf8');
|
||||||
|
|
@ -199,6 +201,7 @@ export class CRNetworkManager {
|
||||||
request.request._redirectChain.push(request.request);
|
request.request._redirectChain.push(request.request);
|
||||||
response._requestFinished(new Error('Response body is unavailable for redirect responses'));
|
response._requestFinished(new Error('Response body is unavailable for redirect responses'));
|
||||||
this._requestIdToRequest.delete(request._requestId);
|
this._requestIdToRequest.delete(request._requestId);
|
||||||
|
if (request._interceptionId)
|
||||||
this._attemptedAuthentications.delete(request._interceptionId);
|
this._attemptedAuthentications.delete(request._interceptionId);
|
||||||
this._page._frameManager.requestReceivedResponse(response);
|
this._page._frameManager.requestReceivedResponse(response);
|
||||||
this._page._frameManager.requestFinished(request.request);
|
this._page._frameManager.requestFinished(request.request);
|
||||||
|
|
@ -222,9 +225,11 @@ export class CRNetworkManager {
|
||||||
|
|
||||||
// Under certain conditions we never get the Network.responseReceived
|
// Under certain conditions we never get the Network.responseReceived
|
||||||
// event from protocol. @see https://crbug.com/883475
|
// event from protocol. @see https://crbug.com/883475
|
||||||
if (request.request.response())
|
const response = request.request.response();
|
||||||
request.request.response()._requestFinished();
|
if (response)
|
||||||
|
response._requestFinished();
|
||||||
this._requestIdToRequest.delete(request._requestId);
|
this._requestIdToRequest.delete(request._requestId);
|
||||||
|
if (request._interceptionId)
|
||||||
this._attemptedAuthentications.delete(request._interceptionId);
|
this._attemptedAuthentications.delete(request._interceptionId);
|
||||||
this._page._frameManager.requestFinished(request.request);
|
this._page._frameManager.requestFinished(request.request);
|
||||||
}
|
}
|
||||||
|
|
@ -239,32 +244,33 @@ export class CRNetworkManager {
|
||||||
if (response)
|
if (response)
|
||||||
response._requestFinished();
|
response._requestFinished();
|
||||||
this._requestIdToRequest.delete(request._requestId);
|
this._requestIdToRequest.delete(request._requestId);
|
||||||
|
if (request._interceptionId)
|
||||||
this._attemptedAuthentications.delete(request._interceptionId);
|
this._attemptedAuthentications.delete(request._interceptionId);
|
||||||
request.request._setFailureText(event.errorText);
|
request.request._setFailureText(event.errorText);
|
||||||
this._page._frameManager.requestFailed(request.request, event.canceled);
|
this._page._frameManager.requestFailed(request.request, !!event.canceled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class InterceptableRequest implements network.RequestDelegate {
|
class InterceptableRequest implements network.RequestDelegate {
|
||||||
readonly request: network.Request;
|
readonly request: network.Request;
|
||||||
_requestId: string;
|
_requestId: string;
|
||||||
_interceptionId: string;
|
_interceptionId: string | null;
|
||||||
_documentId: string;
|
_documentId: string | undefined;
|
||||||
private _client: CRSession;
|
private _client: CRSession;
|
||||||
|
|
||||||
constructor(client: CRSession, frame: frames.Frame | null, interceptionId: string, documentId: string | undefined, allowInterception: boolean, event: Protocol.Network.requestWillBeSentPayload, redirectChain: network.Request[]) {
|
constructor(client: CRSession, frame: frames.Frame | null, interceptionId: string | null, documentId: string | undefined, allowInterception: boolean, event: Protocol.Network.requestWillBeSentPayload, redirectChain: network.Request[]) {
|
||||||
this._client = client;
|
this._client = client;
|
||||||
this._requestId = event.requestId;
|
this._requestId = event.requestId;
|
||||||
this._interceptionId = interceptionId;
|
this._interceptionId = interceptionId;
|
||||||
this._documentId = documentId;
|
this._documentId = documentId;
|
||||||
|
|
||||||
this.request = new network.Request(allowInterception ? this : null, frame, redirectChain, documentId,
|
this.request = new network.Request(allowInterception ? this : null, frame, redirectChain, documentId,
|
||||||
event.request.url, event.type.toLowerCase(), event.request.method, event.request.postData, headersObject(event.request.headers));
|
event.request.url, (event.type || '').toLowerCase(), event.request.method, event.request.postData, headersObject(event.request.headers));
|
||||||
}
|
}
|
||||||
|
|
||||||
async continue(overrides: { headers?: network.Headers; } = {}) {
|
async continue(overrides: { headers?: network.Headers; } = {}) {
|
||||||
await this._client.send('Fetch.continueRequest', {
|
await this._client.send('Fetch.continueRequest', {
|
||||||
requestId: this._interceptionId,
|
requestId: this._interceptionId!,
|
||||||
headers: overrides.headers ? headersArray(overrides.headers) : undefined,
|
headers: overrides.headers ? headersArray(overrides.headers) : undefined,
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
// In certain cases, protocol will return error if the request was already canceled
|
// In certain cases, protocol will return error if the request was already canceled
|
||||||
|
|
@ -287,7 +293,7 @@ class InterceptableRequest implements network.RequestDelegate {
|
||||||
responseHeaders['content-length'] = String(platform.Buffer.byteLength(responseBody));
|
responseHeaders['content-length'] = String(platform.Buffer.byteLength(responseBody));
|
||||||
|
|
||||||
await this._client.send('Fetch.fulfillRequest', {
|
await this._client.send('Fetch.fulfillRequest', {
|
||||||
requestId: this._interceptionId,
|
requestId: this._interceptionId!,
|
||||||
responseCode: response.status || 200,
|
responseCode: response.status || 200,
|
||||||
responsePhrase: network.STATUS_TEXTS[String(response.status || 200)],
|
responsePhrase: network.STATUS_TEXTS[String(response.status || 200)],
|
||||||
responseHeaders: headersArray(responseHeaders),
|
responseHeaders: headersArray(responseHeaders),
|
||||||
|
|
@ -303,7 +309,7 @@ class InterceptableRequest implements network.RequestDelegate {
|
||||||
const errorReason = errorReasons[errorCode];
|
const errorReason = errorReasons[errorCode];
|
||||||
assert(errorReason, 'Unknown error code: ' + errorCode);
|
assert(errorReason, 'Unknown error code: ' + errorCode);
|
||||||
await this._client.send('Fetch.failRequest', {
|
await this._client.send('Fetch.failRequest', {
|
||||||
requestId: this._interceptionId,
|
requestId: this._interceptionId!,
|
||||||
errorReason
|
errorReason
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
// In certain cases, protocol will return error if the request was already canceled
|
// In certain cases, protocol will return error if the request was already canceled
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as dom from '../dom';
|
import * as dom from '../dom';
|
||||||
|
import * as js from '../javascript';
|
||||||
import * as frames from '../frames';
|
import * as frames from '../frames';
|
||||||
import { debugError, helper, RegisteredListener } from '../helper';
|
import { debugError, helper, RegisteredListener } from '../helper';
|
||||||
import * as network from '../network';
|
import * as network from '../network';
|
||||||
|
|
@ -151,7 +152,7 @@ export class CRPage implements PageDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleFrameTree(frameTree: Protocol.Page.FrameTree) {
|
_handleFrameTree(frameTree: Protocol.Page.FrameTree) {
|
||||||
this._onFrameAttached(frameTree.frame.id, frameTree.frame.parentId);
|
this._onFrameAttached(frameTree.frame.id, frameTree.frame.parentId || null);
|
||||||
this._onFrameNavigated(frameTree.frame, true);
|
this._onFrameNavigated(frameTree.frame, true);
|
||||||
if (!frameTree.childFrames)
|
if (!frameTree.childFrames)
|
||||||
return;
|
return;
|
||||||
|
|
@ -196,7 +197,7 @@ export class CRPage implements PageDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
_onExecutionContextCreated(contextPayload: Protocol.Runtime.ExecutionContextDescription) {
|
_onExecutionContextCreated(contextPayload: Protocol.Runtime.ExecutionContextDescription) {
|
||||||
const frame = this._page._frameManager.frame(contextPayload.auxData ? contextPayload.auxData.frameId : null);
|
const frame = contextPayload.auxData ? this._page._frameManager.frame(contextPayload.auxData.frameId) : null;
|
||||||
if (!frame)
|
if (!frame)
|
||||||
return;
|
return;
|
||||||
if (contextPayload.auxData && contextPayload.auxData.type === 'isolated')
|
if (contextPayload.auxData && contextPayload.auxData.type === 'isolated')
|
||||||
|
|
@ -240,7 +241,7 @@ export class CRPage implements PageDelegate {
|
||||||
// @see https://github.com/GoogleChrome/puppeteer/issues/3865
|
// @see https://github.com/GoogleChrome/puppeteer/issues/3865
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const context = this._contextIdToContext.get(event.executionContextId);
|
const context = this._contextIdToContext.get(event.executionContextId)!;
|
||||||
const values = event.args.map(arg => context._createHandle(arg));
|
const values = event.args.map(arg => context._createHandle(arg));
|
||||||
this._page._addConsoleMessage(event.type, values, toConsoleMessageLocation(event.stackTrace));
|
this._page._addConsoleMessage(event.type, values, toConsoleMessageLocation(event.stackTrace));
|
||||||
}
|
}
|
||||||
|
|
@ -252,7 +253,7 @@ export class CRPage implements PageDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
_onBindingCalled(event: Protocol.Runtime.bindingCalledPayload) {
|
_onBindingCalled(event: Protocol.Runtime.bindingCalledPayload) {
|
||||||
const context = this._contextIdToContext.get(event.executionContextId);
|
const context = this._contextIdToContext.get(event.executionContextId)!;
|
||||||
this._page._onBindingCalled(event.payload, context);
|
this._page._onBindingCalled(event.payload, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -283,7 +284,7 @@ export class CRPage implements PageDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
async _onFileChooserOpened(event: Protocol.Page.fileChooserOpenedPayload) {
|
async _onFileChooserOpened(event: Protocol.Page.fileChooserOpenedPayload) {
|
||||||
const frame = this._page._frameManager.frame(event.frameId);
|
const frame = this._page._frameManager.frame(event.frameId)!;
|
||||||
const utilityContext = await frame._utilityContext();
|
const utilityContext = await frame._utilityContext();
|
||||||
const handle = await this.adoptBackendNodeId(event.backendNodeId, utilityContext);
|
const handle = await this.adoptBackendNodeId(event.backendNodeId, utilityContext);
|
||||||
this._page._onFileChooserOpened(handle);
|
this._page._onFileChooserOpened(handle);
|
||||||
|
|
@ -458,7 +459,7 @@ export class CRPage implements PageDelegate {
|
||||||
return { width: layoutMetrics.layoutViewport.clientWidth, height: layoutMetrics.layoutViewport.clientHeight };
|
return { width: layoutMetrics.layoutViewport.clientWidth, height: layoutMetrics.layoutViewport.clientHeight };
|
||||||
}
|
}
|
||||||
|
|
||||||
async setInputFiles(handle: dom.ElementHandle, files: types.FilePayload[]): Promise<void> {
|
async setInputFiles(handle: dom.ElementHandle<HTMLInputElement>, files: types.FilePayload[]): Promise<void> {
|
||||||
await handle.evaluate(dom.setFileInputFunction, files);
|
await handle.evaluate(dom.setFileInputFunction, files);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -492,7 +493,7 @@ export class CRPage implements PageDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toRemoteObject(handle: dom.ElementHandle): Protocol.Runtime.RemoteObject {
|
function toRemoteObject(handle: js.JSHandle): Protocol.Runtime.RemoteObject {
|
||||||
return handle._remoteObject as Protocol.Runtime.RemoteObject;
|
return handle._remoteObject as Protocol.Runtime.RemoteObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,6 @@ export class CRPDF {
|
||||||
pageRanges,
|
pageRanges,
|
||||||
preferCSSPageSize
|
preferCSSPageSize
|
||||||
});
|
});
|
||||||
return await readProtocolStream(this._client, result.stream, path);
|
return await readProtocolStream(this._client, result.stream!, path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ export async function releaseObject(client: CRSession, remoteObject: Protocol.Ru
|
||||||
|
|
||||||
export async function readProtocolStream(client: CRSession, handle: string, path: string | null): Promise<platform.BufferType> {
|
export async function readProtocolStream(client: CRSession, handle: string, path: string | null): Promise<platform.BufferType> {
|
||||||
let eof = false;
|
let eof = false;
|
||||||
let fd;
|
let fd: number | undefined;
|
||||||
if (path)
|
if (path)
|
||||||
fd = await platform.openFdAsync(path, 'w');
|
fd = await platform.openFdAsync(path, 'w');
|
||||||
const bufs = [];
|
const bufs = [];
|
||||||
|
|
@ -77,16 +77,16 @@ export async function readProtocolStream(client: CRSession, handle: string, path
|
||||||
const buf = platform.Buffer.from(response.data, response.base64Encoded ? 'base64' : undefined);
|
const buf = platform.Buffer.from(response.data, response.base64Encoded ? 'base64' : undefined);
|
||||||
bufs.push(buf);
|
bufs.push(buf);
|
||||||
if (path)
|
if (path)
|
||||||
await platform.writeFdAsync(fd, buf);
|
await platform.writeFdAsync(fd!, buf);
|
||||||
}
|
}
|
||||||
if (path)
|
if (path)
|
||||||
await platform.closeFdAsync(fd);
|
await platform.closeFdAsync(fd!);
|
||||||
await client.send('IO.close', {handle});
|
await client.send('IO.close', {handle});
|
||||||
let resultBuffer = null;
|
let resultBuffer = null;
|
||||||
try {
|
try {
|
||||||
resultBuffer = platform.Buffer.concat(bufs);
|
resultBuffer = platform.Buffer.concat(bufs);
|
||||||
} finally {
|
} finally {
|
||||||
return resultBuffer;
|
return resultBuffer!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ export class CRTarget {
|
||||||
_crPage: CRPage | null = null;
|
_crPage: CRPage | null = null;
|
||||||
private _workerPromise: Promise<Worker> | null = null;
|
private _workerPromise: Promise<Worker> | null = null;
|
||||||
readonly _initializedPromise: Promise<boolean>;
|
readonly _initializedPromise: Promise<boolean>;
|
||||||
_initializedCallback: (value?: unknown) => void;
|
_initializedCallback: (success: boolean) => void = () => {};
|
||||||
_isInitialized: boolean;
|
_isInitialized: boolean;
|
||||||
|
|
||||||
static fromPage(page: Page): CRTarget {
|
static fromPage(page: Page): CRTarget {
|
||||||
|
|
@ -135,7 +135,7 @@ export class CRTarget {
|
||||||
const { openerId } = this._targetInfo;
|
const { openerId } = this._targetInfo;
|
||||||
if (!openerId)
|
if (!openerId)
|
||||||
return null;
|
return null;
|
||||||
return this._browser._targets.get(openerId);
|
return this._browser._targets.get(openerId)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
createCDPSession(): Promise<CRSession> {
|
createCDPSession(): Promise<CRSession> {
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ export class CRWorkers {
|
||||||
if (event.targetInfo.type !== 'worker')
|
if (event.targetInfo.type !== 'worker')
|
||||||
return;
|
return;
|
||||||
const url = event.targetInfo.url;
|
const url = event.targetInfo.url;
|
||||||
const session = CRConnection.fromSession(client).session(event.sessionId);
|
const session = CRConnection.fromSession(client).session(event.sessionId)!;
|
||||||
const worker = new Worker(url);
|
const worker = new Worker(url);
|
||||||
page._addWorker(event.sessionId, worker);
|
page._addWorker(event.sessionId, worker);
|
||||||
session.once('Runtime.executionContextCreated', async event => {
|
session.once('Runtime.executionContextCreated', async event => {
|
||||||
|
|
@ -36,7 +36,7 @@ export class CRWorkers {
|
||||||
});
|
});
|
||||||
// This might fail if the target is closed before we recieve all execution contexts.
|
// This might fail if the target is closed before we recieve all execution contexts.
|
||||||
session.send('Runtime.enable', {}).catch(debugError);
|
session.send('Runtime.enable', {}).catch(debugError);
|
||||||
session.on('Runtime.consoleAPICalled', event => page._addConsoleMessage(event.type, event.args.map(o => worker._existingExecutionContext._createHandle(o)), toConsoleMessageLocation(event.stackTrace)));
|
session.on('Runtime.consoleAPICalled', event => page._addConsoleMessage(event.type, event.args.map(o => worker._existingExecutionContext!._createHandle(o)), toConsoleMessageLocation(event.stackTrace)));
|
||||||
session.on('Runtime.exceptionThrown', exception => page.emit(Events.Page.PageError, exceptionToError(exception.exceptionDetails)));
|
session.on('Runtime.exceptionThrown', exception => page.emit(Events.Page.PageError, exceptionToError(exception.exceptionDetails)));
|
||||||
});
|
});
|
||||||
client.on('Target.detachedFromTarget', event => page._removeWorker(event.sessionId));
|
client.on('Target.detachedFromTarget', event => page._removeWorker(event.sessionId));
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"noImplicitAny": true
|
"strict": true
|
||||||
},
|
},
|
||||||
"extends": "../../tsconfig.json"
|
"extends": "../../tsconfig.json"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
27
src/dom.ts
27
src/dom.ts
|
|
@ -62,7 +62,7 @@ export class FrameExecutionContext extends js.ExecutionContext {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
_createHandle(remoteObject: any): js.JSHandle | null {
|
_createHandle(remoteObject: any): js.JSHandle {
|
||||||
if (this.frame._page._delegate.isElementHandle(remoteObject))
|
if (this.frame._page._delegate.isElementHandle(remoteObject))
|
||||||
return new ElementHandle(this, remoteObject);
|
return new ElementHandle(this, remoteObject);
|
||||||
return super._createHandle(remoteObject);
|
return super._createHandle(remoteObject);
|
||||||
|
|
@ -88,7 +88,7 @@ export class FrameExecutionContext extends js.ExecutionContext {
|
||||||
);
|
);
|
||||||
if (!handle.asElement())
|
if (!handle.asElement())
|
||||||
await handle.dispose();
|
await handle.dispose();
|
||||||
return handle.asElement();
|
return handle.asElement() as ElementHandle<Element>;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _$array(selector: string, scope?: ElementHandle): Promise<js.JSHandle<Element[]>> {
|
async _$array(selector: string, scope?: ElementHandle): Promise<js.JSHandle<Element[]>> {
|
||||||
|
|
@ -103,9 +103,9 @@ export class FrameExecutionContext extends js.ExecutionContext {
|
||||||
const arrayHandle = await this._$array(selector, scope);
|
const arrayHandle = await this._$array(selector, scope);
|
||||||
const properties = await arrayHandle.getProperties();
|
const properties = await arrayHandle.getProperties();
|
||||||
await arrayHandle.dispose();
|
await arrayHandle.dispose();
|
||||||
const result = [];
|
const result: ElementHandle<Element>[] = [];
|
||||||
for (const property of properties.values()) {
|
for (const property of properties.values()) {
|
||||||
const elementHandle = property.asElement();
|
const elementHandle = property.asElement() as ElementHandle<Element>;
|
||||||
if (elementHandle)
|
if (elementHandle)
|
||||||
result.push(elementHandle);
|
result.push(elementHandle);
|
||||||
else
|
else
|
||||||
|
|
@ -121,6 +121,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||||
|
|
||||||
constructor(context: FrameExecutionContext, remoteObject: any) {
|
constructor(context: FrameExecutionContext, remoteObject: any) {
|
||||||
super(context, remoteObject);
|
super(context, remoteObject);
|
||||||
|
this._context = context;
|
||||||
this._page = context.frame._page;
|
this._page = context.frame._page;
|
||||||
return helper.logPublicApiCalls('handle', this);
|
return helper.logPublicApiCalls('handle', this);
|
||||||
}
|
}
|
||||||
|
|
@ -131,7 +132,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||||
|
|
||||||
_evaluateInUtility: types.EvaluateOn<T> = async (pageFunction, ...args) => {
|
_evaluateInUtility: types.EvaluateOn<T> = async (pageFunction, ...args) => {
|
||||||
const utility = await this._context.frame._utilityContext();
|
const utility = await this._context.frame._utilityContext();
|
||||||
return utility.evaluate(pageFunction, this, ...args);
|
return utility.evaluate(pageFunction as any, this, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
async ownerFrame(): Promise<frames.Frame | null> {
|
async ownerFrame(): Promise<frames.Frame | null> {
|
||||||
|
|
@ -242,10 +243,10 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||||
const [box, border] = await Promise.all([
|
const [box, border] = await Promise.all([
|
||||||
this.boundingBox(),
|
this.boundingBox(),
|
||||||
this._evaluateInUtility((node: Node) => {
|
this._evaluateInUtility((node: Node) => {
|
||||||
if (node.nodeType !== Node.ELEMENT_NODE)
|
if (node.nodeType !== Node.ELEMENT_NODE || !node.ownerDocument || !node.ownerDocument.defaultView)
|
||||||
return { x: 0, y: 0 };
|
return { x: 0, y: 0 };
|
||||||
const style = node.ownerDocument.defaultView.getComputedStyle(node as Element);
|
const style = node.ownerDocument.defaultView.getComputedStyle(node as Element);
|
||||||
return { x: parseInt(style.borderLeftWidth, 10), y: parseInt(style.borderTopWidth, 10) };
|
return { x: parseInt(style.borderLeftWidth || '', 10), y: parseInt(style.borderTopWidth || '', 10) };
|
||||||
}).catch(debugError),
|
}).catch(debugError),
|
||||||
]);
|
]);
|
||||||
const point = { x: relativePoint.x, y: relativePoint.y };
|
const point = { x: relativePoint.x, y: relativePoint.y };
|
||||||
|
|
@ -317,7 +318,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||||
const element = node as HTMLSelectElement;
|
const element = node as HTMLSelectElement;
|
||||||
|
|
||||||
const options = Array.from(element.options);
|
const options = Array.from(element.options);
|
||||||
element.value = undefined;
|
element.value = undefined as any;
|
||||||
for (let index = 0; index < options.length; index++) {
|
for (let index = 0; index < options.length; index++) {
|
||||||
const option = options[index];
|
const option = options[index];
|
||||||
option.selected = optionsToSelect.some(optionToSelect => {
|
option.selected = optionsToSelect.some(optionToSelect => {
|
||||||
|
|
@ -382,6 +383,8 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||||
const range = element.ownerDocument.createRange();
|
const range = element.ownerDocument.createRange();
|
||||||
range.selectNodeContents(element);
|
range.selectNodeContents(element);
|
||||||
const selection = element.ownerDocument.defaultView.getSelection();
|
const selection = element.ownerDocument.defaultView.getSelection();
|
||||||
|
if (!selection)
|
||||||
|
return 'Element belongs to invisible iframe.';
|
||||||
selection.removeAllRanges();
|
selection.removeAllRanges();
|
||||||
selection.addRange(range);
|
selection.addRange(range);
|
||||||
element.focus();
|
element.focus();
|
||||||
|
|
@ -414,12 +417,12 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||||
}
|
}
|
||||||
return item;
|
return item;
|
||||||
}));
|
}));
|
||||||
await this._page._delegate.setInputFiles(this, filePayloads);
|
await this._page._delegate.setInputFiles(this as any as ElementHandle<HTMLInputElement>, filePayloads);
|
||||||
}
|
}
|
||||||
|
|
||||||
async focus() {
|
async focus() {
|
||||||
const errorMessage = await this._evaluateInUtility((element: Node) => {
|
const errorMessage = await this._evaluateInUtility((element: Node) => {
|
||||||
if (!element['focus'])
|
if (!(element as any)['focus'])
|
||||||
return 'Node is not an HTML or SVG element.';
|
return 'Node is not an HTML or SVG element.';
|
||||||
(element as HTMLElement|SVGElement).focus();
|
(element as HTMLElement|SVGElement).focus();
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -532,10 +535,10 @@ export function waitForFunctionTask(selector: string | undefined, pageFunction:
|
||||||
return innerPredicate(...args);
|
return innerPredicate(...args);
|
||||||
return innerPredicate(element, ...args);
|
return innerPredicate(element, ...args);
|
||||||
}
|
}
|
||||||
}, await context._injected(), selector, predicateBody, polling, options.timeout, ...args);
|
}, await context._injected(), selector, predicateBody, polling, options.timeout || 0, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function waitForSelectorTask(selector: string, visibility: types.Visibility | undefined, timeout: number): Task {
|
export function waitForSelectorTask(selector: string, visibility: types.Visibility, timeout: number): Task {
|
||||||
return async (context: FrameExecutionContext) => {
|
return async (context: FrameExecutionContext) => {
|
||||||
selector = normalizeSelector(selector);
|
selector = normalizeSelector(selector);
|
||||||
return context.evaluateHandle((injected: Injected, selector: string, visibility: types.Visibility, timeout: number) => {
|
return context.evaluateHandle((injected: Injected, selector: string, visibility: types.Visibility, timeout: number) => {
|
||||||
|
|
|
||||||
111
src/frames.ts
111
src/frames.ts
|
|
@ -63,6 +63,7 @@ export class FrameManager {
|
||||||
|
|
||||||
constructor(page: Page) {
|
constructor(page: Page) {
|
||||||
this._page = page;
|
this._page = page;
|
||||||
|
this._mainFrame = undefined as any as Frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
mainFrame(): Frame {
|
mainFrame(): Frame {
|
||||||
|
|
@ -86,7 +87,7 @@ export class FrameManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
frameAttached(frameId: string, parentFrameId: string | null | undefined): Frame {
|
frameAttached(frameId: string, parentFrameId: string | null | undefined): Frame {
|
||||||
const parentFrame = parentFrameId ? this._frames.get(parentFrameId) : null;
|
const parentFrame = parentFrameId ? this._frames.get(parentFrameId)! : null;
|
||||||
if (!parentFrame) {
|
if (!parentFrame) {
|
||||||
if (this._mainFrame) {
|
if (this._mainFrame) {
|
||||||
// Update frame id to retain frame identity on cross-process navigation.
|
// Update frame id to retain frame identity on cross-process navigation.
|
||||||
|
|
@ -108,7 +109,7 @@ export class FrameManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
frameCommittedNewDocumentNavigation(frameId: string, url: string, name: string, documentId: string, initial: boolean) {
|
frameCommittedNewDocumentNavigation(frameId: string, url: string, name: string, documentId: string, initial: boolean) {
|
||||||
const frame = this._frames.get(frameId);
|
const frame = this._frames.get(frameId)!;
|
||||||
for (const child of frame.childFrames())
|
for (const child of frame.childFrames())
|
||||||
this._removeFramesRecursively(child);
|
this._removeFramesRecursively(child);
|
||||||
frame._url = url;
|
frame._url = url;
|
||||||
|
|
@ -185,9 +186,10 @@ export class FrameManager {
|
||||||
|
|
||||||
requestStarted(request: network.Request) {
|
requestStarted(request: network.Request) {
|
||||||
this._inflightRequestStarted(request);
|
this._inflightRequestStarted(request);
|
||||||
if (request._documentId && request.frame() && !request.redirectChain().length) {
|
const frame = request.frame();
|
||||||
|
if (request._documentId && frame && !request.redirectChain().length) {
|
||||||
for (const watcher of this._lifecycleWatchers)
|
for (const watcher of this._lifecycleWatchers)
|
||||||
watcher._onNavigationRequest(request.frame(), request);
|
watcher._onNavigationRequest(frame, request);
|
||||||
}
|
}
|
||||||
this._page.emit(Events.Page.Request, request);
|
this._page.emit(Events.Page.Request, request);
|
||||||
}
|
}
|
||||||
|
|
@ -203,14 +205,15 @@ export class FrameManager {
|
||||||
|
|
||||||
requestFailed(request: network.Request, canceled: boolean) {
|
requestFailed(request: network.Request, canceled: boolean) {
|
||||||
this._inflightRequestFinished(request);
|
this._inflightRequestFinished(request);
|
||||||
if (request._documentId && request.frame()) {
|
const frame = request.frame();
|
||||||
const isCurrentDocument = request.frame()._lastDocumentId === request._documentId;
|
if (request._documentId && frame) {
|
||||||
|
const isCurrentDocument = frame._lastDocumentId === request._documentId;
|
||||||
if (!isCurrentDocument) {
|
if (!isCurrentDocument) {
|
||||||
let errorText = request.failure().errorText;
|
let errorText = request.failure()!.errorText;
|
||||||
if (canceled)
|
if (canceled)
|
||||||
errorText += '; maybe frame was detached?';
|
errorText += '; maybe frame was detached?';
|
||||||
for (const watcher of this._lifecycleWatchers)
|
for (const watcher of this._lifecycleWatchers)
|
||||||
watcher._onAbortedNewDocumentNavigation(request.frame(), request._documentId, errorText);
|
watcher._onAbortedNewDocumentNavigation(frame, request._documentId, errorText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this._page.emit(Events.Page.RequestFailed, request);
|
this._page.emit(Events.Page.RequestFailed, request);
|
||||||
|
|
@ -227,9 +230,9 @@ export class FrameManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _inflightRequestFinished(request: network.Request) {
|
private _inflightRequestFinished(request: network.Request) {
|
||||||
if (!request.frame() || request.url().endsWith('favicon.ico'))
|
|
||||||
return;
|
|
||||||
const frame = request.frame();
|
const frame = request.frame();
|
||||||
|
if (!frame || request.url().endsWith('favicon.ico'))
|
||||||
|
return;
|
||||||
if (!frame._inflightRequests.has(request))
|
if (!frame._inflightRequests.has(request))
|
||||||
return;
|
return;
|
||||||
frame._inflightRequests.delete(request);
|
frame._inflightRequests.delete(request);
|
||||||
|
|
@ -240,9 +243,9 @@ export class FrameManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _inflightRequestStarted(request: network.Request) {
|
private _inflightRequestStarted(request: network.Request) {
|
||||||
if (!request.frame() || request.url().endsWith('favicon.ico'))
|
|
||||||
return;
|
|
||||||
const frame = request.frame();
|
const frame = request.frame();
|
||||||
|
if (!frame || request.url().endsWith('favicon.ico'))
|
||||||
|
return;
|
||||||
frame._inflightRequests.add(request);
|
frame._inflightRequests.add(request);
|
||||||
if (frame._inflightRequests.size === 1)
|
if (frame._inflightRequests.size === 1)
|
||||||
this._stopNetworkIdleTimer(frame, 'networkidle0');
|
this._stopNetworkIdleTimer(frame, 'networkidle0');
|
||||||
|
|
@ -260,7 +263,9 @@ export class FrameManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _stopNetworkIdleTimer(frame: Frame, event: LifecycleEvent) {
|
private _stopNetworkIdleTimer(frame: Frame, event: LifecycleEvent) {
|
||||||
clearTimeout(frame._networkIdleTimers.get(event));
|
const timeoutId = frame._networkIdleTimers.get(event);
|
||||||
|
if (timeoutId)
|
||||||
|
clearTimeout(timeoutId);
|
||||||
frame._networkIdleTimers.delete(event);
|
frame._networkIdleTimers.delete(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -270,12 +275,12 @@ export class Frame {
|
||||||
readonly _firedLifecycleEvents: Set<LifecycleEvent>;
|
readonly _firedLifecycleEvents: Set<LifecycleEvent>;
|
||||||
_lastDocumentId: string;
|
_lastDocumentId: string;
|
||||||
readonly _page: Page;
|
readonly _page: Page;
|
||||||
private _parentFrame: Frame;
|
private _parentFrame: Frame | null;
|
||||||
_url = '';
|
_url = '';
|
||||||
private _detached = false;
|
private _detached = false;
|
||||||
private _contextData = new Map<ContextType, ContextData>();
|
private _contextData = new Map<ContextType, ContextData>();
|
||||||
private _childFrames = new Set<Frame>();
|
private _childFrames = new Set<Frame>();
|
||||||
_name: string;
|
_name = '';
|
||||||
_inflightRequests = new Set<network.Request>();
|
_inflightRequests = new Set<network.Request>();
|
||||||
readonly _networkIdleTimers = new Map<LifecycleEvent, NodeJS.Timer>();
|
readonly _networkIdleTimers = new Map<LifecycleEvent, NodeJS.Timer>();
|
||||||
|
|
||||||
|
|
@ -318,10 +323,10 @@ export class Frame {
|
||||||
]);
|
]);
|
||||||
if (!error) {
|
if (!error) {
|
||||||
const promises = [watcher.timeoutOrTerminationPromise];
|
const promises = [watcher.timeoutOrTerminationPromise];
|
||||||
if (navigateResult.newDocumentId) {
|
if (navigateResult!.newDocumentId) {
|
||||||
watcher.setExpectedDocumentId(navigateResult.newDocumentId, url);
|
watcher.setExpectedDocumentId(navigateResult!.newDocumentId, url);
|
||||||
promises.push(watcher.newDocumentNavigationPromise);
|
promises.push(watcher.newDocumentNavigationPromise);
|
||||||
} else if (navigateResult.isSameDocument) {
|
} else if (navigateResult!.isSameDocument) {
|
||||||
promises.push(watcher.sameDocumentNavigationPromise);
|
promises.push(watcher.sameDocumentNavigationPromise);
|
||||||
} else {
|
} else {
|
||||||
promises.push(watcher.sameDocumentNavigationPromise, watcher.newDocumentNavigationPromise);
|
promises.push(watcher.sameDocumentNavigationPromise, watcher.newDocumentNavigationPromise);
|
||||||
|
|
@ -361,7 +366,7 @@ export class Frame {
|
||||||
_context(contextType: ContextType): Promise<dom.FrameExecutionContext> {
|
_context(contextType: ContextType): Promise<dom.FrameExecutionContext> {
|
||||||
if (this._detached)
|
if (this._detached)
|
||||||
throw new Error(`Execution Context is not available in detached frame "${this.url()}" (are you trying to evaluate?)`);
|
throw new Error(`Execution Context is not available in detached frame "${this.url()}" (are you trying to evaluate?)`);
|
||||||
return this._contextData.get(contextType).contextPromise;
|
return this._contextData.get(contextType)!.contextPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
_mainContext(): Promise<dom.FrameExecutionContext> {
|
_mainContext(): Promise<dom.FrameExecutionContext> {
|
||||||
|
|
@ -505,14 +510,13 @@ export class Frame {
|
||||||
const context = await this._mainContext();
|
const context = await this._mainContext();
|
||||||
return this._raceWithCSPError(async () => {
|
return this._raceWithCSPError(async () => {
|
||||||
if (url !== null)
|
if (url !== null)
|
||||||
return (await context.evaluateHandle(addScriptUrl, url, type)).asElement();
|
return (await context.evaluateHandle(addScriptUrl, url, type)).asElement()!;
|
||||||
if (path !== null) {
|
if (path !== null) {
|
||||||
let contents = await platform.readFileAsync(path, 'utf8');
|
let contents = await platform.readFileAsync(path, 'utf8');
|
||||||
contents += '//# sourceURL=' + path.replace(/\n/g, '');
|
contents += '//# sourceURL=' + path.replace(/\n/g, '');
|
||||||
return (await context.evaluateHandle(addScriptContent, contents, type)).asElement();
|
return (await context.evaluateHandle(addScriptContent, contents, type)).asElement()!;
|
||||||
}
|
}
|
||||||
if (content !== null)
|
return (await context.evaluateHandle(addScriptContent, content!, type)).asElement()!;
|
||||||
return (await context.evaluateHandle(addScriptContent, content, type)).asElement();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
async function addScriptUrl(url: string, type: string): Promise<HTMLElement> {
|
async function addScriptUrl(url: string, type: string): Promise<HTMLElement> {
|
||||||
|
|
@ -554,16 +558,15 @@ export class Frame {
|
||||||
const context = await this._mainContext();
|
const context = await this._mainContext();
|
||||||
return this._raceWithCSPError(async () => {
|
return this._raceWithCSPError(async () => {
|
||||||
if (url !== null)
|
if (url !== null)
|
||||||
return (await context.evaluateHandle(addStyleUrl, url)).asElement();
|
return (await context.evaluateHandle(addStyleUrl, url)).asElement()!;
|
||||||
|
|
||||||
if (path !== null) {
|
if (path !== null) {
|
||||||
let contents = await platform.readFileAsync(path, 'utf8');
|
let contents = await platform.readFileAsync(path, 'utf8');
|
||||||
contents += '/*# sourceURL=' + path.replace(/\n/g, '') + '*/';
|
contents += '/*# sourceURL=' + path.replace(/\n/g, '') + '*/';
|
||||||
return (await context.evaluateHandle(addStyleContent, contents)).asElement();
|
return (await context.evaluateHandle(addStyleContent, contents)).asElement()!;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (content !== null)
|
return (await context.evaluateHandle(addStyleContent, content!)).asElement()!;
|
||||||
return (await context.evaluateHandle(addStyleContent, content)).asElement();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
async function addStyleUrl(url: string): Promise<HTMLElement> {
|
async function addStyleUrl(url: string): Promise<HTMLElement> {
|
||||||
|
|
@ -595,7 +598,7 @@ export class Frame {
|
||||||
|
|
||||||
private async _raceWithCSPError(func: () => Promise<dom.ElementHandle>): Promise<dom.ElementHandle> {
|
private async _raceWithCSPError(func: () => Promise<dom.ElementHandle>): Promise<dom.ElementHandle> {
|
||||||
const listeners: RegisteredListener[] = [];
|
const listeners: RegisteredListener[] = [];
|
||||||
let result: dom.ElementHandle | undefined;
|
let result: dom.ElementHandle;
|
||||||
let error: Error | undefined;
|
let error: Error | undefined;
|
||||||
let cspMessage: ConsoleMessage | undefined;
|
let cspMessage: ConsoleMessage | undefined;
|
||||||
const actionPromise = new Promise<dom.ElementHandle>(async resolve => {
|
const actionPromise = new Promise<dom.ElementHandle>(async resolve => {
|
||||||
|
|
@ -620,7 +623,7 @@ export class Frame {
|
||||||
throw new Error(cspMessage.text());
|
throw new Error(cspMessage.text());
|
||||||
if (error)
|
if (error)
|
||||||
throw error;
|
throw error;
|
||||||
return result;
|
return result!;
|
||||||
}
|
}
|
||||||
|
|
||||||
async click(selector: string, options?: WaitForOptions & ClickOptions) {
|
async click(selector: string, options?: WaitForOptions & ClickOptions) {
|
||||||
|
|
@ -683,17 +686,19 @@ export class Frame {
|
||||||
return Promise.reject(new Error('Unsupported target type: ' + (typeof selectorOrFunctionOrTimeout)));
|
return Promise.reject(new Error('Unsupported target type: ' + (typeof selectorOrFunctionOrTimeout)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _optionallyWaitForSelectorInUtilityContext(selector: string, options: WaitForOptions | undefined): Promise<dom.ElementHandle<Element> | null> {
|
private async _optionallyWaitForSelectorInUtilityContext(selector: string, options: WaitForOptions | undefined): Promise<dom.ElementHandle<Element>> {
|
||||||
const { timeout = this._page._timeoutSettings.timeout(), waitFor = 'visible' } = (options || {});
|
const { timeout = this._page._timeoutSettings.timeout(), waitFor = 'visible' } = (options || {});
|
||||||
let handle: dom.ElementHandle<Element> | null;
|
let handle: dom.ElementHandle<Element>;
|
||||||
if (waitFor !== 'nowait') {
|
if (waitFor !== 'nowait') {
|
||||||
handle = await this._waitForSelectorInUtilityContext(selector, waitFor, timeout);
|
const maybeHandle = await this._waitForSelectorInUtilityContext(selector, waitFor, timeout);
|
||||||
if (!handle)
|
if (!maybeHandle)
|
||||||
throw new Error('No node found for selector: ' + selectorToString(selector, waitFor));
|
throw new Error('No node found for selector: ' + selectorToString(selector, waitFor));
|
||||||
|
handle = maybeHandle;
|
||||||
} else {
|
} else {
|
||||||
const context = await this._context('utility');
|
const context = await this._context('utility');
|
||||||
handle = await context._$(selector);
|
const maybeHandle = await context._$(selector);
|
||||||
assert(handle, 'No node found for selector: ' + selector);
|
assert(maybeHandle, 'No node found for selector: ' + selector);
|
||||||
|
handle = maybeHandle!;
|
||||||
}
|
}
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
@ -742,7 +747,7 @@ export class Frame {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _scheduleRerunnableTask(task: dom.Task, contextType: ContextType, timeout?: number, title?: string): Promise<js.JSHandle> {
|
private _scheduleRerunnableTask(task: dom.Task, contextType: ContextType, timeout?: number, title?: string): Promise<js.JSHandle> {
|
||||||
const data = this._contextData.get(contextType);
|
const data = this._contextData.get(contextType)!;
|
||||||
const rerunnableTask = new RerunnableTask(data, task, timeout, title);
|
const rerunnableTask = new RerunnableTask(data, task, timeout, title);
|
||||||
data.rerunnableTasks.add(rerunnableTask);
|
data.rerunnableTasks.add(rerunnableTask);
|
||||||
if (data.context)
|
if (data.context)
|
||||||
|
|
@ -751,7 +756,7 @@ export class Frame {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setContext(contextType: ContextType, context: dom.FrameExecutionContext | null) {
|
private _setContext(contextType: ContextType, context: dom.FrameExecutionContext | null) {
|
||||||
const data = this._contextData.get(contextType);
|
const data = this._contextData.get(contextType)!;
|
||||||
data.context = context;
|
data.context = context;
|
||||||
if (context) {
|
if (context) {
|
||||||
data.contextResolveCallback.call(null, context);
|
data.contextResolveCallback.call(null, context);
|
||||||
|
|
@ -765,7 +770,7 @@ export class Frame {
|
||||||
}
|
}
|
||||||
|
|
||||||
_contextCreated(contextType: ContextType, context: dom.FrameExecutionContext) {
|
_contextCreated(contextType: ContextType, context: dom.FrameExecutionContext) {
|
||||||
const data = this._contextData.get(contextType);
|
const data = this._contextData.get(contextType)!;
|
||||||
// In case of multiple sessions to the same target, there's a race between
|
// In case of multiple sessions to the same target, there's a race between
|
||||||
// connections so we might end up creating multiple isolated worlds.
|
// connections so we might end up creating multiple isolated worlds.
|
||||||
// We can use either.
|
// We can use either.
|
||||||
|
|
@ -787,10 +792,10 @@ class RerunnableTask {
|
||||||
private _contextData: ContextData;
|
private _contextData: ContextData;
|
||||||
private _task: dom.Task;
|
private _task: dom.Task;
|
||||||
private _runCount: number;
|
private _runCount: number;
|
||||||
private _resolve: (result: js.JSHandle) => void;
|
private _resolve: (result: js.JSHandle) => void = () => {};
|
||||||
private _reject: (reason: Error) => void;
|
private _reject: (reason: Error) => void = () => {};
|
||||||
private _timeoutTimer: NodeJS.Timer;
|
private _timeoutTimer?: NodeJS.Timer;
|
||||||
private _terminated: boolean;
|
private _terminated = false;
|
||||||
|
|
||||||
constructor(data: ContextData, task: dom.Task, timeout?: number, title?: string) {
|
constructor(data: ContextData, task: dom.Task, timeout?: number, title?: string) {
|
||||||
this._contextData = data;
|
this._contextData = data;
|
||||||
|
|
@ -834,7 +839,7 @@ class RerunnableTask {
|
||||||
// If execution context has been already destroyed, `context.evaluate` will
|
// If execution context has been already destroyed, `context.evaluate` will
|
||||||
// throw an error - ignore this predicate run altogether.
|
// throw an error - ignore this predicate run altogether.
|
||||||
if (!error && await context.evaluate(s => !s, success).catch(e => true)) {
|
if (!error && await context.evaluate(s => !s, success).catch(e => true)) {
|
||||||
await success.dispose();
|
await success!.dispose();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -851,12 +856,13 @@ class RerunnableTask {
|
||||||
if (error)
|
if (error)
|
||||||
this._reject(error);
|
this._reject(error);
|
||||||
else
|
else
|
||||||
this._resolve(success);
|
this._resolve(success!);
|
||||||
|
|
||||||
this._doCleanup();
|
this._doCleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
_doCleanup() {
|
_doCleanup() {
|
||||||
|
if (this._timeoutTimer)
|
||||||
clearTimeout(this._timeoutTimer);
|
clearTimeout(this._timeoutTimer);
|
||||||
this._contextData.rerunnableTasks.delete(this);
|
this._contextData.rerunnableTasks.delete(this);
|
||||||
}
|
}
|
||||||
|
|
@ -870,13 +876,13 @@ class LifecycleWatcher {
|
||||||
private _expectedLifecycle: LifecycleEvent[];
|
private _expectedLifecycle: LifecycleEvent[];
|
||||||
private _frame: Frame;
|
private _frame: Frame;
|
||||||
private _navigationRequest: network.Request | null = null;
|
private _navigationRequest: network.Request | null = null;
|
||||||
private _sameDocumentNavigationCompleteCallback: () => void;
|
private _sameDocumentNavigationCompleteCallback: () => void = () => {};
|
||||||
private _lifecycleCallback: () => void;
|
private _lifecycleCallback: () => void = () => {};
|
||||||
private _newDocumentNavigationCompleteCallback: () => void;
|
private _newDocumentNavigationCompleteCallback: () => void = () => {};
|
||||||
private _frameDetachedCallback: (err: Error) => void;
|
private _frameDetachedCallback: (err: Error) => void = () => {};
|
||||||
private _navigationAbortedCallback: (err: Error) => void;
|
private _navigationAbortedCallback: (err: Error) => void = () => {};
|
||||||
private _maximumTimer: NodeJS.Timer;
|
private _maximumTimer?: NodeJS.Timer;
|
||||||
private _hasSameDocumentNavigation: boolean;
|
private _hasSameDocumentNavigation = false;
|
||||||
private _targetUrl: string | undefined;
|
private _targetUrl: string | undefined;
|
||||||
private _expectedDocumentId: string | undefined;
|
private _expectedDocumentId: string | undefined;
|
||||||
private _urlMatch: types.URLMatch | undefined;
|
private _urlMatch: types.URLMatch | undefined;
|
||||||
|
|
@ -971,7 +977,7 @@ class LifecycleWatcher {
|
||||||
this._checkLifecycleComplete();
|
this._checkLifecycleComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
navigationResponse(): Promise<network.Response | null> {
|
async navigationResponse(): Promise<network.Response | null> {
|
||||||
return this._navigationRequest ? this._navigationRequest._finalRequest._waitForFinished() : null;
|
return this._navigationRequest ? this._navigationRequest._finalRequest._waitForFinished() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1009,6 +1015,7 @@ class LifecycleWatcher {
|
||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
this._frame._page._frameManager._lifecycleWatchers.delete(this);
|
this._frame._page._frameManager._lifecycleWatchers.delete(this);
|
||||||
|
if (this._maximumTimer)
|
||||||
clearTimeout(this._maximumTimer);
|
clearTimeout(this._maximumTimer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,10 +46,10 @@ class Helper {
|
||||||
const method = Reflect.get(classType.prototype, methodName);
|
const method = Reflect.get(classType.prototype, methodName);
|
||||||
if (methodName === 'constructor' || typeof methodName !== 'string' || methodName.startsWith('_') || typeof method !== 'function' || method.constructor.name !== 'AsyncFunction')
|
if (methodName === 'constructor' || typeof methodName !== 'string' || methodName.startsWith('_') || typeof method !== 'function' || method.constructor.name !== 'AsyncFunction')
|
||||||
continue;
|
continue;
|
||||||
Reflect.set(classType.prototype, methodName, function(...args: any[]) {
|
Reflect.set(classType.prototype, methodName, function(this: any, ...args: any[]) {
|
||||||
const syncStack: any = {};
|
const syncStack: any = {};
|
||||||
Error.captureStackTrace(syncStack);
|
Error.captureStackTrace(syncStack);
|
||||||
return method.call(this, ...args).catch(e => {
|
return method.call(this, ...args).catch((e: any) => {
|
||||||
const stack = syncStack.stack.substring(syncStack.stack.indexOf('\n') + 1);
|
const stack = syncStack.stack.substring(syncStack.stack.indexOf('\n') + 1);
|
||||||
const clientStack = stack.substring(stack.indexOf('\n'));
|
const clientStack = stack.substring(stack.indexOf('\n'));
|
||||||
if (e instanceof Error && e.stack && !e.stack.includes(clientStack))
|
if (e instanceof Error && e.stack && !e.stack.includes(clientStack))
|
||||||
|
|
@ -112,7 +112,9 @@ class Helper {
|
||||||
predicate: Function,
|
predicate: Function,
|
||||||
timeout: number,
|
timeout: number,
|
||||||
abortPromise: Promise<Error>): Promise<any> {
|
abortPromise: Promise<Error>): Promise<any> {
|
||||||
let eventTimeout, resolveCallback, rejectCallback;
|
let eventTimeout: NodeJS.Timer;
|
||||||
|
let resolveCallback: (event: any) => void = () => {};
|
||||||
|
let rejectCallback: (error: any) => void = () => {};
|
||||||
const promise = new Promise((resolve, reject) => {
|
const promise = new Promise((resolve, reject) => {
|
||||||
resolveCallback = resolve;
|
resolveCallback = resolve;
|
||||||
rejectCallback = reject;
|
rejectCallback = reject;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"noImplicitAny": true
|
"strict": true
|
||||||
},
|
},
|
||||||
"extends": "../../tsconfig.json"
|
"extends": "../../tsconfig.json"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,10 @@ export const XPathEngine: SelectorEngine = {
|
||||||
name: 'xpath',
|
name: 'xpath',
|
||||||
|
|
||||||
create(root: SelectorRoot, targetElement: Element, type: SelectorType): string | undefined {
|
create(root: SelectorRoot, targetElement: Element, type: SelectorType): string | undefined {
|
||||||
const document = root instanceof Document ? root : root.ownerDocument;
|
const maybeDocument = root instanceof Document ? root : root.ownerDocument;
|
||||||
if (!document)
|
if (!maybeDocument)
|
||||||
return;
|
return;
|
||||||
|
const document = maybeDocument!;
|
||||||
|
|
||||||
const xpathCache = new Map<string, Element[]>();
|
const xpathCache = new Map<string, Element[]>();
|
||||||
if (type === 'notext')
|
if (type === 'notext')
|
||||||
|
|
|
||||||
|
|
@ -349,7 +349,12 @@ class Engine {
|
||||||
for (let [element, boundary] of currentStep) {
|
for (let [element, boundary] of currentStep) {
|
||||||
let next: (Element | SelectorRoot)[] = [];
|
let next: (Element | SelectorRoot)[] = [];
|
||||||
if (token.combinator === '^') {
|
if (token.combinator === '^') {
|
||||||
next = element === boundary ? [] : (parentOrRoot(element) ? [parentOrRoot(element)] : []);
|
if (element === boundary) {
|
||||||
|
next = [];
|
||||||
|
} else {
|
||||||
|
const parent = parentOrRoot(element);
|
||||||
|
next = parent ? [parent] : [];
|
||||||
|
}
|
||||||
} else if (token.combinator === '>') {
|
} else if (token.combinator === '>') {
|
||||||
boundary = element;
|
boundary = element;
|
||||||
next = this._matchChildren(element, token, all);
|
next = this._matchChildren(element, token, all);
|
||||||
|
|
@ -367,7 +372,7 @@ class Engine {
|
||||||
}
|
}
|
||||||
if (element === boundary)
|
if (element === boundary)
|
||||||
break;
|
break;
|
||||||
element = parentOrRoot(element);
|
element = parentOrRoot(element)!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const nextElement of next) {
|
for (const nextElement of next) {
|
||||||
|
|
|
||||||
|
|
@ -137,10 +137,10 @@ export class Keyboard {
|
||||||
}
|
}
|
||||||
|
|
||||||
async type(text: string, options?: { delay?: number }) {
|
async type(text: string, options?: { delay?: number }) {
|
||||||
const delay = (options && options.delay) || null;
|
const delay = (options && options.delay) || undefined;
|
||||||
for (const char of text) {
|
for (const char of text) {
|
||||||
if (keyboardLayout.keyDefinitions[char]) {
|
if (keyboardLayout.keyDefinitions[char]) {
|
||||||
await this.press(char, {delay});
|
await this.press(char, { delay });
|
||||||
} else {
|
} else {
|
||||||
if (delay)
|
if (delay)
|
||||||
await new Promise(f => setTimeout(f, delay));
|
await new Promise(f => setTimeout(f, delay));
|
||||||
|
|
|
||||||
|
|
@ -62,16 +62,16 @@ export class JSHandle<T = any> {
|
||||||
}
|
}
|
||||||
|
|
||||||
evaluate: types.EvaluateOn<T> = (pageFunction, ...args) => {
|
evaluate: types.EvaluateOn<T> = (pageFunction, ...args) => {
|
||||||
return this._context.evaluate(pageFunction, this, ...args);
|
return this._context.evaluate(pageFunction as any, this, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
evaluateHandle: types.EvaluateHandleOn<T> = (pageFunction, ...args) => {
|
evaluateHandle: types.EvaluateHandleOn<T> = (pageFunction, ...args) => {
|
||||||
return this._context.evaluateHandle(pageFunction, this, ...args);
|
return this._context.evaluateHandle(pageFunction as any, this, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getProperty(propertyName: string): Promise<JSHandle | null> {
|
async getProperty(propertyName: string): Promise<JSHandle | null> {
|
||||||
const objectHandle = await this.evaluateHandle((object, propertyName) => {
|
const objectHandle = await this.evaluateHandle((object: any, propertyName) => {
|
||||||
const result = {__proto__: null};
|
const result: any = {__proto__: null};
|
||||||
result[propertyName] = object[propertyName];
|
result[propertyName] = object[propertyName];
|
||||||
return result;
|
return result;
|
||||||
}, propertyName);
|
}, propertyName);
|
||||||
|
|
|
||||||
|
|
@ -101,17 +101,17 @@ export class Request {
|
||||||
private _url: string;
|
private _url: string;
|
||||||
private _resourceType: string;
|
private _resourceType: string;
|
||||||
private _method: string;
|
private _method: string;
|
||||||
private _postData: string;
|
private _postData: string | undefined;
|
||||||
private _headers: Headers;
|
private _headers: Headers;
|
||||||
private _frame: frames.Frame;
|
private _frame: frames.Frame | null;
|
||||||
private _waitForResponsePromise: Promise<Response>;
|
private _waitForResponsePromise: Promise<Response>;
|
||||||
private _waitForResponsePromiseCallback: (value?: Response) => void;
|
private _waitForResponsePromiseCallback: (value: Response) => void = () => {};
|
||||||
private _waitForFinishedPromise: Promise<Response | undefined>;
|
private _waitForFinishedPromise: Promise<Response | null>;
|
||||||
private _waitForFinishedPromiseCallback: (value?: Response | undefined) => void;
|
private _waitForFinishedPromiseCallback: (value: Response | null) => void = () => {};
|
||||||
private _interceptionHandled = false;
|
private _interceptionHandled = false;
|
||||||
|
|
||||||
constructor(delegate: RequestDelegate | null, frame: frames.Frame | null, redirectChain: Request[], documentId: string,
|
constructor(delegate: RequestDelegate | null, frame: frames.Frame | null, redirectChain: Request[], documentId: string | undefined,
|
||||||
url: string, resourceType: string, method: string, postData: string, headers: Headers) {
|
url: string, resourceType: string, method: string, postData: string | undefined, headers: Headers) {
|
||||||
this._delegate = delegate;
|
this._delegate = delegate;
|
||||||
this._frame = frame;
|
this._frame = frame;
|
||||||
this._redirectChain = redirectChain;
|
this._redirectChain = redirectChain;
|
||||||
|
|
@ -130,7 +130,7 @@ export class Request {
|
||||||
|
|
||||||
_setFailureText(failureText: string) {
|
_setFailureText(failureText: string) {
|
||||||
this._failureText = failureText;
|
this._failureText = failureText;
|
||||||
this._waitForFinishedPromiseCallback();
|
this._waitForFinishedPromiseCallback(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
url(): string {
|
url(): string {
|
||||||
|
|
@ -157,7 +157,7 @@ export class Request {
|
||||||
return this._response;
|
return this._response;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _waitForFinished(): Promise<Response | undefined> {
|
async _waitForFinished(): Promise<Response | null> {
|
||||||
return this._waitForFinishedPromise;
|
return this._waitForFinishedPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -200,7 +200,7 @@ export class Request {
|
||||||
assert(this._delegate, 'Request Interception is not enabled!');
|
assert(this._delegate, 'Request Interception is not enabled!');
|
||||||
assert(!this._interceptionHandled, 'Request is already handled!');
|
assert(!this._interceptionHandled, 'Request is already handled!');
|
||||||
this._interceptionHandled = true;
|
this._interceptionHandled = true;
|
||||||
await this._delegate.abort(errorCode);
|
await this._delegate!.abort(errorCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
async fulfill(response: { status: number; headers: Headers; contentType: string; body: (string | platform.BufferType); }) { // Mocking responses for dataURL requests is not currently supported.
|
async fulfill(response: { status: number; headers: Headers; contentType: string; body: (string | platform.BufferType); }) { // Mocking responses for dataURL requests is not currently supported.
|
||||||
|
|
@ -209,7 +209,7 @@ export class Request {
|
||||||
assert(this._delegate, 'Request Interception is not enabled!');
|
assert(this._delegate, 'Request Interception is not enabled!');
|
||||||
assert(!this._interceptionHandled, 'Request is already handled!');
|
assert(!this._interceptionHandled, 'Request is already handled!');
|
||||||
this._interceptionHandled = true;
|
this._interceptionHandled = true;
|
||||||
await this._delegate.fulfill(response);
|
await this._delegate!.fulfill(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
async continue(overrides: { headers?: { [key: string]: string } } = {}) {
|
async continue(overrides: { headers?: { [key: string]: string } } = {}) {
|
||||||
|
|
@ -218,7 +218,7 @@ export class Request {
|
||||||
return;
|
return;
|
||||||
assert(this._delegate, 'Request Interception is not enabled!');
|
assert(this._delegate, 'Request Interception is not enabled!');
|
||||||
assert(!this._interceptionHandled, 'Request is already handled!');
|
assert(!this._interceptionHandled, 'Request is already handled!');
|
||||||
await this._delegate.continue(overrides);
|
await this._delegate!.continue(overrides);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
17
src/page.ts
17
src/page.ts
|
|
@ -65,7 +65,7 @@ export interface PageDelegate {
|
||||||
getOwnerFrame(handle: dom.ElementHandle): Promise<frames.Frame | null>;
|
getOwnerFrame(handle: dom.ElementHandle): Promise<frames.Frame | null>;
|
||||||
getContentQuads(handle: dom.ElementHandle): Promise<types.Quad[] | null>;
|
getContentQuads(handle: dom.ElementHandle): Promise<types.Quad[] | null>;
|
||||||
layoutViewport(): Promise<{ width: number, height: number }>;
|
layoutViewport(): Promise<{ width: number, height: number }>;
|
||||||
setInputFiles(handle: dom.ElementHandle, files: types.FilePayload[]): Promise<void>;
|
setInputFiles(handle: dom.ElementHandle<HTMLInputElement>, files: types.FilePayload[]): Promise<void>;
|
||||||
getBoundingBox(handle: dom.ElementHandle): Promise<types.Rect | null>;
|
getBoundingBox(handle: dom.ElementHandle): Promise<types.Rect | null>;
|
||||||
|
|
||||||
getAccessibilityTree(): Promise<accessibility.AXNode>;
|
getAccessibilityTree(): Promise<accessibility.AXNode>;
|
||||||
|
|
@ -114,7 +114,9 @@ export class Page extends platform.EventEmitter {
|
||||||
constructor(delegate: PageDelegate, browserContext: BrowserContext) {
|
constructor(delegate: PageDelegate, browserContext: BrowserContext) {
|
||||||
super();
|
super();
|
||||||
this._delegate = delegate;
|
this._delegate = delegate;
|
||||||
|
this._closedCallback = () => {};
|
||||||
this._closedPromise = new Promise(f => this._closedCallback = f);
|
this._closedPromise = new Promise(f => this._closedCallback = f);
|
||||||
|
this._disconnectedCallback = () => {};
|
||||||
this._disconnectedPromise = new Promise(f => this._disconnectedCallback = f);
|
this._disconnectedPromise = new Promise(f => this._disconnectedCallback = f);
|
||||||
this._browserContext = browserContext;
|
this._browserContext = browserContext;
|
||||||
this._state = {
|
this._state = {
|
||||||
|
|
@ -160,7 +162,7 @@ export class Page extends platform.EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
async _onFileChooserOpened(handle: dom.ElementHandle) {
|
async _onFileChooserOpened(handle: dom.ElementHandle) {
|
||||||
const multiple = await handle.evaluate((element: HTMLInputElement) => !!element.multiple);
|
const multiple = await handle.evaluate(element => !!(element as HTMLInputElement).multiple);
|
||||||
if (!this.listenerCount(Events.Page.FileChooser)) {
|
if (!this.listenerCount(Events.Page.FileChooser)) {
|
||||||
await handle.dispose();
|
await handle.dispose();
|
||||||
return;
|
return;
|
||||||
|
|
@ -197,10 +199,10 @@ export class Page extends platform.EventEmitter {
|
||||||
return this.mainFrame().waitForSelector(selector, options);
|
return this.mainFrame().waitForSelector(selector, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _createSelector(name: string, handle: dom.ElementHandle<Element>): Promise<string> {
|
async _createSelector(name: string, handle: dom.ElementHandle<Element>): Promise<string | undefined> {
|
||||||
const mainContext = await this.mainFrame()._mainContext();
|
const mainContext = await this.mainFrame()._mainContext();
|
||||||
return mainContext.evaluate((injected: Injected, target: Element, name: string) => {
|
return mainContext.evaluate((injected: Injected, target: Element, name: string) => {
|
||||||
return injected.engines.get(name).create(document.documentElement, target);
|
return injected.engines.get(name)!.create(document.documentElement, target);
|
||||||
}, await mainContext._injected(), handle, name);
|
}, await mainContext._injected(), handle, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -270,7 +272,7 @@ export class Page extends platform.EventEmitter {
|
||||||
const {name, seq, args} = JSON.parse(payload);
|
const {name, seq, args} = JSON.parse(payload);
|
||||||
let expression = null;
|
let expression = null;
|
||||||
try {
|
try {
|
||||||
const result = await this._pageBindings.get(name)(...args);
|
const result = await this._pageBindings.get(name)!(...args);
|
||||||
expression = helper.evaluationString(deliverResult, name, seq, result);
|
expression = helper.evaluationString(deliverResult, name, seq, result);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof Error)
|
if (error instanceof Error)
|
||||||
|
|
@ -493,7 +495,7 @@ export class Page extends platform.EventEmitter {
|
||||||
return this.mainFrame().type(selector, text, options);
|
return this.mainFrame().type(selector, text, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
async waitFor(selectorOrFunctionOrTimeout: (string | number | Function), options?: types.WaitForFunctionOptions & { visibility?: types.Visibility }, ...args: any[]): Promise<js.JSHandle> {
|
async waitFor(selectorOrFunctionOrTimeout: (string | number | Function), options?: types.WaitForFunctionOptions & { visibility?: types.Visibility }, ...args: any[]): Promise<js.JSHandle | null> {
|
||||||
return this.mainFrame().waitFor(selectorOrFunctionOrTimeout, options, ...args);
|
return this.mainFrame().waitFor(selectorOrFunctionOrTimeout, options, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -531,10 +533,11 @@ export class Worker {
|
||||||
private _url: string;
|
private _url: string;
|
||||||
private _executionContextPromise: Promise<js.ExecutionContext>;
|
private _executionContextPromise: Promise<js.ExecutionContext>;
|
||||||
private _executionContextCallback: (value?: js.ExecutionContext) => void;
|
private _executionContextCallback: (value?: js.ExecutionContext) => void;
|
||||||
_existingExecutionContext: js.ExecutionContext | null;
|
_existingExecutionContext: js.ExecutionContext | null = null;
|
||||||
|
|
||||||
constructor(url: string) {
|
constructor(url: string) {
|
||||||
this._url = url;
|
this._url = url;
|
||||||
|
this._executionContextCallback = () => {};
|
||||||
this._executionContextPromise = new Promise(x => this._executionContextCallback = x);
|
this._executionContextPromise = new Promise(x => this._executionContextCallback = x);
|
||||||
return helper.logPublicApiCalls('worker', this);
|
return helper.logPublicApiCalls('worker', this);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,9 @@ export const isNode = typeof process === 'object' && !!process && typeof process
|
||||||
|
|
||||||
export function promisify(nodeFunction: Function): Function {
|
export function promisify(nodeFunction: Function): Function {
|
||||||
assert(isNode);
|
assert(isNode);
|
||||||
function promisified(...args) {
|
function promisified(...args: any[]) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
function callback(err, ...result) {
|
function callback(err: any, ...result: any[]) {
|
||||||
if (err)
|
if (err)
|
||||||
return reject(err);
|
return reject(err);
|
||||||
if (result.length === 1)
|
if (result.length === 1)
|
||||||
|
|
@ -200,7 +200,7 @@ export async function closeFdAsync(fd: number): Promise<void> {
|
||||||
return await promisify(nodeFS.close)(fd);
|
return await promisify(nodeFS.close)(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMimeType(file: string): string {
|
export function getMimeType(file: string): string | null {
|
||||||
assertFileAccess();
|
assertFileAccess();
|
||||||
return mime.getType(file);
|
return mime.getType(file);
|
||||||
}
|
}
|
||||||
|
|
@ -228,7 +228,7 @@ export function pngToJpeg(buffer: Buffer): Buffer {
|
||||||
|
|
||||||
function nodeFetch(url: string): Promise<string> {
|
function nodeFetch(url: string): Promise<string> {
|
||||||
let resolve: (url: string) => void;
|
let resolve: (url: string) => void;
|
||||||
let reject: (e: Error) => void;
|
let reject: (e: Error) => void = () => {};
|
||||||
const promise = new Promise<string>((res, rej) => { resolve = res; reject = rej; });
|
const promise = new Promise<string>((res, rej) => { resolve = res; reject = rej; });
|
||||||
|
|
||||||
const endpointURL = new URL(url);
|
const endpointURL = new URL(url);
|
||||||
|
|
|
||||||
|
|
@ -29,10 +29,10 @@ export class Screenshotter {
|
||||||
this._page = page;
|
this._page = page;
|
||||||
|
|
||||||
const browserContext = page.browserContext();
|
const browserContext = page.browserContext();
|
||||||
this._queue = browserContext[taskQueueSymbol];
|
this._queue = (browserContext as any)[taskQueueSymbol];
|
||||||
if (!this._queue) {
|
if (!this._queue) {
|
||||||
this._queue = new TaskQueue();
|
this._queue = new TaskQueue();
|
||||||
browserContext[taskQueueSymbol] = this._queue;
|
(browserContext as any)[taskQueueSymbol] = this._queue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -45,7 +45,7 @@ export class Screenshotter {
|
||||||
if (!viewport) {
|
if (!viewport) {
|
||||||
viewportSize = await this._page.evaluate(() => {
|
viewportSize = await this._page.evaluate(() => {
|
||||||
if (!document.body || !document.documentElement)
|
if (!document.body || !document.documentElement)
|
||||||
return null;
|
return;
|
||||||
return {
|
return {
|
||||||
width: Math.max(document.body.offsetWidth, document.documentElement.offsetWidth),
|
width: Math.max(document.body.offsetWidth, document.documentElement.offsetWidth),
|
||||||
height: Math.max(document.body.offsetHeight, document.documentElement.offsetHeight),
|
height: Math.max(document.body.offsetHeight, document.documentElement.offsetHeight),
|
||||||
|
|
@ -79,13 +79,13 @@ export class Screenshotter {
|
||||||
options.clip = trimClipToViewport(viewport, options.clip);
|
options.clip = trimClipToViewport(viewport, options.clip);
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await this._screenshot(format, options, overridenViewport || viewport);
|
const result = await this._screenshot(format, options, (overridenViewport || viewport)!);
|
||||||
|
|
||||||
if (overridenViewport) {
|
if (overridenViewport) {
|
||||||
if (viewport)
|
if (viewport)
|
||||||
await this._page.setViewport(viewport);
|
await this._page.setViewport(viewport);
|
||||||
else
|
else
|
||||||
await this._page._delegate.resetViewport(viewportSize);
|
await this._page._delegate.resetViewport(viewportSize!);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}).catch(rewriteError);
|
}).catch(rewriteError);
|
||||||
|
|
@ -97,12 +97,15 @@ export class Screenshotter {
|
||||||
return this._queue.postTask(async () => {
|
return this._queue.postTask(async () => {
|
||||||
let overridenViewport: types.Viewport | undefined;
|
let overridenViewport: types.Viewport | undefined;
|
||||||
|
|
||||||
let boundingBox = await this._page._delegate.getBoundingBoxForScreenshot(handle);
|
let maybeBoundingBox = await this._page._delegate.getBoundingBoxForScreenshot(handle);
|
||||||
assert(boundingBox, 'Node is either not visible or not an HTMLElement');
|
assert(maybeBoundingBox, 'Node is either not visible or not an HTMLElement');
|
||||||
|
let boundingBox = maybeBoundingBox!;
|
||||||
assert(boundingBox.width !== 0, 'Node has 0 width.');
|
assert(boundingBox.width !== 0, 'Node has 0 width.');
|
||||||
assert(boundingBox.height !== 0, 'Node has 0 height.');
|
assert(boundingBox.height !== 0, 'Node has 0 height.');
|
||||||
boundingBox = enclosingIntRect(boundingBox);
|
boundingBox = enclosingIntRect(boundingBox);
|
||||||
const viewport = this._page.viewport();
|
|
||||||
|
// TODO: viewport may be null here.
|
||||||
|
const viewport = this._page.viewport()!;
|
||||||
|
|
||||||
if (!this._page._delegate.canScreenshotOutsideViewport()) {
|
if (!this._page._delegate.canScreenshotOutsideViewport()) {
|
||||||
if (boundingBox.width > viewport.width || boundingBox.height > viewport.height) {
|
if (boundingBox.width > viewport.width || boundingBox.height > viewport.height) {
|
||||||
|
|
@ -115,7 +118,9 @@ export class Screenshotter {
|
||||||
}
|
}
|
||||||
|
|
||||||
await handle.scrollIntoViewIfNeeded();
|
await handle.scrollIntoViewIfNeeded();
|
||||||
boundingBox = enclosingIntRect(await this._page._delegate.getBoundingBoxForScreenshot(handle));
|
maybeBoundingBox = await this._page._delegate.getBoundingBoxForScreenshot(handle);
|
||||||
|
assert(maybeBoundingBox, 'Node is either not visible or not an HTMLElement');
|
||||||
|
boundingBox = enclosingIntRect(maybeBoundingBox!);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!overridenViewport)
|
if (!overridenViewport)
|
||||||
|
|
@ -159,7 +164,7 @@ class TaskQueue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function trimClipToViewport(viewport: types.Viewport | null, clip: types.Rect | null): types.Rect | null {
|
function trimClipToViewport(viewport: types.Viewport | null, clip: types.Rect | undefined): types.Rect | undefined {
|
||||||
if (!clip || !viewport)
|
if (!clip || !viewport)
|
||||||
return clip;
|
return clip;
|
||||||
const p1 = { x: Math.min(clip.x, viewport.width), y: Math.min(clip.y, viewport.height) };
|
const p1 = { x: Math.min(clip.x, viewport.width), y: Math.min(clip.y, viewport.height) };
|
||||||
|
|
|
||||||
|
|
@ -56,12 +56,12 @@ export class BrowserFetcher {
|
||||||
|
|
||||||
canDownload(revision: string = this._preferredRevision): Promise<boolean> {
|
canDownload(revision: string = this._preferredRevision): Promise<boolean> {
|
||||||
const url = this._params(this._platform, revision).downloadUrl;
|
const url = this._params(this._platform, revision).downloadUrl;
|
||||||
let resolve;
|
let resolve: (result: boolean) => void = () => {};
|
||||||
const promise = new Promise<boolean>(x => resolve = x);
|
const promise = new Promise<boolean>(x => resolve = x);
|
||||||
const request = httpRequest(url, 'HEAD', response => {
|
const request = httpRequest(url, 'HEAD', response => {
|
||||||
resolve(response.statusCode === 200);
|
resolve(response.statusCode === 200);
|
||||||
});
|
});
|
||||||
request.on('error', error => {
|
request.on('error', (error: any) => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
resolve(false);
|
resolve(false);
|
||||||
});
|
});
|
||||||
|
|
@ -92,8 +92,8 @@ export class BrowserFetcher {
|
||||||
async localRevisions(): Promise<string[]> {
|
async localRevisions(): Promise<string[]> {
|
||||||
if (!await existsAsync(this._downloadsFolder))
|
if (!await existsAsync(this._downloadsFolder))
|
||||||
return [];
|
return [];
|
||||||
const fileNames = await readdirAsync(this._downloadsFolder);
|
const fileNames: string[] = await readdirAsync(this._downloadsFolder);
|
||||||
return fileNames.map(fileName => parseFolderPath(fileName)).filter(entry => entry && entry.platform === this._platform).map(entry => entry.revision);
|
return fileNames.map(fileName => parseFolderPath(fileName)).filter(entry => entry && entry.platform === this._platform).map(entry => entry!.revision);
|
||||||
}
|
}
|
||||||
|
|
||||||
async remove(revision: string = this._preferredRevision) {
|
async remove(revision: string = this._preferredRevision) {
|
||||||
|
|
@ -123,8 +123,9 @@ function parseFolderPath(folderPath: string): { platform: string; revision: stri
|
||||||
return {platform, revision};
|
return {platform, revision};
|
||||||
}
|
}
|
||||||
|
|
||||||
function downloadFile(url: string, destinationPath: string, progressCallback: OnProgressCallback | null): Promise<any> {
|
function downloadFile(url: string, destinationPath: string, progressCallback: OnProgressCallback | undefined): Promise<any> {
|
||||||
let fulfill, reject;
|
let fulfill: () => void = () => {};
|
||||||
|
let reject: (error: any) => void = () => {};
|
||||||
let downloadedBytes = 0;
|
let downloadedBytes = 0;
|
||||||
let totalBytes = 0;
|
let totalBytes = 0;
|
||||||
|
|
||||||
|
|
@ -146,12 +147,12 @@ function downloadFile(url: string, destinationPath: string, progressCallback: On
|
||||||
if (progressCallback)
|
if (progressCallback)
|
||||||
response.on('data', onData);
|
response.on('data', onData);
|
||||||
});
|
});
|
||||||
request.on('error', error => reject(error));
|
request.on('error', (error: any) => reject(error));
|
||||||
return promise;
|
return promise;
|
||||||
|
|
||||||
function onData(chunk) {
|
function onData(chunk: string) {
|
||||||
downloadedBytes += chunk.length;
|
downloadedBytes += chunk.length;
|
||||||
progressCallback(downloadedBytes, totalBytes);
|
progressCallback!(downloadedBytes, totalBytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -186,7 +187,7 @@ function httpRequest(url: string, method: string, response: (r: any) => void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const requestCallback = res => {
|
const requestCallback = (res: any) => {
|
||||||
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location)
|
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location)
|
||||||
httpRequest(res.headers.location, method, response);
|
httpRequest(res.headers.location, method, response);
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,7 @@ export class CRPlaywright implements Playwright {
|
||||||
const usePipe = chromeArguments.includes('--remote-debugging-pipe');
|
const usePipe = chromeArguments.includes('--remote-debugging-pipe');
|
||||||
|
|
||||||
const { launchedProcess, gracefullyClose } = await launchProcess({
|
const { launchedProcess, gracefullyClose } = await launchProcess({
|
||||||
executablePath: chromeExecutable,
|
executablePath: chromeExecutable!,
|
||||||
args: chromeArguments,
|
args: chromeArguments,
|
||||||
env,
|
env,
|
||||||
handleSIGINT,
|
handleSIGINT,
|
||||||
|
|
@ -152,7 +152,7 @@ export class CRPlaywright implements Playwright {
|
||||||
handleSIGHUP,
|
handleSIGHUP,
|
||||||
dumpio,
|
dumpio,
|
||||||
pipe: usePipe,
|
pipe: usePipe,
|
||||||
tempDir: temporaryUserDataDir,
|
tempDir: temporaryUserDataDir || undefined,
|
||||||
attemptToGracefullyClose: async () => {
|
attemptToGracefullyClose: async () => {
|
||||||
if (!connectOptions)
|
if (!connectOptions)
|
||||||
return Promise.reject();
|
return Promise.reject();
|
||||||
|
|
@ -246,9 +246,9 @@ export class CRPlaywright implements Playwright {
|
||||||
...defaultOptions,
|
...defaultOptions,
|
||||||
...options,
|
...options,
|
||||||
};
|
};
|
||||||
assert(!!(downloadURLs as any)[options.platform], 'Unsupported platform: ' + options.platform);
|
assert(!!(downloadURLs as any)[options.platform!], 'Unsupported platform: ' + options.platform);
|
||||||
|
|
||||||
return new BrowserFetcher(options.path, options.platform, this._revision, (platform: string, revision: string) => {
|
return new BrowserFetcher(options.path!, options.platform!, this._revision, (platform: string, revision: string) => {
|
||||||
let archiveName = '';
|
let archiveName = '';
|
||||||
let executablePath = '';
|
let executablePath = '';
|
||||||
if (platform === 'linux') {
|
if (platform === 'linux') {
|
||||||
|
|
|
||||||
|
|
@ -152,7 +152,7 @@ export class FFPlaywright implements Playwright {
|
||||||
handleSIGHUP,
|
handleSIGHUP,
|
||||||
dumpio,
|
dumpio,
|
||||||
pipe: false,
|
pipe: false,
|
||||||
tempDir: temporaryProfileDir,
|
tempDir: temporaryProfileDir || undefined,
|
||||||
attemptToGracefullyClose: async () => {
|
attemptToGracefullyClose: async () => {
|
||||||
if (!connectOptions)
|
if (!connectOptions)
|
||||||
return Promise.reject();
|
return Promise.reject();
|
||||||
|
|
@ -232,9 +232,9 @@ export class FFPlaywright implements Playwright {
|
||||||
...defaultOptions,
|
...defaultOptions,
|
||||||
...options,
|
...options,
|
||||||
};
|
};
|
||||||
assert(!!downloadURLs[options.platform], 'Unsupported platform: ' + options.platform);
|
assert(!!(downloadURLs as any)[options.platform!], 'Unsupported platform: ' + options.platform);
|
||||||
|
|
||||||
return new BrowserFetcher(options.path, options.platform, this._revision, (platform: string, revision: string) => {
|
return new BrowserFetcher(options.path!, options.platform!, this._revision, (platform: string, revision: string) => {
|
||||||
let executablePath = '';
|
let executablePath = '';
|
||||||
if (platform === 'linux')
|
if (platform === 'linux')
|
||||||
executablePath = path.join('firefox', 'firefox');
|
executablePath = path.join('firefox', 'firefox');
|
||||||
|
|
@ -243,7 +243,7 @@ export class FFPlaywright implements Playwright {
|
||||||
else if (platform === 'win32' || platform === 'win64')
|
else if (platform === 'win32' || platform === 'win64')
|
||||||
executablePath = path.join('firefox', 'firefox.exe');
|
executablePath = path.join('firefox', 'firefox.exe');
|
||||||
return {
|
return {
|
||||||
downloadUrl: util.format(downloadURLs[platform], options.host, revision),
|
downloadUrl: util.format((downloadURLs as any)[platform], options.host, revision),
|
||||||
executablePath
|
executablePath
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
@ -465,8 +465,8 @@ const DEFAULT_PREFERENCES = {
|
||||||
|
|
||||||
async function createProfile(extraPrefs?: object): Promise<string> {
|
async function createProfile(extraPrefs?: object): Promise<string> {
|
||||||
const profilePath = await mkdtempAsync(path.join(os.tmpdir(), 'playwright_dev_firefox_profile-'));
|
const profilePath = await mkdtempAsync(path.join(os.tmpdir(), 'playwright_dev_firefox_profile-'));
|
||||||
const prefsJS = [];
|
const prefsJS: string[] = [];
|
||||||
const userJS = [];
|
const userJS: string[] = [];
|
||||||
|
|
||||||
const prefs = { ...DEFAULT_PREFERENCES, ...extraPrefs };
|
const prefs = { ...DEFAULT_PREFERENCES, ...extraPrefs };
|
||||||
for (const [key, value] of Object.entries(prefs))
|
for (const [key, value] of Object.entries(prefs))
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import { debugError, helper, RegisteredListener } from '../helper';
|
||||||
import { ConnectionTransport } from '../transport';
|
import { ConnectionTransport } from '../transport';
|
||||||
|
|
||||||
export class PipeTransport implements ConnectionTransport {
|
export class PipeTransport implements ConnectionTransport {
|
||||||
private _pipeWrite: NodeJS.WritableStream;
|
private _pipeWrite: NodeJS.WritableStream | null;
|
||||||
private _pendingMessage = '';
|
private _pendingMessage = '';
|
||||||
private _eventListeners: RegisteredListener[];
|
private _eventListeners: RegisteredListener[];
|
||||||
onmessage?: (message: string) => void;
|
onmessage?: (message: string) => void;
|
||||||
|
|
@ -36,13 +36,13 @@ export class PipeTransport implements ConnectionTransport {
|
||||||
helper.addEventListener(pipeRead, 'error', debugError),
|
helper.addEventListener(pipeRead, 'error', debugError),
|
||||||
helper.addEventListener(pipeWrite, 'error', debugError),
|
helper.addEventListener(pipeWrite, 'error', debugError),
|
||||||
];
|
];
|
||||||
this.onmessage = null;
|
this.onmessage = undefined;
|
||||||
this.onclose = null;
|
this.onclose = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
send(message: string) {
|
send(message: string) {
|
||||||
this._pipeWrite.write(message);
|
this._pipeWrite!.write(message);
|
||||||
this._pipeWrite.write('\0');
|
this._pipeWrite!.write('\0');
|
||||||
}
|
}
|
||||||
|
|
||||||
_dispatch(buffer: Buffer) {
|
_dispatch(buffer: Buffer) {
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ const removeFolderAsync = platform.promisify(removeFolder);
|
||||||
export type LaunchProcessOptions = {
|
export type LaunchProcessOptions = {
|
||||||
executablePath: string,
|
executablePath: string,
|
||||||
args: string[],
|
args: string[],
|
||||||
env?: {[key: string]: string},
|
env?: {[key: string]: string | undefined},
|
||||||
|
|
||||||
handleSIGINT?: boolean,
|
handleSIGINT?: boolean,
|
||||||
handleSIGTERM?: boolean,
|
handleSIGTERM?: boolean,
|
||||||
|
|
@ -133,6 +133,7 @@ export async function launchProcess(options: LaunchProcessOptions): Promise<Laun
|
||||||
}
|
}
|
||||||
// Attempt to remove temporary profile directory to avoid littering.
|
// Attempt to remove temporary profile directory to avoid littering.
|
||||||
try {
|
try {
|
||||||
|
if (options.tempDir)
|
||||||
removeFolder.sync(options.tempDir);
|
removeFolder.sync(options.tempDir);
|
||||||
} catch (e) { }
|
} catch (e) { }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
6
src/server/tsconfig.json
Normal file
6
src/server/tsconfig.json
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"strict": true
|
||||||
|
},
|
||||||
|
"extends": "../../tsconfig.json"
|
||||||
|
}
|
||||||
|
|
@ -122,7 +122,7 @@ export class WKPlaywright implements Playwright {
|
||||||
let connectOptions: WKConnectOptions | undefined = undefined;
|
let connectOptions: WKConnectOptions | undefined = undefined;
|
||||||
|
|
||||||
const { launchedProcess, gracefullyClose } = await launchProcess({
|
const { launchedProcess, gracefullyClose } = await launchProcess({
|
||||||
executablePath: webkitExecutable,
|
executablePath: webkitExecutable!,
|
||||||
args: webkitArguments,
|
args: webkitArguments,
|
||||||
env,
|
env,
|
||||||
handleSIGINT,
|
handleSIGINT,
|
||||||
|
|
@ -130,7 +130,6 @@ export class WKPlaywright implements Playwright {
|
||||||
handleSIGHUP,
|
handleSIGHUP,
|
||||||
dumpio,
|
dumpio,
|
||||||
pipe: true,
|
pipe: true,
|
||||||
tempDir: null,
|
|
||||||
attemptToGracefullyClose: async () => {
|
attemptToGracefullyClose: async () => {
|
||||||
if (!connectOptions)
|
if (!connectOptions)
|
||||||
return Promise.reject();
|
return Promise.reject();
|
||||||
|
|
@ -191,13 +190,13 @@ export class WKPlaywright implements Playwright {
|
||||||
...defaultOptions,
|
...defaultOptions,
|
||||||
...options,
|
...options,
|
||||||
};
|
};
|
||||||
assert(!!downloadURLs[options.platform], 'Unsupported platform: ' + options.platform);
|
assert(!!(downloadURLs as any)[options.platform!], 'Unsupported platform: ' + options.platform);
|
||||||
|
|
||||||
return new BrowserFetcher(options.path, options.platform, this._revision, (platform: string, revision: string) => {
|
return new BrowserFetcher(options.path!, options.platform!, this._revision, (platform: string, revision: string) => {
|
||||||
return {
|
return {
|
||||||
downloadUrl: (platform === 'mac') ?
|
downloadUrl: (platform === 'mac') ?
|
||||||
util.format(downloadURLs[platform], options.host, revision, getMacVersion()) :
|
util.format(downloadURLs[platform], options!.host, revision, getMacVersion()) :
|
||||||
util.format(downloadURLs[platform], options.host, revision),
|
util.format((downloadURLs as any)[platform], options!.host, revision),
|
||||||
executablePath: 'pw_run.sh',
|
executablePath: 'pw_run.sh',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
@ -211,9 +210,9 @@ export class WKPlaywright implements Playwright {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_ARGS = [];
|
const DEFAULT_ARGS: string[] = [];
|
||||||
|
|
||||||
let cachedMacVersion = undefined;
|
let cachedMacVersion: string | undefined = undefined;
|
||||||
function getMacVersion() {
|
function getMacVersion() {
|
||||||
if (!cachedMacVersion) {
|
if (!cachedMacVersion) {
|
||||||
const [major, minor] = execSync('sw_vers -productVersion').toString('utf8').trim().split('.');
|
const [major, minor] = execSync('sw_vers -productVersion').toString('utf8').trim().split('.');
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ export class SlowMoTransport {
|
||||||
const message = this._incomingMessageQueue.shift();
|
const message = this._incomingMessageQueue.shift();
|
||||||
try {
|
try {
|
||||||
if (this.onmessage)
|
if (this.onmessage)
|
||||||
this.onmessage(message);
|
this.onmessage(message!);
|
||||||
} finally {
|
} finally {
|
||||||
this._scheduleQueueDispatch();
|
this._scheduleQueueDispatch();
|
||||||
}
|
}
|
||||||
|
|
@ -77,8 +77,8 @@ export class SlowMoTransport {
|
||||||
if (this.onclose)
|
if (this.onclose)
|
||||||
this.onclose();
|
this.onclose();
|
||||||
this._closed = true;
|
this._closed = true;
|
||||||
this._delegate.onmessage = null;
|
this._delegate.onmessage = undefined;
|
||||||
this._delegate.onclose = null;
|
this._delegate.onclose = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
send(s: string) {
|
send(s: string) {
|
||||||
|
|
|
||||||
6
src/webkit/tsconfig.json
Normal file
6
src/webkit/tsconfig.json
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"strict": true
|
||||||
|
},
|
||||||
|
"extends": "../../tsconfig.json"
|
||||||
|
}
|
||||||
|
|
@ -125,20 +125,20 @@ export class WKBrowser extends platform.EventEmitter implements Browser {
|
||||||
|
|
||||||
if (this._firstPageProxyCallback) {
|
if (this._firstPageProxyCallback) {
|
||||||
this._firstPageProxyCallback();
|
this._firstPageProxyCallback();
|
||||||
this._firstPageProxyCallback = null;
|
this._firstPageProxyCallback = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_onPageProxyDestroyed(event: Protocol.Browser.pageProxyDestroyedPayload) {
|
_onPageProxyDestroyed(event: Protocol.Browser.pageProxyDestroyedPayload) {
|
||||||
const pageProxyId = event.pageProxyId;
|
const pageProxyId = event.pageProxyId;
|
||||||
const pageProxy = this._pageProxies.get(pageProxyId);
|
const pageProxy = this._pageProxies.get(pageProxyId)!;
|
||||||
pageProxy.didClose();
|
pageProxy.didClose();
|
||||||
pageProxy.dispose();
|
pageProxy.dispose();
|
||||||
this._pageProxies.delete(pageProxyId);
|
this._pageProxies.delete(pageProxyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onPageProxyMessageReceived(event: PageProxyMessageReceivedPayload) {
|
_onPageProxyMessageReceived(event: PageProxyMessageReceivedPayload) {
|
||||||
const pageProxy = this._pageProxies.get(event.pageProxyId);
|
const pageProxy = this._pageProxies.get(event.pageProxyId)!;
|
||||||
pageProxy.dispatchMessageToSession(event.message);
|
pageProxy.dispatchMessageToSession(event.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -166,14 +166,14 @@ export class WKBrowser extends platform.EventEmitter implements Browser {
|
||||||
|
|
||||||
newPage: async (): Promise<Page> => {
|
newPage: async (): Promise<Page> => {
|
||||||
const { pageProxyId } = await this._browserSession.send('Browser.createPage', { browserContextId });
|
const { pageProxyId } = await this._browserSession.send('Browser.createPage', { browserContextId });
|
||||||
const pageProxy = this._pageProxies.get(pageProxyId);
|
const pageProxy = this._pageProxies.get(pageProxyId)!;
|
||||||
return await pageProxy.page();
|
return await pageProxy.page();
|
||||||
},
|
},
|
||||||
|
|
||||||
close: async (): Promise<void> => {
|
close: async (): Promise<void> => {
|
||||||
assert(browserContextId, 'Non-incognito profiles cannot be closed!');
|
assert(browserContextId, 'Non-incognito profiles cannot be closed!');
|
||||||
await this._browserSession.send('Browser.deleteContext', { browserContextId });
|
await this._browserSession.send('Browser.deleteContext', { browserContextId: browserContextId! });
|
||||||
this._contexts.delete(browserContextId);
|
this._contexts.delete(browserContextId!);
|
||||||
},
|
},
|
||||||
|
|
||||||
cookies: async (): Promise<network.NetworkCookie[]> => {
|
cookies: async (): Promise<network.NetworkCookie[]> => {
|
||||||
|
|
|
||||||
|
|
@ -77,8 +77,8 @@ export class WKConnection {
|
||||||
if (this._closed)
|
if (this._closed)
|
||||||
return;
|
return;
|
||||||
this._closed = true;
|
this._closed = true;
|
||||||
this._transport.onmessage = null;
|
this._transport.onmessage = undefined;
|
||||||
this._transport.onclose = null;
|
this._transport.onclose = undefined;
|
||||||
this.browserSession.dispose();
|
this.browserSession.dispose();
|
||||||
this._onDisconnect();
|
this._onDisconnect();
|
||||||
}
|
}
|
||||||
|
|
@ -90,10 +90,11 @@ export class WKConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class WKSession extends platform.EventEmitter {
|
export class WKSession extends platform.EventEmitter {
|
||||||
connection?: WKConnection;
|
connection: WKConnection;
|
||||||
errorText: string;
|
errorText: string;
|
||||||
readonly sessionId: string;
|
readonly sessionId: string;
|
||||||
|
|
||||||
|
private _disposed = false;
|
||||||
private readonly _rawSend: (message: any) => void;
|
private readonly _rawSend: (message: any) => void;
|
||||||
private readonly _callbacks = new Map<number, {resolve:(o: any) => void, reject: (e: Error) => void, error: Error, method: string}>();
|
private readonly _callbacks = new Map<number, {resolve:(o: any) => void, reject: (e: Error) => void, error: Error, method: string}>();
|
||||||
|
|
||||||
|
|
@ -109,13 +110,19 @@ export class WKSession extends platform.EventEmitter {
|
||||||
this.sessionId = sessionId;
|
this.sessionId = sessionId;
|
||||||
this._rawSend = rawSend;
|
this._rawSend = rawSend;
|
||||||
this.errorText = errorText;
|
this.errorText = errorText;
|
||||||
|
|
||||||
|
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>(
|
send<T extends keyof Protocol.CommandParameters>(
|
||||||
method: T,
|
method: T,
|
||||||
params?: Protocol.CommandParameters[T]
|
params?: Protocol.CommandParameters[T]
|
||||||
): Promise<Protocol.CommandReturnValues[T]> {
|
): Promise<Protocol.CommandReturnValues[T]> {
|
||||||
if (!this.connection)
|
if (this._disposed)
|
||||||
return Promise.reject(new Error(`Protocol error (${method}): ${this.errorText}`));
|
return Promise.reject(new Error(`Protocol error (${method}): ${this.errorText}`));
|
||||||
const id = this.connection.nextMessageId();
|
const id = this.connection.nextMessageId();
|
||||||
const messageObj = { id, method, params };
|
const messageObj = { id, method, params };
|
||||||
|
|
@ -128,20 +135,20 @@ export class WKSession extends platform.EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
isDisposed(): boolean {
|
isDisposed(): boolean {
|
||||||
return !this.connection;
|
return this._disposed;
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
for (const callback of this._callbacks.values())
|
for (const callback of this._callbacks.values())
|
||||||
callback.reject(rewriteError(callback.error, `Protocol error (${callback.method}): ${this.errorText}`));
|
callback.reject(rewriteError(callback.error, `Protocol error (${callback.method}): ${this.errorText}`));
|
||||||
this._callbacks.clear();
|
this._callbacks.clear();
|
||||||
this.connection = undefined;
|
this._disposed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatchMessage(object: any) {
|
dispatchMessage(object: any) {
|
||||||
debugWrappedMessage('◀ RECV ' + JSON.stringify(object, null, 2));
|
debugWrappedMessage('◀ RECV ' + JSON.stringify(object, null, 2));
|
||||||
if (object.id && this._callbacks.has(object.id)) {
|
if (object.id && this._callbacks.has(object.id)) {
|
||||||
const callback = this._callbacks.get(object.id);
|
const callback = this._callbacks.get(object.id)!;
|
||||||
this._callbacks.delete(object.id);
|
this._callbacks.delete(object.id);
|
||||||
if (object.error)
|
if (object.error)
|
||||||
callback.reject(createProtocolError(callback.error, callback.method, object));
|
callback.reject(createProtocolError(callback.error, callback.method, object));
|
||||||
|
|
|
||||||
|
|
@ -28,14 +28,13 @@ export class WKExecutionContext implements js.ExecutionContextDelegate {
|
||||||
private _globalObjectId?: Promise<string>;
|
private _globalObjectId?: Promise<string>;
|
||||||
_session: WKSession;
|
_session: WKSession;
|
||||||
_contextId: number | undefined;
|
_contextId: number | undefined;
|
||||||
private _contextDestroyedCallback: () => void;
|
private _contextDestroyedCallback: () => void = () => {};
|
||||||
private _executionContextDestroyedPromise: Promise<unknown>;
|
private _executionContextDestroyedPromise: Promise<unknown>;
|
||||||
_jsonStringifyObjectId: Protocol.Runtime.RemoteObjectId | undefined;
|
_jsonStringifyObjectId: Protocol.Runtime.RemoteObjectId | undefined;
|
||||||
|
|
||||||
constructor(client: WKSession, contextId: number | undefined) {
|
constructor(client: WKSession, contextId: number | undefined) {
|
||||||
this._session = client;
|
this._session = client;
|
||||||
this._contextId = contextId;
|
this._contextId = contextId;
|
||||||
this._contextDestroyedCallback = null;
|
|
||||||
this._executionContextDestroyedPromise = new Promise((resolve, reject) => {
|
this._executionContextDestroyedPromise = new Promise((resolve, reject) => {
|
||||||
this._contextDestroyedCallback = resolve;
|
this._contextDestroyedCallback = resolve;
|
||||||
});
|
});
|
||||||
|
|
@ -59,7 +58,7 @@ export class WKExecutionContext implements js.ExecutionContextDelegate {
|
||||||
if (response.result.type === 'object' && response.result.className === 'Promise') {
|
if (response.result.type === 'object' && response.result.className === 'Promise') {
|
||||||
return Promise.race([
|
return Promise.race([
|
||||||
this._executionContextDestroyedPromise.then(() => contextDestroyedResult),
|
this._executionContextDestroyedPromise.then(() => contextDestroyedResult),
|
||||||
this._awaitPromise(response.result.objectId),
|
this._awaitPromise(response.result.objectId!),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
|
|
@ -131,7 +130,7 @@ export class WKExecutionContext implements js.ExecutionContextDelegate {
|
||||||
if (response.result.type === 'object' && response.result.className === 'Promise') {
|
if (response.result.type === 'object' && response.result.className === 'Promise') {
|
||||||
return Promise.race([
|
return Promise.race([
|
||||||
this._executionContextDestroyedPromise.then(() => contextDestroyedResult),
|
this._executionContextDestroyedPromise.then(() => contextDestroyedResult),
|
||||||
this._awaitPromise(response.result.objectId),
|
this._awaitPromise(response.result.objectId!),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
|
|
@ -198,7 +197,7 @@ export class WKExecutionContext implements js.ExecutionContextDelegate {
|
||||||
throw new Error('Execution context was destroyed, most likely because of a navigation.');
|
throw new Error('Execution context was destroyed, most likely because of a navigation.');
|
||||||
throw e;
|
throw e;
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
return response.result.objectId;
|
return response.result.objectId!;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return this._globalObjectId;
|
return this._globalObjectId;
|
||||||
|
|
@ -233,8 +232,11 @@ export class WKExecutionContext implements js.ExecutionContextDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getProperties(handle: js.JSHandle): Promise<Map<string, js.JSHandle>> {
|
async getProperties(handle: js.JSHandle): Promise<Map<string, js.JSHandle>> {
|
||||||
|
const objectId = toRemoteObject(handle).objectId;
|
||||||
|
if (!objectId)
|
||||||
|
return new Map();
|
||||||
const response = await this._session.send('Runtime.getProperties', {
|
const response = await this._session.send('Runtime.getProperties', {
|
||||||
objectId: toRemoteObject(handle).objectId,
|
objectId,
|
||||||
ownProperties: true
|
ownProperties: true
|
||||||
});
|
});
|
||||||
const result = new Map();
|
const result = new Map();
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ function toModifiersMask(modifiers: Set<input.Modifier>): number {
|
||||||
|
|
||||||
export class RawKeyboardImpl implements input.RawKeyboard {
|
export class RawKeyboardImpl implements input.RawKeyboard {
|
||||||
private readonly _pageProxySession: WKSession;
|
private readonly _pageProxySession: WKSession;
|
||||||
private _session: WKSession;
|
private _session?: WKSession;
|
||||||
|
|
||||||
constructor(session: WKSession) {
|
constructor(session: WKSession) {
|
||||||
this._pageProxySession = session;
|
this._pageProxySession = session;
|
||||||
|
|
@ -83,7 +83,7 @@ export class RawKeyboardImpl implements input.RawKeyboard {
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendText(text: string): Promise<void> {
|
async sendText(text: string): Promise<void> {
|
||||||
await this._session.send('Page.insertText', { text });
|
await this._session!.send('Page.insertText', { text });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ export class WKNetworkManager {
|
||||||
constructor(page: Page, pageProxySession: WKSession) {
|
constructor(page: Page, pageProxySession: WKSession) {
|
||||||
this._page = page;
|
this._page = page;
|
||||||
this._pageProxySession = pageProxySession;
|
this._pageProxySession = pageProxySession;
|
||||||
|
this._session = undefined as any as WKSession;
|
||||||
}
|
}
|
||||||
|
|
||||||
async initializePageProxySession(credentials: types.Credentials | null) {
|
async initializePageProxySession(credentials: types.Credentials | null) {
|
||||||
|
|
@ -96,13 +97,13 @@ export class WKNetworkManager {
|
||||||
// TODO(einbinder) this will fail if we are an XHR document request
|
// TODO(einbinder) this will fail if we are an XHR document request
|
||||||
const isNavigationRequest = event.type === 'Document';
|
const isNavigationRequest = event.type === 'Document';
|
||||||
const documentId = isNavigationRequest ? event.loaderId : undefined;
|
const documentId = isNavigationRequest ? event.loaderId : undefined;
|
||||||
const request = new InterceptableRequest(this._session, this._page._state.interceptNetwork, frame, event, redirectChain, documentId);
|
const request = new InterceptableRequest(this._session, !!this._page._state.interceptNetwork, frame, event, redirectChain, documentId);
|
||||||
this._requestIdToRequest.set(event.requestId, request);
|
this._requestIdToRequest.set(event.requestId, request);
|
||||||
this._page._frameManager.requestStarted(request.request);
|
this._page._frameManager.requestStarted(request.request);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onRequestIntercepted(event: Protocol.Network.requestInterceptedPayload) {
|
_onRequestIntercepted(event: Protocol.Network.requestInterceptedPayload) {
|
||||||
this._requestIdToRequest.get(event.requestId)._interceptedCallback();
|
this._requestIdToRequest.get(event.requestId)!._interceptedCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
_createResponse(request: InterceptableRequest, responsePayload: Protocol.Network.Response): network.Response {
|
_createResponse(request: InterceptableRequest, responsePayload: Protocol.Network.Response): network.Response {
|
||||||
|
|
@ -141,8 +142,9 @@ export class WKNetworkManager {
|
||||||
|
|
||||||
// Under certain conditions we never get the Network.responseReceived
|
// Under certain conditions we never get the Network.responseReceived
|
||||||
// event from protocol. @see https://crbug.com/883475
|
// event from protocol. @see https://crbug.com/883475
|
||||||
if (request.request.response())
|
const response = request.request.response();
|
||||||
request.request.response()._requestFinished();
|
if (response)
|
||||||
|
response._requestFinished();
|
||||||
this._requestIdToRequest.delete(request._requestId);
|
this._requestIdToRequest.delete(request._requestId);
|
||||||
this._page._frameManager.requestFinished(request.request);
|
this._page._frameManager.requestFinished(request.request);
|
||||||
}
|
}
|
||||||
|
|
@ -192,7 +194,7 @@ class InterceptableRequest implements network.RequestDelegate {
|
||||||
readonly request: network.Request;
|
readonly request: network.Request;
|
||||||
_requestId: string;
|
_requestId: string;
|
||||||
_documentId: string | undefined;
|
_documentId: string | undefined;
|
||||||
_interceptedCallback: () => void;
|
_interceptedCallback: () => void = () => {};
|
||||||
private _interceptedPromise: Promise<unknown>;
|
private _interceptedPromise: Promise<unknown>;
|
||||||
|
|
||||||
constructor(session: WKSession, allowInterception: boolean, frame: frames.Frame | null, event: Protocol.Network.requestWillBeSentPayload, redirectChain: network.Request[], documentId: string | undefined) {
|
constructor(session: WKSession, allowInterception: boolean, frame: frames.Frame | null, event: Protocol.Network.requestWillBeSentPayload, redirectChain: network.Request[], documentId: string | undefined) {
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ export class WKPage implements PageDelegate {
|
||||||
this._page = new Page(this, browserContext);
|
this._page = new Page(this, browserContext);
|
||||||
this._networkManager = new WKNetworkManager(this._page, pageProxySession);
|
this._networkManager = new WKNetworkManager(this._page, pageProxySession);
|
||||||
this._workers = new WKWorkers(this._page);
|
this._workers = new WKWorkers(this._page);
|
||||||
|
this._session = undefined as any as WKSession;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _initializePageProxySession() {
|
async _initializePageProxySession() {
|
||||||
|
|
@ -173,7 +174,7 @@ export class WKPage implements PageDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleFrameTree(frameTree: Protocol.Page.FrameResourceTree) {
|
_handleFrameTree(frameTree: Protocol.Page.FrameResourceTree) {
|
||||||
this._onFrameAttached(frameTree.frame.id, frameTree.frame.parentId);
|
this._onFrameAttached(frameTree.frame.id, frameTree.frame.parentId || null);
|
||||||
this._onFrameNavigated(frameTree.frame, true);
|
this._onFrameNavigated(frameTree.frame, true);
|
||||||
if (!frameTree.childFrames)
|
if (!frameTree.childFrames)
|
||||||
return;
|
return;
|
||||||
|
|
@ -233,8 +234,8 @@ export class WKPage implements PageDelegate {
|
||||||
async _onConsoleMessage(event: Protocol.Console.messageAddedPayload) {
|
async _onConsoleMessage(event: Protocol.Console.messageAddedPayload) {
|
||||||
const { type, level, text, parameters, url, line: lineNumber, column: columnNumber, source } = event.message;
|
const { type, level, text, parameters, url, line: lineNumber, column: columnNumber, source } = event.message;
|
||||||
if (level === 'debug' && parameters && parameters[0].value === BINDING_CALL_MESSAGE) {
|
if (level === 'debug' && parameters && parameters[0].value === BINDING_CALL_MESSAGE) {
|
||||||
const parsedObjectId = JSON.parse(parameters[1].objectId);
|
const parsedObjectId = JSON.parse(parameters[1].objectId!);
|
||||||
const context = this._contextIdToContext.get(parsedObjectId.injectedScriptId);
|
const context = this._contextIdToContext.get(parsedObjectId.injectedScriptId)!;
|
||||||
this._page._onBindingCalled(parameters[2].value, context);
|
this._page._onBindingCalled(parameters[2].value, context);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -245,7 +246,7 @@ export class WKPage implements PageDelegate {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let derivedType: string = type;
|
let derivedType: string = type || '';
|
||||||
if (type === 'log')
|
if (type === 'log')
|
||||||
derivedType = level;
|
derivedType = level;
|
||||||
else if (type === 'timing')
|
else if (type === 'timing')
|
||||||
|
|
@ -256,13 +257,13 @@ export class WKPage implements PageDelegate {
|
||||||
let context: dom.FrameExecutionContext | null = null;
|
let context: dom.FrameExecutionContext | null = null;
|
||||||
if (p.objectId) {
|
if (p.objectId) {
|
||||||
const objectId = JSON.parse(p.objectId);
|
const objectId = JSON.parse(p.objectId);
|
||||||
context = this._contextIdToContext.get(objectId.injectedScriptId);
|
context = this._contextIdToContext.get(objectId.injectedScriptId)!;
|
||||||
} else {
|
} else {
|
||||||
context = mainFrameContext;
|
context = mainFrameContext;
|
||||||
}
|
}
|
||||||
return context._createHandle(p);
|
return context._createHandle(p);
|
||||||
});
|
});
|
||||||
this._page._addConsoleMessage(derivedType, handles, { url, lineNumber: lineNumber - 1, columnNumber: columnNumber - 1 }, handles.length ? undefined : text);
|
this._page._addConsoleMessage(derivedType, handles, { url, lineNumber: (lineNumber || 1) - 1, columnNumber: (columnNumber || 1) - 1 }, handles.length ? undefined : text);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onDialog(event: Protocol.Dialog.javascriptDialogOpeningPayload) {
|
_onDialog(event: Protocol.Dialog.javascriptDialogOpeningPayload) {
|
||||||
|
|
@ -276,7 +277,7 @@ export class WKPage implements PageDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
async _onFileChooserOpened(event: {frameId: Protocol.Network.FrameId, element: Protocol.Runtime.RemoteObject}) {
|
async _onFileChooserOpened(event: {frameId: Protocol.Network.FrameId, element: Protocol.Runtime.RemoteObject}) {
|
||||||
const context = await this._page._frameManager.frame(event.frameId)._mainContext();
|
const context = await this._page._frameManager.frame(event.frameId)!._mainContext();
|
||||||
const handle = context._createHandle(event.element).asElement()!;
|
const handle = context._createHandle(event.element).asElement()!;
|
||||||
this._page._onFileChooserOpened(handle);
|
this._page._onFileChooserOpened(handle);
|
||||||
}
|
}
|
||||||
|
|
@ -418,7 +419,7 @@ export class WKPage implements PageDelegate {
|
||||||
|
|
||||||
async getContentFrame(handle: dom.ElementHandle): Promise<frames.Frame | null> {
|
async getContentFrame(handle: dom.ElementHandle): Promise<frames.Frame | null> {
|
||||||
const nodeInfo = await this._session.send('DOM.describeNode', {
|
const nodeInfo = await this._session.send('DOM.describeNode', {
|
||||||
objectId: toRemoteObject(handle).objectId
|
objectId: toRemoteObject(handle).objectId!
|
||||||
});
|
});
|
||||||
if (!nodeInfo.contentFrameId)
|
if (!nodeInfo.contentFrameId)
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -462,7 +463,7 @@ export class WKPage implements PageDelegate {
|
||||||
|
|
||||||
async getContentQuads(handle: dom.ElementHandle): Promise<types.Quad[] | null> {
|
async getContentQuads(handle: dom.ElementHandle): Promise<types.Quad[] | null> {
|
||||||
const result = await this._session.send('DOM.getContentQuads', {
|
const result = await this._session.send('DOM.getContentQuads', {
|
||||||
objectId: toRemoteObject(handle).objectId
|
objectId: toRemoteObject(handle).objectId!
|
||||||
}).catch(debugError);
|
}).catch(debugError);
|
||||||
if (!result)
|
if (!result)
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -478,8 +479,8 @@ export class WKPage implements PageDelegate {
|
||||||
return this._page.evaluate(() => ({ width: innerWidth, height: innerHeight }));
|
return this._page.evaluate(() => ({ width: innerWidth, height: innerHeight }));
|
||||||
}
|
}
|
||||||
|
|
||||||
async setInputFiles(handle: dom.ElementHandle, files: types.FilePayload[]): Promise<void> {
|
async setInputFiles(handle: dom.ElementHandle<HTMLInputElement>, files: types.FilePayload[]): Promise<void> {
|
||||||
const objectId = toRemoteObject(handle).objectId;
|
const objectId = toRemoteObject(handle).objectId!;
|
||||||
await this._session.send('DOM.setInputFiles', { objectId, files });
|
await this._session.send('DOM.setInputFiles', { objectId, files });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ export class WKPageProxy {
|
||||||
private _pagePromise: Promise<Page> | null = null;
|
private _pagePromise: Promise<Page> | null = null;
|
||||||
private _wkPage: WKPage | null = null;
|
private _wkPage: WKPage | null = null;
|
||||||
private readonly _firstTargetPromise: Promise<void>;
|
private readonly _firstTargetPromise: Promise<void>;
|
||||||
private _firstTargetCallback: () => void;
|
private _firstTargetCallback?: () => void;
|
||||||
private readonly _sessions = new Map<string, WKSession>();
|
private readonly _sessions = new Map<string, WKSession>();
|
||||||
private readonly _eventListeners: RegisteredListener[];
|
private readonly _eventListeners: RegisteredListener[];
|
||||||
|
|
||||||
|
|
@ -71,16 +71,15 @@ export class WKPageProxy {
|
||||||
}
|
}
|
||||||
|
|
||||||
onPopupCreated(popupPageProxy: WKPageProxy) {
|
onPopupCreated(popupPageProxy: WKPageProxy) {
|
||||||
if (!this._wkPage)
|
const wkPage = this._wkPage;
|
||||||
|
if (!wkPage || !wkPage._page.listenerCount(Events.Page.Popup))
|
||||||
return;
|
return;
|
||||||
if (!this._wkPage._page.listenerCount(Events.Page.Popup))
|
popupPageProxy.page().then(page => wkPage._page.emit(Events.Page.Popup, page));
|
||||||
return;
|
|
||||||
popupPageProxy.page().then(page => this._wkPage._page.emit(Events.Page.Popup, page));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _initializeWKPage(): Promise<Page> {
|
private async _initializeWKPage(): Promise<Page> {
|
||||||
await this._firstTargetPromise;
|
await this._firstTargetPromise;
|
||||||
let session: WKSession;
|
let session: WKSession | undefined;
|
||||||
for (const anySession of this._sessions.values()) {
|
for (const anySession of this._sessions.values()) {
|
||||||
if (!(anySession as any)[provisionalMessagesSymbol]) {
|
if (!(anySession as any)[provisionalMessagesSymbol]) {
|
||||||
session = anySession;
|
session = anySession;
|
||||||
|
|
@ -89,10 +88,10 @@ export class WKPageProxy {
|
||||||
}
|
}
|
||||||
assert(session, 'One non-provisional target session must exist');
|
assert(session, 'One non-provisional target session must exist');
|
||||||
this._wkPage = new WKPage(this._browserContext, this._pageProxySession);
|
this._wkPage = new WKPage(this._browserContext, this._pageProxySession);
|
||||||
this._wkPage.setSession(session);
|
this._wkPage.setSession(session!);
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
this._wkPage._initializePageProxySession(),
|
this._wkPage._initializePageProxySession(),
|
||||||
this._wkPage._initializeSession(session, false),
|
this._wkPage._initializeSession(session!, false),
|
||||||
]);
|
]);
|
||||||
return this._wkPage._page;
|
return this._wkPage._page;
|
||||||
}
|
}
|
||||||
|
|
@ -110,7 +109,7 @@ export class WKPageProxy {
|
||||||
this._sessions.set(targetInfo.targetId, session);
|
this._sessions.set(targetInfo.targetId, session);
|
||||||
if (this._firstTargetCallback) {
|
if (this._firstTargetCallback) {
|
||||||
this._firstTargetCallback();
|
this._firstTargetCallback();
|
||||||
this._firstTargetCallback = null;
|
this._firstTargetCallback = undefined;
|
||||||
}
|
}
|
||||||
if (targetInfo.isProvisional)
|
if (targetInfo.isProvisional)
|
||||||
(session as any)[provisionalMessagesSymbol] = [];
|
(session as any)[provisionalMessagesSymbol] = [];
|
||||||
|
|
@ -138,7 +137,7 @@ export class WKPageProxy {
|
||||||
if (provisionalMessages)
|
if (provisionalMessages)
|
||||||
provisionalMessages.push(message);
|
provisionalMessages.push(message);
|
||||||
else
|
else
|
||||||
session.dispatchMessage(JSON.parse(message));
|
session!.dispatchMessage(JSON.parse(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
private _onDidCommitProvisionalTarget(event: Protocol.Target.didCommitProvisionalTargetPayload) {
|
private _onDidCommitProvisionalTarget(event: Protocol.Target.didCommitProvisionalTargetPayload) {
|
||||||
|
|
@ -148,12 +147,12 @@ export class WKPageProxy {
|
||||||
const oldSession = this._sessions.get(oldTargetId);
|
const oldSession = this._sessions.get(oldTargetId);
|
||||||
assert(oldSession, 'Unknown old target: ' + oldTargetId);
|
assert(oldSession, 'Unknown old target: ' + oldTargetId);
|
||||||
// TODO: make some calls like screenshot catch swapped out error and retry.
|
// TODO: make some calls like screenshot catch swapped out error and retry.
|
||||||
oldSession.errorText = 'Target was swapped out.';
|
oldSession!.errorText = 'Target was swapped out.';
|
||||||
const provisionalMessages = (newSession as any)[provisionalMessagesSymbol];
|
const provisionalMessages = (newSession as any)[provisionalMessagesSymbol];
|
||||||
assert(provisionalMessages, 'Committing target must be provisional');
|
assert(provisionalMessages, 'Committing target must be provisional');
|
||||||
(newSession as any)[provisionalMessagesSymbol] = undefined;
|
(newSession as any)[provisionalMessagesSymbol] = undefined;
|
||||||
for (const message of provisionalMessages)
|
for (const message of provisionalMessages)
|
||||||
newSession.dispatchMessage(JSON.parse(message));
|
newSession!.dispatchMessage(JSON.parse(message));
|
||||||
this._wkPage.setSession(newSession);
|
this._wkPage!.setSession(newSession!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,11 +61,11 @@ export class WKWorkers {
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
helper.addEventListener(session, 'Worker.dispatchMessageFromWorker', (event: Protocol.Worker.dispatchMessageFromWorkerPayload) => {
|
helper.addEventListener(session, 'Worker.dispatchMessageFromWorker', (event: Protocol.Worker.dispatchMessageFromWorkerPayload) => {
|
||||||
const workerSession = this._workerSessions.get(event.workerId);
|
const workerSession = this._workerSessions.get(event.workerId)!;
|
||||||
workerSession.dispatchMessage(JSON.parse(event.message));
|
workerSession.dispatchMessage(JSON.parse(event.message));
|
||||||
}),
|
}),
|
||||||
helper.addEventListener(session, 'Worker.workerTerminated', (event: Protocol.Worker.workerTerminatedPayload) => {
|
helper.addEventListener(session, 'Worker.workerTerminated', (event: Protocol.Worker.workerTerminatedPayload) => {
|
||||||
const workerSession = this._workerSessions.get(event.workerId);
|
const workerSession = this._workerSessions.get(event.workerId)!;
|
||||||
workerSession.dispose();
|
workerSession.dispose();
|
||||||
this._workerSessions.delete(event.workerId);
|
this._workerSessions.delete(event.workerId);
|
||||||
this._page._removeWorker(event.workerId);
|
this._page._removeWorker(event.workerId);
|
||||||
|
|
@ -79,15 +79,15 @@ export class WKWorkers {
|
||||||
|
|
||||||
async _onConsoleMessage(worker: Worker, event: Protocol.Console.messageAddedPayload) {
|
async _onConsoleMessage(worker: Worker, event: Protocol.Console.messageAddedPayload) {
|
||||||
const { type, level, text, parameters, url, line: lineNumber, column: columnNumber } = event.message;
|
const { type, level, text, parameters, url, line: lineNumber, column: columnNumber } = event.message;
|
||||||
let derivedType: string = type;
|
let derivedType: string = type || '';
|
||||||
if (type === 'log')
|
if (type === 'log')
|
||||||
derivedType = level;
|
derivedType = level;
|
||||||
else if (type === 'timing')
|
else if (type === 'timing')
|
||||||
derivedType = 'timeEnd';
|
derivedType = 'timeEnd';
|
||||||
|
|
||||||
const handles = (parameters || []).map(p => {
|
const handles = (parameters || []).map(p => {
|
||||||
return worker._existingExecutionContext._createHandle(p);
|
return worker._existingExecutionContext!._createHandle(p);
|
||||||
});
|
});
|
||||||
this._page._addConsoleMessage(derivedType, handles, { url, lineNumber: lineNumber - 1, columnNumber: columnNumber - 1 }, handles.length ? undefined : text);
|
this._page._addConsoleMessage(derivedType, handles, { url, lineNumber: (lineNumber || 1) - 1, columnNumber: (columnNumber || 1) - 1 }, handles.length ? undefined : text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||||
await browser.stopTracing();
|
await browser.stopTracing();
|
||||||
|
|
||||||
const traceJson = JSON.parse(fs.readFileSync(outputFile).toString());
|
const traceJson = JSON.parse(fs.readFileSync(outputFile).toString());
|
||||||
expect(traceJson.metadata['trace-config']).toContain('disabled-by-default-v8.cpu_profiler.hires');
|
expect(traceJson.metadata['trace-config']).toContain('disabled-by-default-v8.cpu_profiler.hires', 'Does not contain expected category');
|
||||||
});
|
});
|
||||||
it('should throw if tracing on two pages', async({browser, page, server, outputFile}) => {
|
it('should throw if tracing on two pages', async({browser, page, server, outputFile}) => {
|
||||||
await browser.startTracing(page, {path: outputFile});
|
await browser.startTracing(page, {path: outputFile});
|
||||||
|
|
@ -64,7 +64,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||||
await page.goto(server.PREFIX + '/grid.html');
|
await page.goto(server.PREFIX + '/grid.html');
|
||||||
const trace = await browser.stopTracing();
|
const trace = await browser.stopTracing();
|
||||||
const buf = fs.readFileSync(outputFile);
|
const buf = fs.readFileSync(outputFile);
|
||||||
expect(trace.toString()).toEqual(buf.toString());
|
expect(trace.toString()).toEqual(buf.toString(), 'Tracing buffer mismatch');
|
||||||
});
|
});
|
||||||
it('should work without options', async({browser, page, server, outputFile}) => {
|
it('should work without options', async({browser, page, server, outputFile}) => {
|
||||||
await browser.startTracing(page);
|
await browser.startTracing(page);
|
||||||
|
|
@ -87,7 +87,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||||
await browser.startTracing(page, {screenshots: true});
|
await browser.startTracing(page, {screenshots: true});
|
||||||
await page.goto(server.PREFIX + '/grid.html');
|
await page.goto(server.PREFIX + '/grid.html');
|
||||||
const trace = await browser.stopTracing();
|
const trace = await browser.stopTracing();
|
||||||
expect(trace.toString()).toContain('screenshot');
|
expect(trace.toString()).toContain('screenshot', 'Does not contain screenshot');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue