Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(firefox): migrate onto ExecutionContext events #4064

Merged
merged 2 commits into from Feb 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 13 additions & 6 deletions experimental/puppeteer-firefox/lib/ExecutionContext.js
Expand Up @@ -15,10 +15,10 @@ class ExecutionContext {

async evaluateHandle(pageFunction, ...args) {
if (helper.isString(pageFunction)) {
const payload = await this._session.send('Page.evaluate', {
script: pageFunction,
const payload = await this._session.send('Runtime.evaluate', {
expression: pageFunction,
executionContextId: this._executionContextId,
});
}).catch(rewriteError);
return createHandle(this, payload.result, payload.exceptionDetails);
}
if (typeof pageFunction !== 'function')
Expand Down Expand Up @@ -59,10 +59,10 @@ class ExecutionContext {
return {unserializableValue: 'NaN'};
return {value: arg};
});
let payload = null;
let callFunctionPromise;
try {
payload = await this._session.send('Page.evaluate', {
functionText,
callFunctionPromise = this._session.send('Runtime.callFunction', {
functionDeclaration: functionText,
args,
executionContextId: this._executionContextId
});
Expand All @@ -71,7 +71,14 @@ class ExecutionContext {
err.message += ' Are you passing a nested JSHandle?';
throw err;
}
const payload = await callFunctionPromise.catch(rewriteError);
return createHandle(this, payload.result, payload.exceptionDetails);

function rewriteError(error) {
if (error.message.includes('Failed to find execution context with id'))
throw new Error('Execution context was destroyed, most likely because of a navigation.');
throw error;
}
}

frame() {
Expand Down
60 changes: 53 additions & 7 deletions experimental/puppeteer-firefox/lib/FrameManager.js
Expand Up @@ -22,15 +22,41 @@ class FrameManager extends EventEmitter {
this._timeoutSettings = timeoutSettings;
this._mainFrame = null;
this._frames = new Map();
/** @type {!Map<string, !ExecutionContext>} */
this._contextIdToContext = new Map();
this._eventListeners = [
helper.addEventListener(this._session, 'Page.eventFired', this._onEventFired.bind(this)),
helper.addEventListener(this._session, 'Page.frameAttached', this._onFrameAttached.bind(this)),
helper.addEventListener(this._session, 'Page.frameDetached', this._onFrameDetached.bind(this)),
helper.addEventListener(this._session, 'Page.navigationCommitted', this._onNavigationCommitted.bind(this)),
helper.addEventListener(this._session, 'Page.sameDocumentNavigation', this._onSameDocumentNavigation.bind(this)),
helper.addEventListener(this._session, 'Runtime.executionContextCreated', this._onExecutionContextCreated.bind(this)),
helper.addEventListener(this._session, 'Runtime.executionContextDestroyed', this._onExecutionContextDestroyed.bind(this)),
];
}

executionContextById(executionContextId) {
return this._contextIdToContext.get(executionContextId) || null;
}

_onExecutionContextCreated({executionContextId, auxData}) {
const frameId = auxData ? auxData.frameId : null;
const frame = this._frames.get(frameId) || null;
const context = new ExecutionContext(this._session, frame, executionContextId);
if (frame)
frame._setContext(context);
this._contextIdToContext.set(executionContextId, context);
}

_onExecutionContextDestroyed({executionContextId}) {
const context = this._contextIdToContext.get(executionContextId);
if (!context)
return;
this._contextIdToContext.delete(executionContextId);
if (context._frame)
context._frame._setContext(null);
}

frame(frameId) {
return this._frames.get(frameId);
}
Expand Down Expand Up @@ -122,19 +148,37 @@ class Frame {
this._name = '';
/** @type {!Set<!Frame>} */
this._children = new Set();
this._isDetached = false;
this._detached = false;


this._firedEvents = new Set();

/** @type {!Set<!WaitTask>} */
this._waitTasks = new Set();
this._documentPromise = null;
this._contextPromise;
this._contextResolveCallback = null;
this._setContext(null);
}

this._executionContext = new ExecutionContext(this._session, this, this._frameId);
_setContext(context) {
if (context) {
this._contextResolveCallback.call(null, context);
this._contextResolveCallback = null;
for (const waitTask of this._waitTasks)
waitTask.rerun();
} else {
this._documentPromise = null;
this._contextPromise = new Promise(fulfill => {
this._contextResolveCallback = fulfill;
});
}
}

async executionContext() {
return this._executionContext;
if (this._detached)
throw new Error(`Execution Context is not available in detached frame "${this.url()}" (are you trying to evaluate?)`);
return this._contextPromise;
}

/**
Expand Down Expand Up @@ -267,7 +311,7 @@ class Frame {
_detach() {
this._parentFrame._children.delete(this);
this._parentFrame = null;
this._isDetached = true;
this._detached = true;
for (const waitTask of this._waitTasks)
waitTask.terminate(new Error('waitForFunction failed: frame got detached.'));
}
Expand Down Expand Up @@ -438,7 +482,8 @@ class Frame {
}

async evaluate(pageFunction, ...args) {
return this._executionContext.evaluate(pageFunction, ...args);
const context = await this.executionContext();
return context.evaluate(pageFunction, ...args);
}

_document() {
Expand Down Expand Up @@ -497,7 +542,8 @@ class Frame {
}

async evaluateHandle(pageFunction, ...args) {
return this._executionContext.evaluateHandle(pageFunction, ...args);
const context = await this.executionContext();
return context.evaluateHandle(pageFunction, ...args);
}

/**
Expand Down Expand Up @@ -636,7 +682,7 @@ class Frame {
}

isDetached() {
return this._isDetached;
return this._detached;
}

childFrames() {
Expand Down
12 changes: 8 additions & 4 deletions experimental/puppeteer-firefox/lib/JSHandle.js
Expand Up @@ -59,7 +59,7 @@ class JSHandle {
* @return {!Promise<Map<string, !JSHandle>>}
*/
async getProperties() {
const response = await this._session.send('Page.getObjectProperties', {
const response = await this._session.send('Runtime.getObjectProperties', {
executionContextId: this._executionContextId,
objectId: this._objectId,
});
Expand All @@ -85,10 +85,10 @@ class JSHandle {
async jsonValue() {
if (!this._objectId)
return this._deserializeValue(this._protocolValue);
const simpleValue = await this._session.send('Page.evaluate', {
const simpleValue = await this._session.send('Runtime.callFunction', {
executionContextId: this._executionContextId,
returnByValue: true,
functionText: (e => e).toString(),
functionDeclaration: (e => e).toString(),
args: [this._protocolValue],
});
return this._deserializeValue(simpleValue.result);
Expand All @@ -105,9 +105,13 @@ class JSHandle {
if (!this._objectId)
return;
this._disposed = true;
await this._session.send('Page.disposeObject', {
await this._session.send('Runtime.disposeObject', {
executionContextId: this._executionContextId,
objectId: this._objectId,
}).catch(error => {
// Exceptions might happen in case of a page been navigated or closed.
// Swallow these since they are harmless and we don't leak anything in this case.
debugError(error);
});
}
}
Expand Down
9 changes: 5 additions & 4 deletions experimental/puppeteer-firefox/lib/Page.js
Expand Up @@ -27,6 +27,7 @@ class Page extends EventEmitter {
await Promise.all([
session.send('Page.enable'),
session.send('Network.enable'),
session.send('Runtime.enable'),
]);

if (defaultViewport)
Expand Down Expand Up @@ -139,7 +140,7 @@ class Page extends EventEmitter {
else
expression = helper.evaluationString(deliverErrorValue, name, seq, error);
}
this._session.send('Page.evaluate', { script: expression, executionContextId: event.frameId }).catch(debugError);
this._session.send('Runtime.evaluate', { expression, executionContextId: event.executionContextId }).catch(debugError);

/**
* @param {string} name
Expand Down Expand Up @@ -651,9 +652,9 @@ class Page extends EventEmitter {
_onClosed() {
}

_onConsole({type, args, frameId, location}) {
const frame = this._frameManager.frame(frameId);
this.emit(Events.Page.Console, new ConsoleMessage(type, args.map(arg => createHandle(frame._executionContext, arg)), location));
_onConsole({type, args, executionContextId, location}) {
const context = this._frameManager.executionContextById(executionContextId);
this.emit(Events.Page.Console, new ConsoleMessage(type, args.map(arg => createHandle(context, arg)), location));
}

/**
Expand Down
2 changes: 1 addition & 1 deletion experimental/puppeteer-firefox/package.json
Expand Up @@ -9,7 +9,7 @@
"node": ">=8.9.4"
},
"puppeteer": {
"firefox_revision": "3ba79216e3c5ae4e85006047cdd93eac4197427d"
"firefox_revision": "764023af0aa07d232984dec6bc81d9e904f25ddb"
},
"scripts": {
"install": "node install.js",
Expand Down
2 changes: 1 addition & 1 deletion test/evaluation.spec.js
Expand Up @@ -209,7 +209,7 @@ module.exports.addTests = function({testRunner, expect}) {
const result = await page.evaluate(() => document.execCommand('copy'));
expect(result).toBe(true);
});
it_fails_ffox('should throw a nice error after a navigation', async({page, server}) => {
it('should throw a nice error after a navigation', async({page, server}) => {
const executionContext = await page.mainFrame().executionContext();

await Promise.all([
Expand Down