chore: more strict type checking (#466)

This commit is contained in:
Dmitry Gozman 2020-01-13 13:33:25 -08:00 committed by Pavel Feldman
parent d19f10ef42
commit a1d1f26fb7
45 changed files with 345 additions and 277 deletions

View file

@ -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",

View file

@ -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)

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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));

View file

@ -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) {

View file

@ -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();

View file

@ -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);
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,7 +201,8 @@ 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);
this._attemptedAuthentications.delete(request._interceptionId);
if (request._interceptionId)
this._attemptedAuthentications.delete(request._interceptionId);
this._page._frameManager.requestReceivedResponse(response);
this._page._frameManager.requestFinished(request.request);
}
@ -222,10 +225,12 @@ 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);
this._attemptedAuthentications.delete(request._interceptionId);
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);
this._attemptedAuthentications.delete(request._interceptionId);
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

View file

@ -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;
}

View file

@ -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);
}
}

View file

@ -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!;
}
}

View file

@ -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> {

View file

@ -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));

View file

@ -1,6 +1,6 @@
{
"compilerOptions": {
"noImplicitAny": true
"strict": true
},
"extends": "../../tsconfig.json"
}

View file

@ -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) => {

View file

@ -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,13 +856,14 @@ class RerunnableTask {
if (error)
this._reject(error);
else
this._resolve(success);
this._resolve(success!);
this._doCleanup();
}
_doCleanup() {
clearTimeout(this._timeoutTimer);
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,7 +1015,8 @@ class LifecycleWatcher {
dispose() {
this._frame._page._frameManager._lifecycleWatchers.delete(this);
clearTimeout(this._maximumTimer);
if (this._maximumTimer)
clearTimeout(this._maximumTimer);
}
}

View file

@ -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;

View file

@ -1,6 +1,6 @@
{
"compilerOptions": {
"noImplicitAny": true
"strict": true
},
"extends": "../../tsconfig.json"
}

View file

@ -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')

View file

@ -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) {

View file

@ -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));

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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);
}

View file

@ -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);

View file

@ -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) };

View file

@ -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

View file

@ -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') {

View file

@ -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))

View file

@ -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) {

View file

@ -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,7 +133,8 @@ export async function launchProcess(options: LaunchProcessOptions): Promise<Laun
}
// Attempt to remove temporary profile directory to avoid littering.
try {
removeFolder.sync(options.tempDir);
if (options.tempDir)
removeFolder.sync(options.tempDir);
} catch (e) { }
}
}

6
src/server/tsconfig.json Normal file
View file

@ -0,0 +1,6 @@
{
"compilerOptions": {
"strict": true
},
"extends": "../../tsconfig.json"
}

View file

@ -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('.');

View file

@ -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
View file

@ -0,0 +1,6 @@
{
"compilerOptions": {
"strict": true
},
"extends": "../../tsconfig.json"
}

View file

@ -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[]> => {

View file

@ -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));

View file

@ -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();

View file

@ -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 });
}
}

View file

@ -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) {

View file

@ -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 });
}

View file

@ -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!);
}
}

View file

@ -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);
}
}

View file

@ -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');
});
});
};