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/node": "^8.10.34",
|
||||
"@types/pngjs": "^3.4.0",
|
||||
"@types/proxy-from-env": "^1.0.0",
|
||||
"@types/rimraf": "^2.0.2",
|
||||
"@types/ws": "^6.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "^2.6.1",
|
||||
|
|
|
|||
|
|
@ -68,13 +68,13 @@ export class Accessibility {
|
|||
async snapshot(options: {
|
||||
interestingOnly?: boolean;
|
||||
root?: dom.ElementHandle | null;
|
||||
} = {}): Promise<SerializedAXNode> {
|
||||
} = {}): Promise<SerializedAXNode | null> {
|
||||
const {
|
||||
interestingOnly = true,
|
||||
root = null,
|
||||
} = options;
|
||||
const defaultRoot = await this._getAXTree();
|
||||
let needle = defaultRoot;
|
||||
let needle: AXNode | null = defaultRoot;
|
||||
if (root) {
|
||||
needle = await defaultRoot.findElement(root);
|
||||
if (!needle)
|
||||
|
|
|
|||
|
|
@ -103,14 +103,14 @@ export class BrowserContext {
|
|||
if (geolocation) {
|
||||
geolocation.accuracy = geolocation.accuracy || 0;
|
||||
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.`);
|
||||
if (latitude < -90 || latitude > 90)
|
||||
if (latitude !== undefined && (latitude < -90 || latitude > 90))
|
||||
throw new Error(`Invalid latitude "${latitude}": precondition -90 <= LATITUDE <= 90 failed.`);
|
||||
if (accuracy < 0)
|
||||
throw new Error(`Invalid accuracy "${accuracy}": precondition 0 <= ACCURACY failed.`);
|
||||
}
|
||||
this._options.geolocation = geolocation;
|
||||
this._options.geolocation = geolocation || undefined;
|
||||
await this._delegate.setGeolocation(geolocation);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -207,7 +207,7 @@ class CRAXNode implements accessibility.AXNode {
|
|||
|
||||
const node: {[x in keyof accessibility.SerializedAXNode]: any} = {
|
||||
role: this._role,
|
||||
name: this._payload.name.value || ''
|
||||
name: this._payload.name ? (this._payload.name.value || '') : ''
|
||||
};
|
||||
|
||||
const userStringProperties: Array<keyof accessibility.SerializedAXNode> = [
|
||||
|
|
@ -286,7 +286,7 @@ class CRAXNode implements accessibility.AXNode {
|
|||
nodeById.set(payload.nodeId, new CRAXNode(client, payload));
|
||||
for (const node of nodeById.values()) {
|
||||
for (const childId of node._payload.childIds || [])
|
||||
node._children.push(nodeById.get(childId));
|
||||
node._children.push(nodeById.get(childId)!);
|
||||
}
|
||||
return nodeById.values().next().value;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ export class CRBrowser extends platform.EventEmitter implements Browser {
|
|||
_targets = new Map<string, CRTarget>();
|
||||
|
||||
private _tracingRecording = false;
|
||||
private _tracingPath = '';
|
||||
private _tracingPath: string | null = '';
|
||||
private _tracingClient: CRSession | undefined;
|
||||
|
||||
static async connect(options: CRConnectOptions): Promise<CRBrowser> {
|
||||
|
|
@ -79,20 +79,21 @@ export class CRBrowser extends platform.EventEmitter implements Browser {
|
|||
pages: async (): Promise<Page[]> => {
|
||||
const targets = this._allTargets().filter(target => target.browserContext() === context && target.type() === '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> => {
|
||||
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');
|
||||
return target.page();
|
||||
const page = await target.page();
|
||||
return page!;
|
||||
},
|
||||
|
||||
close: async (): Promise<void> => {
|
||||
assert(contextId, 'Non-incognito profiles cannot be closed!');
|
||||
await this._client.send('Target.disposeBrowserContext', {browserContextId: contextId || undefined});
|
||||
this._contexts.delete(contextId);
|
||||
await this._client.send('Target.disposeBrowserContext', { browserContextId: contextId! });
|
||||
this._contexts.delete(contextId!);
|
||||
},
|
||||
|
||||
cookies: async (): Promise<network.NetworkCookie[]> => {
|
||||
|
|
@ -172,7 +173,7 @@ export class CRBrowser extends platform.EventEmitter implements Browser {
|
|||
async _targetCreated(event: Protocol.Target.targetCreatedPayload) {
|
||||
const targetInfo = event.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));
|
||||
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; }) {
|
||||
const target = this._targets.get(event.targetId);
|
||||
const target = this._targets.get(event.targetId)!;
|
||||
target._initializedCallback(false);
|
||||
this._targets.delete(event.targetId);
|
||||
target._didClose();
|
||||
|
|
@ -192,7 +193,7 @@ export class CRBrowser extends platform.EventEmitter implements Browser {
|
|||
}
|
||||
|
||||
_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');
|
||||
const previousURL = target.url();
|
||||
const wasInitialized = target._isInitialized;
|
||||
|
|
@ -242,7 +243,7 @@ export class CRBrowser extends platform.EventEmitter implements Browser {
|
|||
}
|
||||
|
||||
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> {
|
||||
|
|
@ -280,10 +281,10 @@ export class CRBrowser extends platform.EventEmitter implements Browser {
|
|||
assert(this._tracingClient, 'Tracing was not started.');
|
||||
let fulfill: (buffer: platform.BufferType) => void;
|
||||
const contentPromise = new Promise<platform.BufferType>(x => fulfill = x);
|
||||
this._tracingClient.once('Tracing.tracingComplete', event => {
|
||||
readProtocolStream(this._tracingClient, event.stream, this._tracingPath).then(fulfill);
|
||||
this._tracingClient!.once('Tracing.tracingComplete', event => {
|
||||
readProtocolStream(this._tracingClient!, event.stream!, this._tracingPath).then(fulfill);
|
||||
});
|
||||
await this._tracingClient.send('Tracing.end');
|
||||
await this._tracingClient!.send('Tracing.end');
|
||||
this._tracingRecording = false;
|
||||
return contentPromise;
|
||||
}
|
||||
|
|
@ -324,5 +325,5 @@ export async function createTransport(options: CRConnectOptions): Promise<Connec
|
|||
}
|
||||
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 {
|
||||
return session._connection;
|
||||
return session._connection!;
|
||||
}
|
||||
|
||||
session(sessionId: string): CRSession | null {
|
||||
|
|
@ -84,8 +84,8 @@ export class CRConnection extends platform.EventEmitter {
|
|||
if (this._closed)
|
||||
return;
|
||||
this._closed = true;
|
||||
this._transport.onmessage = null;
|
||||
this._transport.onclose = null;
|
||||
this._transport.onmessage = undefined;
|
||||
this._transport.onclose = undefined;
|
||||
for (const session of this._sessions.values())
|
||||
session._onClosed();
|
||||
this._sessions.clear();
|
||||
|
|
@ -99,12 +99,12 @@ export class CRConnection extends platform.EventEmitter {
|
|||
|
||||
async createSession(targetInfo: Protocol.Target.TargetInfo): Promise<CRSession> {
|
||||
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> {
|
||||
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 {
|
||||
_connection: CRConnection;
|
||||
_connection: CRConnection | null;
|
||||
private _callbacks = new Map<number, {resolve:(o: any) => void, reject: (e: Error) => void, error: Error, method: string}>();
|
||||
private _targetType: string;
|
||||
private _sessionId: string;
|
||||
|
|
@ -128,6 +128,12 @@ export class CRSession extends platform.EventEmitter {
|
|||
this._connection = connection;
|
||||
this._targetType = targetType;
|
||||
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>(
|
||||
|
|
@ -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; }) {
|
||||
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);
|
||||
if (object.error)
|
||||
callback.reject(createProtocolError(callback.error, callback.method, object));
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ class JSCoverage {
|
|||
_scriptSources: Map<string, string>;
|
||||
_eventListeners: RegisteredListener[];
|
||||
_resetOnNavigation: boolean;
|
||||
_reportAnonymousScripts: boolean;
|
||||
_reportAnonymousScripts = false;
|
||||
|
||||
constructor(client: CRSession) {
|
||||
this._client = client;
|
||||
|
|
@ -238,12 +238,12 @@ class CSSCoverage {
|
|||
});
|
||||
}
|
||||
|
||||
const coverage = [];
|
||||
const coverage: CoverageEntry[] = [];
|
||||
for (const styleSheetId of this._stylesheetURLs.keys()) {
|
||||
const url = this._stylesheetURLs.get(styleSheetId);
|
||||
const text = this._stylesheetSources.get(styleSheetId);
|
||||
const url = this._stylesheetURLs.get(styleSheetId)!;
|
||||
const text = this._stylesheetSources.get(styleSheetId)!;
|
||||
const ranges = convertToDisjointRanges(styleSheetIdToCoverage.get(styleSheetId) || []);
|
||||
coverage.push({url, ranges, text});
|
||||
coverage.push({ url, ranges, text });
|
||||
}
|
||||
|
||||
return coverage;
|
||||
|
|
@ -277,7 +277,7 @@ function convertToDisjointRanges(nestedRanges: {
|
|||
});
|
||||
|
||||
const hitCountStack = [];
|
||||
const results = [];
|
||||
const results: { start: number; end: number; }[] = [];
|
||||
let lastOffset = 0;
|
||||
// Run scanning line to intersect all ranges.
|
||||
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>> {
|
||||
const objectId = toRemoteObject(handle).objectId;
|
||||
if (!objectId)
|
||||
return new Map();
|
||||
const response = await this._client.send('Runtime.getProperties', {
|
||||
objectId: toRemoteObject(handle).objectId,
|
||||
objectId,
|
||||
ownProperties: true
|
||||
});
|
||||
const result = new Map();
|
||||
|
|
|
|||
|
|
@ -154,11 +154,13 @@ export class CRNetworkManager {
|
|||
requestId: event.requestId
|
||||
}).catch(debugError);
|
||||
}
|
||||
if (!event.networkId)
|
||||
return;
|
||||
|
||||
const requestId = event.networkId;
|
||||
const interceptionId = event.requestId;
|
||||
if (requestId && this._requestIdToRequestWillBeSentEvent.has(requestId)) {
|
||||
const requestWillBeSentEvent = this._requestIdToRequestWillBeSentEvent.get(requestId);
|
||||
if (requestWillBeSentEvent) {
|
||||
this._onRequest(requestWillBeSentEvent, interceptionId);
|
||||
this._requestIdToRequestWillBeSentEvent.delete(requestId);
|
||||
} else {
|
||||
|
|
@ -186,7 +188,7 @@ export class CRNetworkManager {
|
|||
}
|
||||
|
||||
_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 response = await this._client.send('Network.getResponseBody', { requestId: request._requestId });
|
||||
return platform.Buffer.from(response.body, response.base64Encoded ? 'base64' : 'utf8');
|
||||
|
|
@ -199,6 +201,7 @@ export class CRNetworkManager {
|
|||
request.request._redirectChain.push(request.request);
|
||||
response._requestFinished(new Error('Response body is unavailable for redirect responses'));
|
||||
this._requestIdToRequest.delete(request._requestId);
|
||||
if (request._interceptionId)
|
||||
this._attemptedAuthentications.delete(request._interceptionId);
|
||||
this._page._frameManager.requestReceivedResponse(response);
|
||||
this._page._frameManager.requestFinished(request.request);
|
||||
|
|
@ -222,9 +225,11 @@ export class CRNetworkManager {
|
|||
|
||||
// Under certain conditions we never get the Network.responseReceived
|
||||
// event from protocol. @see https://crbug.com/883475
|
||||
if (request.request.response())
|
||||
request.request.response()._requestFinished();
|
||||
const response = request.request.response();
|
||||
if (response)
|
||||
response._requestFinished();
|
||||
this._requestIdToRequest.delete(request._requestId);
|
||||
if (request._interceptionId)
|
||||
this._attemptedAuthentications.delete(request._interceptionId);
|
||||
this._page._frameManager.requestFinished(request.request);
|
||||
}
|
||||
|
|
@ -239,32 +244,33 @@ export class CRNetworkManager {
|
|||
if (response)
|
||||
response._requestFinished();
|
||||
this._requestIdToRequest.delete(request._requestId);
|
||||
if (request._interceptionId)
|
||||
this._attemptedAuthentications.delete(request._interceptionId);
|
||||
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 {
|
||||
readonly request: network.Request;
|
||||
_requestId: string;
|
||||
_interceptionId: string;
|
||||
_documentId: string;
|
||||
_interceptionId: string | null;
|
||||
_documentId: string | undefined;
|
||||
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._requestId = event.requestId;
|
||||
this._interceptionId = interceptionId;
|
||||
this._documentId = 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; } = {}) {
|
||||
await this._client.send('Fetch.continueRequest', {
|
||||
requestId: this._interceptionId,
|
||||
requestId: this._interceptionId!,
|
||||
headers: overrides.headers ? headersArray(overrides.headers) : undefined,
|
||||
}).catch(error => {
|
||||
// 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));
|
||||
|
||||
await this._client.send('Fetch.fulfillRequest', {
|
||||
requestId: this._interceptionId,
|
||||
requestId: this._interceptionId!,
|
||||
responseCode: response.status || 200,
|
||||
responsePhrase: network.STATUS_TEXTS[String(response.status || 200)],
|
||||
responseHeaders: headersArray(responseHeaders),
|
||||
|
|
@ -303,7 +309,7 @@ class InterceptableRequest implements network.RequestDelegate {
|
|||
const errorReason = errorReasons[errorCode];
|
||||
assert(errorReason, 'Unknown error code: ' + errorCode);
|
||||
await this._client.send('Fetch.failRequest', {
|
||||
requestId: this._interceptionId,
|
||||
requestId: this._interceptionId!,
|
||||
errorReason
|
||||
}).catch(error => {
|
||||
// In certain cases, protocol will return error if the request was already canceled
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
|
||||
import * as dom from '../dom';
|
||||
import * as js from '../javascript';
|
||||
import * as frames from '../frames';
|
||||
import { debugError, helper, RegisteredListener } from '../helper';
|
||||
import * as network from '../network';
|
||||
|
|
@ -151,7 +152,7 @@ export class CRPage implements PageDelegate {
|
|||
}
|
||||
|
||||
_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);
|
||||
if (!frameTree.childFrames)
|
||||
return;
|
||||
|
|
@ -196,7 +197,7 @@ export class CRPage implements PageDelegate {
|
|||
}
|
||||
|
||||
_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)
|
||||
return;
|
||||
if (contextPayload.auxData && contextPayload.auxData.type === 'isolated')
|
||||
|
|
@ -240,7 +241,7 @@ export class CRPage implements PageDelegate {
|
|||
// @see https://github.com/GoogleChrome/puppeteer/issues/3865
|
||||
return;
|
||||
}
|
||||
const context = this._contextIdToContext.get(event.executionContextId);
|
||||
const context = this._contextIdToContext.get(event.executionContextId)!;
|
||||
const values = event.args.map(arg => context._createHandle(arg));
|
||||
this._page._addConsoleMessage(event.type, values, toConsoleMessageLocation(event.stackTrace));
|
||||
}
|
||||
|
|
@ -252,7 +253,7 @@ export class CRPage implements PageDelegate {
|
|||
}
|
||||
|
||||
_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);
|
||||
}
|
||||
|
||||
|
|
@ -283,7 +284,7 @@ export class CRPage implements PageDelegate {
|
|||
}
|
||||
|
||||
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 handle = await this.adoptBackendNodeId(event.backendNodeId, utilityContext);
|
||||
this._page._onFileChooserOpened(handle);
|
||||
|
|
@ -458,7 +459,7 @@ export class CRPage implements PageDelegate {
|
|||
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);
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -125,6 +125,6 @@ export class CRPDF {
|
|||
pageRanges,
|
||||
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> {
|
||||
let eof = false;
|
||||
let fd;
|
||||
let fd: number | undefined;
|
||||
if (path)
|
||||
fd = await platform.openFdAsync(path, 'w');
|
||||
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);
|
||||
bufs.push(buf);
|
||||
if (path)
|
||||
await platform.writeFdAsync(fd, buf);
|
||||
await platform.writeFdAsync(fd!, buf);
|
||||
}
|
||||
if (path)
|
||||
await platform.closeFdAsync(fd);
|
||||
await platform.closeFdAsync(fd!);
|
||||
await client.send('IO.close', {handle});
|
||||
let resultBuffer = null;
|
||||
try {
|
||||
resultBuffer = platform.Buffer.concat(bufs);
|
||||
} finally {
|
||||
return resultBuffer;
|
||||
return resultBuffer!;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ export class CRTarget {
|
|||
_crPage: CRPage | null = null;
|
||||
private _workerPromise: Promise<Worker> | null = null;
|
||||
readonly _initializedPromise: Promise<boolean>;
|
||||
_initializedCallback: (value?: unknown) => void;
|
||||
_initializedCallback: (success: boolean) => void = () => {};
|
||||
_isInitialized: boolean;
|
||||
|
||||
static fromPage(page: Page): CRTarget {
|
||||
|
|
@ -135,7 +135,7 @@ export class CRTarget {
|
|||
const { openerId } = this._targetInfo;
|
||||
if (!openerId)
|
||||
return null;
|
||||
return this._browser._targets.get(openerId);
|
||||
return this._browser._targets.get(openerId)!;
|
||||
}
|
||||
|
||||
createCDPSession(): Promise<CRSession> {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export class CRWorkers {
|
|||
if (event.targetInfo.type !== 'worker')
|
||||
return;
|
||||
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);
|
||||
page._addWorker(event.sessionId, worker);
|
||||
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.
|
||||
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)));
|
||||
});
|
||||
client.on('Target.detachedFromTarget', event => page._removeWorker(event.sessionId));
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"noImplicitAny": true
|
||||
"strict": true
|
||||
},
|
||||
"extends": "../../tsconfig.json"
|
||||
}
|
||||
|
|
|
|||
27
src/dom.ts
27
src/dom.ts
|
|
@ -62,7 +62,7 @@ export class FrameExecutionContext extends js.ExecutionContext {
|
|||
return result;
|
||||
}
|
||||
|
||||
_createHandle(remoteObject: any): js.JSHandle | null {
|
||||
_createHandle(remoteObject: any): js.JSHandle {
|
||||
if (this.frame._page._delegate.isElementHandle(remoteObject))
|
||||
return new ElementHandle(this, remoteObject);
|
||||
return super._createHandle(remoteObject);
|
||||
|
|
@ -88,7 +88,7 @@ export class FrameExecutionContext extends js.ExecutionContext {
|
|||
);
|
||||
if (!handle.asElement())
|
||||
await handle.dispose();
|
||||
return handle.asElement();
|
||||
return handle.asElement() as ElementHandle<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 properties = await arrayHandle.getProperties();
|
||||
await arrayHandle.dispose();
|
||||
const result = [];
|
||||
const result: ElementHandle<Element>[] = [];
|
||||
for (const property of properties.values()) {
|
||||
const elementHandle = property.asElement();
|
||||
const elementHandle = property.asElement() as ElementHandle<Element>;
|
||||
if (elementHandle)
|
||||
result.push(elementHandle);
|
||||
else
|
||||
|
|
@ -121,6 +121,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
|||
|
||||
constructor(context: FrameExecutionContext, remoteObject: any) {
|
||||
super(context, remoteObject);
|
||||
this._context = context;
|
||||
this._page = context.frame._page;
|
||||
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) => {
|
||||
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> {
|
||||
|
|
@ -242,10 +243,10 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
|||
const [box, border] = await Promise.all([
|
||||
this.boundingBox(),
|
||||
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 };
|
||||
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),
|
||||
]);
|
||||
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 options = Array.from(element.options);
|
||||
element.value = undefined;
|
||||
element.value = undefined as any;
|
||||
for (let index = 0; index < options.length; index++) {
|
||||
const option = options[index];
|
||||
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();
|
||||
range.selectNodeContents(element);
|
||||
const selection = element.ownerDocument.defaultView.getSelection();
|
||||
if (!selection)
|
||||
return 'Element belongs to invisible iframe.';
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
element.focus();
|
||||
|
|
@ -414,12 +417,12 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
|||
}
|
||||
return item;
|
||||
}));
|
||||
await this._page._delegate.setInputFiles(this, filePayloads);
|
||||
await this._page._delegate.setInputFiles(this as any as ElementHandle<HTMLInputElement>, filePayloads);
|
||||
}
|
||||
|
||||
async focus() {
|
||||
const errorMessage = await this._evaluateInUtility((element: Node) => {
|
||||
if (!element['focus'])
|
||||
if (!(element as any)['focus'])
|
||||
return 'Node is not an HTML or SVG element.';
|
||||
(element as HTMLElement|SVGElement).focus();
|
||||
return false;
|
||||
|
|
@ -532,10 +535,10 @@ export function waitForFunctionTask(selector: string | undefined, pageFunction:
|
|||
return innerPredicate(...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) => {
|
||||
selector = normalizeSelector(selector);
|
||||
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) {
|
||||
this._page = page;
|
||||
this._mainFrame = undefined as any as Frame;
|
||||
}
|
||||
|
||||
mainFrame(): Frame {
|
||||
|
|
@ -86,7 +87,7 @@ export class FrameManager {
|
|||
}
|
||||
|
||||
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 (this._mainFrame) {
|
||||
// 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) {
|
||||
const frame = this._frames.get(frameId);
|
||||
const frame = this._frames.get(frameId)!;
|
||||
for (const child of frame.childFrames())
|
||||
this._removeFramesRecursively(child);
|
||||
frame._url = url;
|
||||
|
|
@ -185,9 +186,10 @@ export class FrameManager {
|
|||
|
||||
requestStarted(request: network.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)
|
||||
watcher._onNavigationRequest(request.frame(), request);
|
||||
watcher._onNavigationRequest(frame, request);
|
||||
}
|
||||
this._page.emit(Events.Page.Request, request);
|
||||
}
|
||||
|
|
@ -203,14 +205,15 @@ export class FrameManager {
|
|||
|
||||
requestFailed(request: network.Request, canceled: boolean) {
|
||||
this._inflightRequestFinished(request);
|
||||
if (request._documentId && request.frame()) {
|
||||
const isCurrentDocument = request.frame()._lastDocumentId === request._documentId;
|
||||
const frame = request.frame();
|
||||
if (request._documentId && frame) {
|
||||
const isCurrentDocument = frame._lastDocumentId === request._documentId;
|
||||
if (!isCurrentDocument) {
|
||||
let errorText = request.failure().errorText;
|
||||
let errorText = request.failure()!.errorText;
|
||||
if (canceled)
|
||||
errorText += '; maybe frame was detached?';
|
||||
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);
|
||||
|
|
@ -227,9 +230,9 @@ export class FrameManager {
|
|||
}
|
||||
|
||||
private _inflightRequestFinished(request: network.Request) {
|
||||
if (!request.frame() || request.url().endsWith('favicon.ico'))
|
||||
return;
|
||||
const frame = request.frame();
|
||||
if (!frame || request.url().endsWith('favicon.ico'))
|
||||
return;
|
||||
if (!frame._inflightRequests.has(request))
|
||||
return;
|
||||
frame._inflightRequests.delete(request);
|
||||
|
|
@ -240,9 +243,9 @@ export class FrameManager {
|
|||
}
|
||||
|
||||
private _inflightRequestStarted(request: network.Request) {
|
||||
if (!request.frame() || request.url().endsWith('favicon.ico'))
|
||||
return;
|
||||
const frame = request.frame();
|
||||
if (!frame || request.url().endsWith('favicon.ico'))
|
||||
return;
|
||||
frame._inflightRequests.add(request);
|
||||
if (frame._inflightRequests.size === 1)
|
||||
this._stopNetworkIdleTimer(frame, 'networkidle0');
|
||||
|
|
@ -260,7 +263,9 @@ export class FrameManager {
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -270,12 +275,12 @@ export class Frame {
|
|||
readonly _firedLifecycleEvents: Set<LifecycleEvent>;
|
||||
_lastDocumentId: string;
|
||||
readonly _page: Page;
|
||||
private _parentFrame: Frame;
|
||||
private _parentFrame: Frame | null;
|
||||
_url = '';
|
||||
private _detached = false;
|
||||
private _contextData = new Map<ContextType, ContextData>();
|
||||
private _childFrames = new Set<Frame>();
|
||||
_name: string;
|
||||
_name = '';
|
||||
_inflightRequests = new Set<network.Request>();
|
||||
readonly _networkIdleTimers = new Map<LifecycleEvent, NodeJS.Timer>();
|
||||
|
||||
|
|
@ -318,10 +323,10 @@ export class Frame {
|
|||
]);
|
||||
if (!error) {
|
||||
const promises = [watcher.timeoutOrTerminationPromise];
|
||||
if (navigateResult.newDocumentId) {
|
||||
watcher.setExpectedDocumentId(navigateResult.newDocumentId, url);
|
||||
if (navigateResult!.newDocumentId) {
|
||||
watcher.setExpectedDocumentId(navigateResult!.newDocumentId, url);
|
||||
promises.push(watcher.newDocumentNavigationPromise);
|
||||
} else if (navigateResult.isSameDocument) {
|
||||
} else if (navigateResult!.isSameDocument) {
|
||||
promises.push(watcher.sameDocumentNavigationPromise);
|
||||
} else {
|
||||
promises.push(watcher.sameDocumentNavigationPromise, watcher.newDocumentNavigationPromise);
|
||||
|
|
@ -361,7 +366,7 @@ export class Frame {
|
|||
_context(contextType: ContextType): Promise<dom.FrameExecutionContext> {
|
||||
if (this._detached)
|
||||
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> {
|
||||
|
|
@ -505,14 +510,13 @@ export class Frame {
|
|||
const context = await this._mainContext();
|
||||
return this._raceWithCSPError(async () => {
|
||||
if (url !== null)
|
||||
return (await context.evaluateHandle(addScriptUrl, url, type)).asElement();
|
||||
return (await context.evaluateHandle(addScriptUrl, url, type)).asElement()!;
|
||||
if (path !== null) {
|
||||
let contents = await platform.readFileAsync(path, 'utf8');
|
||||
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> {
|
||||
|
|
@ -554,16 +558,15 @@ export class Frame {
|
|||
const context = await this._mainContext();
|
||||
return this._raceWithCSPError(async () => {
|
||||
if (url !== null)
|
||||
return (await context.evaluateHandle(addStyleUrl, url)).asElement();
|
||||
return (await context.evaluateHandle(addStyleUrl, url)).asElement()!;
|
||||
|
||||
if (path !== null) {
|
||||
let contents = await platform.readFileAsync(path, 'utf8');
|
||||
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> {
|
||||
|
|
@ -595,7 +598,7 @@ export class Frame {
|
|||
|
||||
private async _raceWithCSPError(func: () => Promise<dom.ElementHandle>): Promise<dom.ElementHandle> {
|
||||
const listeners: RegisteredListener[] = [];
|
||||
let result: dom.ElementHandle | undefined;
|
||||
let result: dom.ElementHandle;
|
||||
let error: Error | undefined;
|
||||
let cspMessage: ConsoleMessage | undefined;
|
||||
const actionPromise = new Promise<dom.ElementHandle>(async resolve => {
|
||||
|
|
@ -620,7 +623,7 @@ export class Frame {
|
|||
throw new Error(cspMessage.text());
|
||||
if (error)
|
||||
throw error;
|
||||
return result;
|
||||
return result!;
|
||||
}
|
||||
|
||||
async click(selector: string, options?: WaitForOptions & ClickOptions) {
|
||||
|
|
@ -683,17 +686,19 @@ export class Frame {
|
|||
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 || {});
|
||||
let handle: dom.ElementHandle<Element> | null;
|
||||
let handle: dom.ElementHandle<Element>;
|
||||
if (waitFor !== 'nowait') {
|
||||
handle = await this._waitForSelectorInUtilityContext(selector, waitFor, timeout);
|
||||
if (!handle)
|
||||
const maybeHandle = await this._waitForSelectorInUtilityContext(selector, waitFor, timeout);
|
||||
if (!maybeHandle)
|
||||
throw new Error('No node found for selector: ' + selectorToString(selector, waitFor));
|
||||
handle = maybeHandle;
|
||||
} else {
|
||||
const context = await this._context('utility');
|
||||
handle = await context._$(selector);
|
||||
assert(handle, 'No node found for selector: ' + selector);
|
||||
const maybeHandle = await context._$(selector);
|
||||
assert(maybeHandle, 'No node found for selector: ' + selector);
|
||||
handle = maybeHandle!;
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
|
@ -742,7 +747,7 @@ export class Frame {
|
|||
}
|
||||
|
||||
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);
|
||||
data.rerunnableTasks.add(rerunnableTask);
|
||||
if (data.context)
|
||||
|
|
@ -751,7 +756,7 @@ export class Frame {
|
|||
}
|
||||
|
||||
private _setContext(contextType: ContextType, context: dom.FrameExecutionContext | null) {
|
||||
const data = this._contextData.get(contextType);
|
||||
const data = this._contextData.get(contextType)!;
|
||||
data.context = context;
|
||||
if (context) {
|
||||
data.contextResolveCallback.call(null, context);
|
||||
|
|
@ -765,7 +770,7 @@ export class Frame {
|
|||
}
|
||||
|
||||
_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
|
||||
// connections so we might end up creating multiple isolated worlds.
|
||||
// We can use either.
|
||||
|
|
@ -787,10 +792,10 @@ class RerunnableTask {
|
|||
private _contextData: ContextData;
|
||||
private _task: dom.Task;
|
||||
private _runCount: number;
|
||||
private _resolve: (result: js.JSHandle) => void;
|
||||
private _reject: (reason: Error) => void;
|
||||
private _timeoutTimer: NodeJS.Timer;
|
||||
private _terminated: boolean;
|
||||
private _resolve: (result: js.JSHandle) => void = () => {};
|
||||
private _reject: (reason: Error) => void = () => {};
|
||||
private _timeoutTimer?: NodeJS.Timer;
|
||||
private _terminated = false;
|
||||
|
||||
constructor(data: ContextData, task: dom.Task, timeout?: number, title?: string) {
|
||||
this._contextData = data;
|
||||
|
|
@ -834,7 +839,7 @@ class RerunnableTask {
|
|||
// If execution context has been already destroyed, `context.evaluate` will
|
||||
// throw an error - ignore this predicate run altogether.
|
||||
if (!error && await context.evaluate(s => !s, success).catch(e => true)) {
|
||||
await success.dispose();
|
||||
await success!.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -851,12 +856,13 @@ class RerunnableTask {
|
|||
if (error)
|
||||
this._reject(error);
|
||||
else
|
||||
this._resolve(success);
|
||||
this._resolve(success!);
|
||||
|
||||
this._doCleanup();
|
||||
}
|
||||
|
||||
_doCleanup() {
|
||||
if (this._timeoutTimer)
|
||||
clearTimeout(this._timeoutTimer);
|
||||
this._contextData.rerunnableTasks.delete(this);
|
||||
}
|
||||
|
|
@ -870,13 +876,13 @@ class LifecycleWatcher {
|
|||
private _expectedLifecycle: LifecycleEvent[];
|
||||
private _frame: Frame;
|
||||
private _navigationRequest: network.Request | null = null;
|
||||
private _sameDocumentNavigationCompleteCallback: () => void;
|
||||
private _lifecycleCallback: () => void;
|
||||
private _newDocumentNavigationCompleteCallback: () => void;
|
||||
private _frameDetachedCallback: (err: Error) => void;
|
||||
private _navigationAbortedCallback: (err: Error) => void;
|
||||
private _maximumTimer: NodeJS.Timer;
|
||||
private _hasSameDocumentNavigation: boolean;
|
||||
private _sameDocumentNavigationCompleteCallback: () => void = () => {};
|
||||
private _lifecycleCallback: () => void = () => {};
|
||||
private _newDocumentNavigationCompleteCallback: () => void = () => {};
|
||||
private _frameDetachedCallback: (err: Error) => void = () => {};
|
||||
private _navigationAbortedCallback: (err: Error) => void = () => {};
|
||||
private _maximumTimer?: NodeJS.Timer;
|
||||
private _hasSameDocumentNavigation = false;
|
||||
private _targetUrl: string | undefined;
|
||||
private _expectedDocumentId: string | undefined;
|
||||
private _urlMatch: types.URLMatch | undefined;
|
||||
|
|
@ -971,7 +977,7 @@ class LifecycleWatcher {
|
|||
this._checkLifecycleComplete();
|
||||
}
|
||||
|
||||
navigationResponse(): Promise<network.Response | null> {
|
||||
async navigationResponse(): Promise<network.Response | null> {
|
||||
return this._navigationRequest ? this._navigationRequest._finalRequest._waitForFinished() : null;
|
||||
}
|
||||
|
||||
|
|
@ -1009,6 +1015,7 @@ class LifecycleWatcher {
|
|||
|
||||
dispose() {
|
||||
this._frame._page._frameManager._lifecycleWatchers.delete(this);
|
||||
if (this._maximumTimer)
|
||||
clearTimeout(this._maximumTimer);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,10 +46,10 @@ class Helper {
|
|||
const method = Reflect.get(classType.prototype, methodName);
|
||||
if (methodName === 'constructor' || typeof methodName !== 'string' || methodName.startsWith('_') || typeof method !== 'function' || method.constructor.name !== 'AsyncFunction')
|
||||
continue;
|
||||
Reflect.set(classType.prototype, methodName, function(...args: any[]) {
|
||||
Reflect.set(classType.prototype, methodName, function(this: any, ...args: any[]) {
|
||||
const syncStack: any = {};
|
||||
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 clientStack = stack.substring(stack.indexOf('\n'));
|
||||
if (e instanceof Error && e.stack && !e.stack.includes(clientStack))
|
||||
|
|
@ -112,7 +112,9 @@ class Helper {
|
|||
predicate: Function,
|
||||
timeout: number,
|
||||
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) => {
|
||||
resolveCallback = resolve;
|
||||
rejectCallback = reject;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"noImplicitAny": true
|
||||
"strict": true
|
||||
},
|
||||
"extends": "../../tsconfig.json"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,9 +23,10 @@ export const XPathEngine: SelectorEngine = {
|
|||
name: 'xpath',
|
||||
|
||||
create(root: SelectorRoot, targetElement: Element, type: SelectorType): string | undefined {
|
||||
const document = root instanceof Document ? root : root.ownerDocument;
|
||||
if (!document)
|
||||
const maybeDocument = root instanceof Document ? root : root.ownerDocument;
|
||||
if (!maybeDocument)
|
||||
return;
|
||||
const document = maybeDocument!;
|
||||
|
||||
const xpathCache = new Map<string, Element[]>();
|
||||
if (type === 'notext')
|
||||
|
|
|
|||
|
|
@ -349,7 +349,12 @@ class Engine {
|
|||
for (let [element, boundary] of currentStep) {
|
||||
let next: (Element | SelectorRoot)[] = [];
|
||||
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 === '>') {
|
||||
boundary = element;
|
||||
next = this._matchChildren(element, token, all);
|
||||
|
|
@ -367,7 +372,7 @@ class Engine {
|
|||
}
|
||||
if (element === boundary)
|
||||
break;
|
||||
element = parentOrRoot(element);
|
||||
element = parentOrRoot(element)!;
|
||||
}
|
||||
}
|
||||
for (const nextElement of next) {
|
||||
|
|
|
|||
|
|
@ -137,10 +137,10 @@ export class Keyboard {
|
|||
}
|
||||
|
||||
async type(text: string, options?: { delay?: number }) {
|
||||
const delay = (options && options.delay) || null;
|
||||
const delay = (options && options.delay) || undefined;
|
||||
for (const char of text) {
|
||||
if (keyboardLayout.keyDefinitions[char]) {
|
||||
await this.press(char, {delay});
|
||||
await this.press(char, { delay });
|
||||
} else {
|
||||
if (delay)
|
||||
await new Promise(f => setTimeout(f, delay));
|
||||
|
|
|
|||
|
|
@ -62,16 +62,16 @@ export class JSHandle<T = any> {
|
|||
}
|
||||
|
||||
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) => {
|
||||
return this._context.evaluateHandle(pageFunction, this, ...args);
|
||||
return this._context.evaluateHandle(pageFunction as any, this, ...args);
|
||||
}
|
||||
|
||||
async getProperty(propertyName: string): Promise<JSHandle | null> {
|
||||
const objectHandle = await this.evaluateHandle((object, propertyName) => {
|
||||
const result = {__proto__: null};
|
||||
const objectHandle = await this.evaluateHandle((object: any, propertyName) => {
|
||||
const result: any = {__proto__: null};
|
||||
result[propertyName] = object[propertyName];
|
||||
return result;
|
||||
}, propertyName);
|
||||
|
|
|
|||
|
|
@ -101,17 +101,17 @@ export class Request {
|
|||
private _url: string;
|
||||
private _resourceType: string;
|
||||
private _method: string;
|
||||
private _postData: string;
|
||||
private _postData: string | undefined;
|
||||
private _headers: Headers;
|
||||
private _frame: frames.Frame;
|
||||
private _frame: frames.Frame | null;
|
||||
private _waitForResponsePromise: Promise<Response>;
|
||||
private _waitForResponsePromiseCallback: (value?: Response) => void;
|
||||
private _waitForFinishedPromise: Promise<Response | undefined>;
|
||||
private _waitForFinishedPromiseCallback: (value?: Response | undefined) => void;
|
||||
private _waitForResponsePromiseCallback: (value: Response) => void = () => {};
|
||||
private _waitForFinishedPromise: Promise<Response | null>;
|
||||
private _waitForFinishedPromiseCallback: (value: Response | null) => void = () => {};
|
||||
private _interceptionHandled = false;
|
||||
|
||||
constructor(delegate: RequestDelegate | null, frame: frames.Frame | null, redirectChain: Request[], documentId: string,
|
||||
url: string, resourceType: string, method: string, postData: string, headers: Headers) {
|
||||
constructor(delegate: RequestDelegate | null, frame: frames.Frame | null, redirectChain: Request[], documentId: string | undefined,
|
||||
url: string, resourceType: string, method: string, postData: string | undefined, headers: Headers) {
|
||||
this._delegate = delegate;
|
||||
this._frame = frame;
|
||||
this._redirectChain = redirectChain;
|
||||
|
|
@ -130,7 +130,7 @@ export class Request {
|
|||
|
||||
_setFailureText(failureText: string) {
|
||||
this._failureText = failureText;
|
||||
this._waitForFinishedPromiseCallback();
|
||||
this._waitForFinishedPromiseCallback(null);
|
||||
}
|
||||
|
||||
url(): string {
|
||||
|
|
@ -157,7 +157,7 @@ export class Request {
|
|||
return this._response;
|
||||
}
|
||||
|
||||
async _waitForFinished(): Promise<Response | undefined> {
|
||||
async _waitForFinished(): Promise<Response | null> {
|
||||
return this._waitForFinishedPromise;
|
||||
}
|
||||
|
||||
|
|
@ -200,7 +200,7 @@ export class Request {
|
|||
assert(this._delegate, 'Request Interception is not enabled!');
|
||||
assert(!this._interceptionHandled, 'Request is already handled!');
|
||||
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.
|
||||
|
|
@ -209,7 +209,7 @@ export class Request {
|
|||
assert(this._delegate, 'Request Interception is not enabled!');
|
||||
assert(!this._interceptionHandled, 'Request is already handled!');
|
||||
this._interceptionHandled = true;
|
||||
await this._delegate.fulfill(response);
|
||||
await this._delegate!.fulfill(response);
|
||||
}
|
||||
|
||||
async continue(overrides: { headers?: { [key: string]: string } } = {}) {
|
||||
|
|
@ -218,7 +218,7 @@ export class Request {
|
|||
return;
|
||||
assert(this._delegate, 'Request Interception is not enabled!');
|
||||
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>;
|
||||
getContentQuads(handle: dom.ElementHandle): Promise<types.Quad[] | null>;
|
||||
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>;
|
||||
|
||||
getAccessibilityTree(): Promise<accessibility.AXNode>;
|
||||
|
|
@ -114,7 +114,9 @@ export class Page extends platform.EventEmitter {
|
|||
constructor(delegate: PageDelegate, browserContext: BrowserContext) {
|
||||
super();
|
||||
this._delegate = delegate;
|
||||
this._closedCallback = () => {};
|
||||
this._closedPromise = new Promise(f => this._closedCallback = f);
|
||||
this._disconnectedCallback = () => {};
|
||||
this._disconnectedPromise = new Promise(f => this._disconnectedCallback = f);
|
||||
this._browserContext = browserContext;
|
||||
this._state = {
|
||||
|
|
@ -160,7 +162,7 @@ export class Page extends platform.EventEmitter {
|
|||
}
|
||||
|
||||
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)) {
|
||||
await handle.dispose();
|
||||
return;
|
||||
|
|
@ -197,10 +199,10 @@ export class Page extends platform.EventEmitter {
|
|||
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();
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
@ -270,7 +272,7 @@ export class Page extends platform.EventEmitter {
|
|||
const {name, seq, args} = JSON.parse(payload);
|
||||
let expression = null;
|
||||
try {
|
||||
const result = await this._pageBindings.get(name)(...args);
|
||||
const result = await this._pageBindings.get(name)!(...args);
|
||||
expression = helper.evaluationString(deliverResult, name, seq, result);
|
||||
} catch (error) {
|
||||
if (error instanceof Error)
|
||||
|
|
@ -493,7 +495,7 @@ export class Page extends platform.EventEmitter {
|
|||
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);
|
||||
}
|
||||
|
||||
|
|
@ -531,10 +533,11 @@ export class Worker {
|
|||
private _url: string;
|
||||
private _executionContextPromise: Promise<js.ExecutionContext>;
|
||||
private _executionContextCallback: (value?: js.ExecutionContext) => void;
|
||||
_existingExecutionContext: js.ExecutionContext | null;
|
||||
_existingExecutionContext: js.ExecutionContext | null = null;
|
||||
|
||||
constructor(url: string) {
|
||||
this._url = url;
|
||||
this._executionContextCallback = () => {};
|
||||
this._executionContextPromise = new Promise(x => this._executionContextCallback = x);
|
||||
return helper.logPublicApiCalls('worker', this);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,9 +23,9 @@ export const isNode = typeof process === 'object' && !!process && typeof process
|
|||
|
||||
export function promisify(nodeFunction: Function): Function {
|
||||
assert(isNode);
|
||||
function promisified(...args) {
|
||||
function promisified(...args: any[]) {
|
||||
return new Promise((resolve, reject) => {
|
||||
function callback(err, ...result) {
|
||||
function callback(err: any, ...result: any[]) {
|
||||
if (err)
|
||||
return reject(err);
|
||||
if (result.length === 1)
|
||||
|
|
@ -200,7 +200,7 @@ export async function closeFdAsync(fd: number): Promise<void> {
|
|||
return await promisify(nodeFS.close)(fd);
|
||||
}
|
||||
|
||||
export function getMimeType(file: string): string {
|
||||
export function getMimeType(file: string): string | null {
|
||||
assertFileAccess();
|
||||
return mime.getType(file);
|
||||
}
|
||||
|
|
@ -228,7 +228,7 @@ export function pngToJpeg(buffer: Buffer): Buffer {
|
|||
|
||||
function nodeFetch(url: string): Promise<string> {
|
||||
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 endpointURL = new URL(url);
|
||||
|
|
|
|||
|
|
@ -29,10 +29,10 @@ export class Screenshotter {
|
|||
this._page = page;
|
||||
|
||||
const browserContext = page.browserContext();
|
||||
this._queue = browserContext[taskQueueSymbol];
|
||||
this._queue = (browserContext as any)[taskQueueSymbol];
|
||||
if (!this._queue) {
|
||||
this._queue = new TaskQueue();
|
||||
browserContext[taskQueueSymbol] = this._queue;
|
||||
(browserContext as any)[taskQueueSymbol] = this._queue;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -45,7 +45,7 @@ export class Screenshotter {
|
|||
if (!viewport) {
|
||||
viewportSize = await this._page.evaluate(() => {
|
||||
if (!document.body || !document.documentElement)
|
||||
return null;
|
||||
return;
|
||||
return {
|
||||
width: Math.max(document.body.offsetWidth, document.documentElement.offsetWidth),
|
||||
height: Math.max(document.body.offsetHeight, document.documentElement.offsetHeight),
|
||||
|
|
@ -79,13 +79,13 @@ export class Screenshotter {
|
|||
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 (viewport)
|
||||
await this._page.setViewport(viewport);
|
||||
else
|
||||
await this._page._delegate.resetViewport(viewportSize);
|
||||
await this._page._delegate.resetViewport(viewportSize!);
|
||||
}
|
||||
return result;
|
||||
}).catch(rewriteError);
|
||||
|
|
@ -97,12 +97,15 @@ export class Screenshotter {
|
|||
return this._queue.postTask(async () => {
|
||||
let overridenViewport: types.Viewport | undefined;
|
||||
|
||||
let boundingBox = await this._page._delegate.getBoundingBoxForScreenshot(handle);
|
||||
assert(boundingBox, 'Node is either not visible or not an HTMLElement');
|
||||
let maybeBoundingBox = await this._page._delegate.getBoundingBoxForScreenshot(handle);
|
||||
assert(maybeBoundingBox, 'Node is either not visible or not an HTMLElement');
|
||||
let boundingBox = maybeBoundingBox!;
|
||||
assert(boundingBox.width !== 0, 'Node has 0 width.');
|
||||
assert(boundingBox.height !== 0, 'Node has 0 height.');
|
||||
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 (boundingBox.width > viewport.width || boundingBox.height > viewport.height) {
|
||||
|
|
@ -115,7 +118,9 @@ export class Screenshotter {
|
|||
}
|
||||
|
||||
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)
|
||||
|
|
@ -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)
|
||||
return clip;
|
||||
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> {
|
||||
const url = this._params(this._platform, revision).downloadUrl;
|
||||
let resolve;
|
||||
let resolve: (result: boolean) => void = () => {};
|
||||
const promise = new Promise<boolean>(x => resolve = x);
|
||||
const request = httpRequest(url, 'HEAD', response => {
|
||||
resolve(response.statusCode === 200);
|
||||
});
|
||||
request.on('error', error => {
|
||||
request.on('error', (error: any) => {
|
||||
console.error(error);
|
||||
resolve(false);
|
||||
});
|
||||
|
|
@ -92,8 +92,8 @@ export class BrowserFetcher {
|
|||
async localRevisions(): Promise<string[]> {
|
||||
if (!await existsAsync(this._downloadsFolder))
|
||||
return [];
|
||||
const fileNames = await readdirAsync(this._downloadsFolder);
|
||||
return fileNames.map(fileName => parseFolderPath(fileName)).filter(entry => entry && entry.platform === this._platform).map(entry => entry.revision);
|
||||
const fileNames: string[] = await readdirAsync(this._downloadsFolder);
|
||||
return fileNames.map(fileName => parseFolderPath(fileName)).filter(entry => entry && entry.platform === this._platform).map(entry => entry!.revision);
|
||||
}
|
||||
|
||||
async remove(revision: string = this._preferredRevision) {
|
||||
|
|
@ -123,8 +123,9 @@ function parseFolderPath(folderPath: string): { platform: string; revision: stri
|
|||
return {platform, revision};
|
||||
}
|
||||
|
||||
function downloadFile(url: string, destinationPath: string, progressCallback: OnProgressCallback | null): Promise<any> {
|
||||
let fulfill, reject;
|
||||
function downloadFile(url: string, destinationPath: string, progressCallback: OnProgressCallback | undefined): Promise<any> {
|
||||
let fulfill: () => void = () => {};
|
||||
let reject: (error: any) => void = () => {};
|
||||
let downloadedBytes = 0;
|
||||
let totalBytes = 0;
|
||||
|
||||
|
|
@ -146,12 +147,12 @@ function downloadFile(url: string, destinationPath: string, progressCallback: On
|
|||
if (progressCallback)
|
||||
response.on('data', onData);
|
||||
});
|
||||
request.on('error', error => reject(error));
|
||||
request.on('error', (error: any) => reject(error));
|
||||
return promise;
|
||||
|
||||
function onData(chunk) {
|
||||
function onData(chunk: string) {
|
||||
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)
|
||||
httpRequest(res.headers.location, method, response);
|
||||
else
|
||||
|
|
|
|||
|
|
@ -144,7 +144,7 @@ export class CRPlaywright implements Playwright {
|
|||
const usePipe = chromeArguments.includes('--remote-debugging-pipe');
|
||||
|
||||
const { launchedProcess, gracefullyClose } = await launchProcess({
|
||||
executablePath: chromeExecutable,
|
||||
executablePath: chromeExecutable!,
|
||||
args: chromeArguments,
|
||||
env,
|
||||
handleSIGINT,
|
||||
|
|
@ -152,7 +152,7 @@ export class CRPlaywright implements Playwright {
|
|||
handleSIGHUP,
|
||||
dumpio,
|
||||
pipe: usePipe,
|
||||
tempDir: temporaryUserDataDir,
|
||||
tempDir: temporaryUserDataDir || undefined,
|
||||
attemptToGracefullyClose: async () => {
|
||||
if (!connectOptions)
|
||||
return Promise.reject();
|
||||
|
|
@ -246,9 +246,9 @@ export class CRPlaywright implements Playwright {
|
|||
...defaultOptions,
|
||||
...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 executablePath = '';
|
||||
if (platform === 'linux') {
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ export class FFPlaywright implements Playwright {
|
|||
handleSIGHUP,
|
||||
dumpio,
|
||||
pipe: false,
|
||||
tempDir: temporaryProfileDir,
|
||||
tempDir: temporaryProfileDir || undefined,
|
||||
attemptToGracefullyClose: async () => {
|
||||
if (!connectOptions)
|
||||
return Promise.reject();
|
||||
|
|
@ -232,9 +232,9 @@ export class FFPlaywright implements Playwright {
|
|||
...defaultOptions,
|
||||
...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 = '';
|
||||
if (platform === 'linux')
|
||||
executablePath = path.join('firefox', 'firefox');
|
||||
|
|
@ -243,7 +243,7 @@ export class FFPlaywright implements Playwright {
|
|||
else if (platform === 'win32' || platform === 'win64')
|
||||
executablePath = path.join('firefox', 'firefox.exe');
|
||||
return {
|
||||
downloadUrl: util.format(downloadURLs[platform], options.host, revision),
|
||||
downloadUrl: util.format((downloadURLs as any)[platform], options.host, revision),
|
||||
executablePath
|
||||
};
|
||||
});
|
||||
|
|
@ -465,8 +465,8 @@ const DEFAULT_PREFERENCES = {
|
|||
|
||||
async function createProfile(extraPrefs?: object): Promise<string> {
|
||||
const profilePath = await mkdtempAsync(path.join(os.tmpdir(), 'playwright_dev_firefox_profile-'));
|
||||
const prefsJS = [];
|
||||
const userJS = [];
|
||||
const prefsJS: string[] = [];
|
||||
const userJS: string[] = [];
|
||||
|
||||
const prefs = { ...DEFAULT_PREFERENCES, ...extraPrefs };
|
||||
for (const [key, value] of Object.entries(prefs))
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import { debugError, helper, RegisteredListener } from '../helper';
|
|||
import { ConnectionTransport } from '../transport';
|
||||
|
||||
export class PipeTransport implements ConnectionTransport {
|
||||
private _pipeWrite: NodeJS.WritableStream;
|
||||
private _pipeWrite: NodeJS.WritableStream | null;
|
||||
private _pendingMessage = '';
|
||||
private _eventListeners: RegisteredListener[];
|
||||
onmessage?: (message: string) => void;
|
||||
|
|
@ -36,13 +36,13 @@ export class PipeTransport implements ConnectionTransport {
|
|||
helper.addEventListener(pipeRead, 'error', debugError),
|
||||
helper.addEventListener(pipeWrite, 'error', debugError),
|
||||
];
|
||||
this.onmessage = null;
|
||||
this.onclose = null;
|
||||
this.onmessage = undefined;
|
||||
this.onclose = undefined;
|
||||
}
|
||||
|
||||
send(message: string) {
|
||||
this._pipeWrite.write(message);
|
||||
this._pipeWrite.write('\0');
|
||||
this._pipeWrite!.write(message);
|
||||
this._pipeWrite!.write('\0');
|
||||
}
|
||||
|
||||
_dispatch(buffer: Buffer) {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ const removeFolderAsync = platform.promisify(removeFolder);
|
|||
export type LaunchProcessOptions = {
|
||||
executablePath: string,
|
||||
args: string[],
|
||||
env?: {[key: string]: string},
|
||||
env?: {[key: string]: string | undefined},
|
||||
|
||||
handleSIGINT?: boolean,
|
||||
handleSIGTERM?: boolean,
|
||||
|
|
@ -133,6 +133,7 @@ export async function launchProcess(options: LaunchProcessOptions): Promise<Laun
|
|||
}
|
||||
// Attempt to remove temporary profile directory to avoid littering.
|
||||
try {
|
||||
if (options.tempDir)
|
||||
removeFolder.sync(options.tempDir);
|
||||
} 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;
|
||||
|
||||
const { launchedProcess, gracefullyClose } = await launchProcess({
|
||||
executablePath: webkitExecutable,
|
||||
executablePath: webkitExecutable!,
|
||||
args: webkitArguments,
|
||||
env,
|
||||
handleSIGINT,
|
||||
|
|
@ -130,7 +130,6 @@ export class WKPlaywright implements Playwright {
|
|||
handleSIGHUP,
|
||||
dumpio,
|
||||
pipe: true,
|
||||
tempDir: null,
|
||||
attemptToGracefullyClose: async () => {
|
||||
if (!connectOptions)
|
||||
return Promise.reject();
|
||||
|
|
@ -191,13 +190,13 @@ export class WKPlaywright implements Playwright {
|
|||
...defaultOptions,
|
||||
...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 {
|
||||
downloadUrl: (platform === 'mac') ?
|
||||
util.format(downloadURLs[platform], options.host, revision, getMacVersion()) :
|
||||
util.format(downloadURLs[platform], options.host, revision),
|
||||
util.format(downloadURLs[platform], options!.host, revision, getMacVersion()) :
|
||||
util.format((downloadURLs as any)[platform], options!.host, revision),
|
||||
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() {
|
||||
if (!cachedMacVersion) {
|
||||
const [major, minor] = execSync('sw_vers -productVersion').toString('utf8').trim().split('.');
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ export class SlowMoTransport {
|
|||
const message = this._incomingMessageQueue.shift();
|
||||
try {
|
||||
if (this.onmessage)
|
||||
this.onmessage(message);
|
||||
this.onmessage(message!);
|
||||
} finally {
|
||||
this._scheduleQueueDispatch();
|
||||
}
|
||||
|
|
@ -77,8 +77,8 @@ export class SlowMoTransport {
|
|||
if (this.onclose)
|
||||
this.onclose();
|
||||
this._closed = true;
|
||||
this._delegate.onmessage = null;
|
||||
this._delegate.onclose = null;
|
||||
this._delegate.onmessage = undefined;
|
||||
this._delegate.onclose = undefined;
|
||||
}
|
||||
|
||||
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) {
|
||||
this._firstPageProxyCallback();
|
||||
this._firstPageProxyCallback = null;
|
||||
this._firstPageProxyCallback = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
_onPageProxyDestroyed(event: Protocol.Browser.pageProxyDestroyedPayload) {
|
||||
const pageProxyId = event.pageProxyId;
|
||||
const pageProxy = this._pageProxies.get(pageProxyId);
|
||||
const pageProxy = this._pageProxies.get(pageProxyId)!;
|
||||
pageProxy.didClose();
|
||||
pageProxy.dispose();
|
||||
this._pageProxies.delete(pageProxyId);
|
||||
}
|
||||
|
||||
_onPageProxyMessageReceived(event: PageProxyMessageReceivedPayload) {
|
||||
const pageProxy = this._pageProxies.get(event.pageProxyId);
|
||||
const pageProxy = this._pageProxies.get(event.pageProxyId)!;
|
||||
pageProxy.dispatchMessageToSession(event.message);
|
||||
}
|
||||
|
||||
|
|
@ -166,14 +166,14 @@ export class WKBrowser extends platform.EventEmitter implements Browser {
|
|||
|
||||
newPage: async (): Promise<Page> => {
|
||||
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();
|
||||
},
|
||||
|
||||
close: async (): Promise<void> => {
|
||||
assert(browserContextId, 'Non-incognito profiles cannot be closed!');
|
||||
await this._browserSession.send('Browser.deleteContext', { browserContextId });
|
||||
this._contexts.delete(browserContextId);
|
||||
await this._browserSession.send('Browser.deleteContext', { browserContextId: browserContextId! });
|
||||
this._contexts.delete(browserContextId!);
|
||||
},
|
||||
|
||||
cookies: async (): Promise<network.NetworkCookie[]> => {
|
||||
|
|
|
|||
|
|
@ -77,8 +77,8 @@ export class WKConnection {
|
|||
if (this._closed)
|
||||
return;
|
||||
this._closed = true;
|
||||
this._transport.onmessage = null;
|
||||
this._transport.onclose = null;
|
||||
this._transport.onmessage = undefined;
|
||||
this._transport.onclose = undefined;
|
||||
this.browserSession.dispose();
|
||||
this._onDisconnect();
|
||||
}
|
||||
|
|
@ -90,10 +90,11 @@ export class WKConnection {
|
|||
}
|
||||
|
||||
export class WKSession extends platform.EventEmitter {
|
||||
connection?: WKConnection;
|
||||
connection: WKConnection;
|
||||
errorText: string;
|
||||
readonly sessionId: string;
|
||||
|
||||
private _disposed = false;
|
||||
private readonly _rawSend: (message: any) => void;
|
||||
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._rawSend = rawSend;
|
||||
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>(
|
||||
method: T,
|
||||
params?: Protocol.CommandParameters[T]
|
||||
): Promise<Protocol.CommandReturnValues[T]> {
|
||||
if (!this.connection)
|
||||
if (this._disposed)
|
||||
return Promise.reject(new Error(`Protocol error (${method}): ${this.errorText}`));
|
||||
const id = this.connection.nextMessageId();
|
||||
const messageObj = { id, method, params };
|
||||
|
|
@ -128,20 +135,20 @@ export class WKSession extends platform.EventEmitter {
|
|||
}
|
||||
|
||||
isDisposed(): boolean {
|
||||
return !this.connection;
|
||||
return this._disposed;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
for (const callback of this._callbacks.values())
|
||||
callback.reject(rewriteError(callback.error, `Protocol error (${callback.method}): ${this.errorText}`));
|
||||
this._callbacks.clear();
|
||||
this.connection = undefined;
|
||||
this._disposed = true;
|
||||
}
|
||||
|
||||
dispatchMessage(object: any) {
|
||||
debugWrappedMessage('◀ RECV ' + JSON.stringify(object, null, 2));
|
||||
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);
|
||||
if (object.error)
|
||||
callback.reject(createProtocolError(callback.error, callback.method, object));
|
||||
|
|
|
|||
|
|
@ -28,14 +28,13 @@ export class WKExecutionContext implements js.ExecutionContextDelegate {
|
|||
private _globalObjectId?: Promise<string>;
|
||||
_session: WKSession;
|
||||
_contextId: number | undefined;
|
||||
private _contextDestroyedCallback: () => void;
|
||||
private _contextDestroyedCallback: () => void = () => {};
|
||||
private _executionContextDestroyedPromise: Promise<unknown>;
|
||||
_jsonStringifyObjectId: Protocol.Runtime.RemoteObjectId | undefined;
|
||||
|
||||
constructor(client: WKSession, contextId: number | undefined) {
|
||||
this._session = client;
|
||||
this._contextId = contextId;
|
||||
this._contextDestroyedCallback = null;
|
||||
this._executionContextDestroyedPromise = new Promise((resolve, reject) => {
|
||||
this._contextDestroyedCallback = resolve;
|
||||
});
|
||||
|
|
@ -59,7 +58,7 @@ export class WKExecutionContext implements js.ExecutionContextDelegate {
|
|||
if (response.result.type === 'object' && response.result.className === 'Promise') {
|
||||
return Promise.race([
|
||||
this._executionContextDestroyedPromise.then(() => contextDestroyedResult),
|
||||
this._awaitPromise(response.result.objectId),
|
||||
this._awaitPromise(response.result.objectId!),
|
||||
]);
|
||||
}
|
||||
return response;
|
||||
|
|
@ -131,7 +130,7 @@ export class WKExecutionContext implements js.ExecutionContextDelegate {
|
|||
if (response.result.type === 'object' && response.result.className === 'Promise') {
|
||||
return Promise.race([
|
||||
this._executionContextDestroyedPromise.then(() => contextDestroyedResult),
|
||||
this._awaitPromise(response.result.objectId),
|
||||
this._awaitPromise(response.result.objectId!),
|
||||
]);
|
||||
}
|
||||
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 e;
|
||||
}).then(response => {
|
||||
return response.result.objectId;
|
||||
return response.result.objectId!;
|
||||
});
|
||||
}
|
||||
return this._globalObjectId;
|
||||
|
|
@ -233,8 +232,11 @@ export class WKExecutionContext implements js.ExecutionContextDelegate {
|
|||
}
|
||||
|
||||
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', {
|
||||
objectId: toRemoteObject(handle).objectId,
|
||||
objectId,
|
||||
ownProperties: true
|
||||
});
|
||||
const result = new Map();
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ function toModifiersMask(modifiers: Set<input.Modifier>): number {
|
|||
|
||||
export class RawKeyboardImpl implements input.RawKeyboard {
|
||||
private readonly _pageProxySession: WKSession;
|
||||
private _session: WKSession;
|
||||
private _session?: WKSession;
|
||||
|
||||
constructor(session: WKSession) {
|
||||
this._pageProxySession = session;
|
||||
|
|
@ -83,7 +83,7 @@ export class RawKeyboardImpl implements input.RawKeyboard {
|
|||
}
|
||||
|
||||
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) {
|
||||
this._page = page;
|
||||
this._pageProxySession = pageProxySession;
|
||||
this._session = undefined as any as WKSession;
|
||||
}
|
||||
|
||||
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
|
||||
const isNavigationRequest = event.type === 'Document';
|
||||
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._page._frameManager.requestStarted(request.request);
|
||||
}
|
||||
|
||||
_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 {
|
||||
|
|
@ -141,8 +142,9 @@ export class WKNetworkManager {
|
|||
|
||||
// Under certain conditions we never get the Network.responseReceived
|
||||
// event from protocol. @see https://crbug.com/883475
|
||||
if (request.request.response())
|
||||
request.request.response()._requestFinished();
|
||||
const response = request.request.response();
|
||||
if (response)
|
||||
response._requestFinished();
|
||||
this._requestIdToRequest.delete(request._requestId);
|
||||
this._page._frameManager.requestFinished(request.request);
|
||||
}
|
||||
|
|
@ -192,7 +194,7 @@ class InterceptableRequest implements network.RequestDelegate {
|
|||
readonly request: network.Request;
|
||||
_requestId: string;
|
||||
_documentId: string | undefined;
|
||||
_interceptedCallback: () => void;
|
||||
_interceptedCallback: () => void = () => {};
|
||||
private _interceptedPromise: Promise<unknown>;
|
||||
|
||||
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._networkManager = new WKNetworkManager(this._page, pageProxySession);
|
||||
this._workers = new WKWorkers(this._page);
|
||||
this._session = undefined as any as WKSession;
|
||||
}
|
||||
|
||||
async _initializePageProxySession() {
|
||||
|
|
@ -173,7 +174,7 @@ export class WKPage implements PageDelegate {
|
|||
}
|
||||
|
||||
_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);
|
||||
if (!frameTree.childFrames)
|
||||
return;
|
||||
|
|
@ -233,8 +234,8 @@ export class WKPage implements PageDelegate {
|
|||
async _onConsoleMessage(event: Protocol.Console.messageAddedPayload) {
|
||||
const { type, level, text, parameters, url, line: lineNumber, column: columnNumber, source } = event.message;
|
||||
if (level === 'debug' && parameters && parameters[0].value === BINDING_CALL_MESSAGE) {
|
||||
const parsedObjectId = JSON.parse(parameters[1].objectId);
|
||||
const context = this._contextIdToContext.get(parsedObjectId.injectedScriptId);
|
||||
const parsedObjectId = JSON.parse(parameters[1].objectId!);
|
||||
const context = this._contextIdToContext.get(parsedObjectId.injectedScriptId)!;
|
||||
this._page._onBindingCalled(parameters[2].value, context);
|
||||
return;
|
||||
}
|
||||
|
|
@ -245,7 +246,7 @@ export class WKPage implements PageDelegate {
|
|||
return;
|
||||
}
|
||||
|
||||
let derivedType: string = type;
|
||||
let derivedType: string = type || '';
|
||||
if (type === 'log')
|
||||
derivedType = level;
|
||||
else if (type === 'timing')
|
||||
|
|
@ -256,13 +257,13 @@ export class WKPage implements PageDelegate {
|
|||
let context: dom.FrameExecutionContext | null = null;
|
||||
if (p.objectId) {
|
||||
const objectId = JSON.parse(p.objectId);
|
||||
context = this._contextIdToContext.get(objectId.injectedScriptId);
|
||||
context = this._contextIdToContext.get(objectId.injectedScriptId)!;
|
||||
} else {
|
||||
context = mainFrameContext;
|
||||
}
|
||||
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) {
|
||||
|
|
@ -276,7 +277,7 @@ export class WKPage implements PageDelegate {
|
|||
}
|
||||
|
||||
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()!;
|
||||
this._page._onFileChooserOpened(handle);
|
||||
}
|
||||
|
|
@ -418,7 +419,7 @@ export class WKPage implements PageDelegate {
|
|||
|
||||
async getContentFrame(handle: dom.ElementHandle): Promise<frames.Frame | null> {
|
||||
const nodeInfo = await this._session.send('DOM.describeNode', {
|
||||
objectId: toRemoteObject(handle).objectId
|
||||
objectId: toRemoteObject(handle).objectId!
|
||||
});
|
||||
if (!nodeInfo.contentFrameId)
|
||||
return null;
|
||||
|
|
@ -462,7 +463,7 @@ export class WKPage implements PageDelegate {
|
|||
|
||||
async getContentQuads(handle: dom.ElementHandle): Promise<types.Quad[] | null> {
|
||||
const result = await this._session.send('DOM.getContentQuads', {
|
||||
objectId: toRemoteObject(handle).objectId
|
||||
objectId: toRemoteObject(handle).objectId!
|
||||
}).catch(debugError);
|
||||
if (!result)
|
||||
return null;
|
||||
|
|
@ -478,8 +479,8 @@ export class WKPage implements PageDelegate {
|
|||
return this._page.evaluate(() => ({ width: innerWidth, height: innerHeight }));
|
||||
}
|
||||
|
||||
async setInputFiles(handle: dom.ElementHandle, files: types.FilePayload[]): Promise<void> {
|
||||
const objectId = toRemoteObject(handle).objectId;
|
||||
async setInputFiles(handle: dom.ElementHandle<HTMLInputElement>, files: types.FilePayload[]): Promise<void> {
|
||||
const objectId = toRemoteObject(handle).objectId!;
|
||||
await this._session.send('DOM.setInputFiles', { objectId, files });
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ export class WKPageProxy {
|
|||
private _pagePromise: Promise<Page> | null = null;
|
||||
private _wkPage: WKPage | null = null;
|
||||
private readonly _firstTargetPromise: Promise<void>;
|
||||
private _firstTargetCallback: () => void;
|
||||
private _firstTargetCallback?: () => void;
|
||||
private readonly _sessions = new Map<string, WKSession>();
|
||||
private readonly _eventListeners: RegisteredListener[];
|
||||
|
||||
|
|
@ -71,16 +71,15 @@ export class WKPageProxy {
|
|||
}
|
||||
|
||||
onPopupCreated(popupPageProxy: WKPageProxy) {
|
||||
if (!this._wkPage)
|
||||
const wkPage = this._wkPage;
|
||||
if (!wkPage || !wkPage._page.listenerCount(Events.Page.Popup))
|
||||
return;
|
||||
if (!this._wkPage._page.listenerCount(Events.Page.Popup))
|
||||
return;
|
||||
popupPageProxy.page().then(page => this._wkPage._page.emit(Events.Page.Popup, page));
|
||||
popupPageProxy.page().then(page => wkPage._page.emit(Events.Page.Popup, page));
|
||||
}
|
||||
|
||||
private async _initializeWKPage(): Promise<Page> {
|
||||
await this._firstTargetPromise;
|
||||
let session: WKSession;
|
||||
let session: WKSession | undefined;
|
||||
for (const anySession of this._sessions.values()) {
|
||||
if (!(anySession as any)[provisionalMessagesSymbol]) {
|
||||
session = anySession;
|
||||
|
|
@ -89,10 +88,10 @@ export class WKPageProxy {
|
|||
}
|
||||
assert(session, 'One non-provisional target session must exist');
|
||||
this._wkPage = new WKPage(this._browserContext, this._pageProxySession);
|
||||
this._wkPage.setSession(session);
|
||||
this._wkPage.setSession(session!);
|
||||
await Promise.all([
|
||||
this._wkPage._initializePageProxySession(),
|
||||
this._wkPage._initializeSession(session, false),
|
||||
this._wkPage._initializeSession(session!, false),
|
||||
]);
|
||||
return this._wkPage._page;
|
||||
}
|
||||
|
|
@ -110,7 +109,7 @@ export class WKPageProxy {
|
|||
this._sessions.set(targetInfo.targetId, session);
|
||||
if (this._firstTargetCallback) {
|
||||
this._firstTargetCallback();
|
||||
this._firstTargetCallback = null;
|
||||
this._firstTargetCallback = undefined;
|
||||
}
|
||||
if (targetInfo.isProvisional)
|
||||
(session as any)[provisionalMessagesSymbol] = [];
|
||||
|
|
@ -138,7 +137,7 @@ export class WKPageProxy {
|
|||
if (provisionalMessages)
|
||||
provisionalMessages.push(message);
|
||||
else
|
||||
session.dispatchMessage(JSON.parse(message));
|
||||
session!.dispatchMessage(JSON.parse(message));
|
||||
}
|
||||
|
||||
private _onDidCommitProvisionalTarget(event: Protocol.Target.didCommitProvisionalTargetPayload) {
|
||||
|
|
@ -148,12 +147,12 @@ export class WKPageProxy {
|
|||
const oldSession = this._sessions.get(oldTargetId);
|
||||
assert(oldSession, 'Unknown old target: ' + oldTargetId);
|
||||
// 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];
|
||||
assert(provisionalMessages, 'Committing target must be provisional');
|
||||
(newSession as any)[provisionalMessagesSymbol] = undefined;
|
||||
for (const message of provisionalMessages)
|
||||
newSession.dispatchMessage(JSON.parse(message));
|
||||
this._wkPage.setSession(newSession);
|
||||
newSession!.dispatchMessage(JSON.parse(message));
|
||||
this._wkPage!.setSession(newSession!);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,11 +61,11 @@ export class WKWorkers {
|
|||
}
|
||||
}),
|
||||
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));
|
||||
}),
|
||||
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();
|
||||
this._workerSessions.delete(event.workerId);
|
||||
this._page._removeWorker(event.workerId);
|
||||
|
|
@ -79,15 +79,15 @@ export class WKWorkers {
|
|||
|
||||
async _onConsoleMessage(worker: Worker, event: Protocol.Console.messageAddedPayload) {
|
||||
const { type, level, text, parameters, url, line: lineNumber, column: columnNumber } = event.message;
|
||||
let derivedType: string = type;
|
||||
let derivedType: string = type || '';
|
||||
if (type === 'log')
|
||||
derivedType = level;
|
||||
else if (type === 'timing')
|
||||
derivedType = 'timeEnd';
|
||||
|
||||
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();
|
||||
|
||||
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}) => {
|
||||
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');
|
||||
const trace = await browser.stopTracing();
|
||||
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}) => {
|
||||
await browser.startTracing(page);
|
||||
|
|
@ -87,7 +87,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||
await browser.startTracing(page, {screenshots: true});
|
||||
await page.goto(server.PREFIX + '/grid.html');
|
||||
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