mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
Make in-page callback survive navigations
This patch makes in-page callbacks survive navigations via running in-page harness code on page load.
This commit is contained in:
parent
ab6a96a991
commit
e8af69e5bb
53
lib/Page.js
53
lib/Page.js
@ -50,12 +50,13 @@ class Page extends EventEmitter {
|
|||||||
this._client = client;
|
this._client = client;
|
||||||
this._screenDPI = screenDPI;
|
this._screenDPI = screenDPI;
|
||||||
this._extraHeaders = {};
|
this._extraHeaders = {};
|
||||||
/** @type {!Map<string, function()>} */
|
/** @type {!Map<string, !InPageCallback>} */
|
||||||
|
this._sourceURLToPageCallback = new Map();
|
||||||
|
/** @type {!Map<string, !InPageCallback>} */
|
||||||
this._scriptIdToPageCallback = new Map();
|
this._scriptIdToPageCallback = new Map();
|
||||||
/** @type {!Map<string, string>} */
|
|
||||||
this._scriptIdToCallbackName = new Map();
|
|
||||||
|
|
||||||
client.on('Debugger.paused', event => this._onDebuggerPaused(event));
|
client.on('Debugger.paused', event => this._onDebuggerPaused(event));
|
||||||
|
client.on('Debugger.scriptParsed', event => this._onScriptParsed(event));
|
||||||
client.on('Network.responseReceived', event => this.emit(Page.Events.ResponseReceived, event.response));
|
client.on('Network.responseReceived', event => this.emit(Page.Events.ResponseReceived, event.response));
|
||||||
client.on('Network.loadingFailed', event => this.emit(Page.Events.ResourceLoadingFailed, event));
|
client.on('Network.loadingFailed', event => this.emit(Page.Events.ResourceLoadingFailed, event));
|
||||||
client.on('Runtime.consoleAPICalled', event => this._onConsoleAPI(event));
|
client.on('Runtime.consoleAPICalled', event => this._onConsoleAPI(event));
|
||||||
@ -103,15 +104,13 @@ class Page extends EventEmitter {
|
|||||||
throw new Error(`Failed to set in-page callback with name ${name}: window['${name}'] already exists!`);
|
throw new Error(`Failed to set in-page callback with name ${name}: window['${name}'] already exists!`);
|
||||||
|
|
||||||
var sourceURL = '__in_page_callback__' + name;
|
var sourceURL = '__in_page_callback__' + name;
|
||||||
// Ensure debugger is enabled.
|
this._sourceURLToPageCallback.set(sourceURL, new InPageCallback(name, callback));
|
||||||
await this._client.send('Debugger.enable', {});
|
var text = helpers.evaluationString(inPageCallback, [name], false /* awaitPromise */, sourceURL);
|
||||||
var scriptPromise = helpers.waitForScriptWithURL(this._client, sourceURL);
|
await Promise.all([
|
||||||
helpers.evaluate(this._client, inPageCallback, [name], false /* awaitPromise */, sourceURL);
|
this._client.send('Debugger.enable', {}),
|
||||||
var script = await scriptPromise;
|
this._client.send('Page.addScriptToEvaluateOnLoad', { scriptSource: text }),
|
||||||
if (!script)
|
helpers.evaluateText(this._client, text, false /* awaitPromise */)
|
||||||
throw new Error(`Failed to set in-page callback with name "${name}"`);
|
]);
|
||||||
this._scriptIdToPageCallback.set(script.scriptId, callback);
|
|
||||||
this._scriptIdToCallbackName.set(script.scriptId, name);
|
|
||||||
|
|
||||||
function inPageCallback(callbackName) {
|
function inPageCallback(callbackName) {
|
||||||
window[callbackName] = (...args) => {
|
window[callbackName] = (...args) => {
|
||||||
@ -124,11 +123,11 @@ class Page extends EventEmitter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} scriptId
|
* @param {!InPageCallback} inPageCallback
|
||||||
*/
|
*/
|
||||||
async _handleInPageCallback(scriptId) {
|
async _handleInPageCallback(inPageCallback) {
|
||||||
var name = /** @type {string} */ (this._scriptIdToCallbackName.get(scriptId));
|
var name = inPageCallback.name;
|
||||||
var callback = /** @type {function()} */ (this._scriptIdToPageCallback.get(scriptId));
|
var callback = inPageCallback.callback;
|
||||||
var args = await this.evaluate(callbackName => window[callbackName].__args, name);
|
var args = await this.evaluate(callbackName => window[callbackName].__args, name);
|
||||||
var result = callback.apply(null, args);
|
var result = callback.apply(null, args);
|
||||||
await this.evaluate(assignResult, name, result);
|
await this.evaluate(assignResult, name, result);
|
||||||
@ -145,13 +144,20 @@ class Page extends EventEmitter {
|
|||||||
|
|
||||||
_onDebuggerPaused(event) {
|
_onDebuggerPaused(event) {
|
||||||
var location = event.callFrames[0] ? event.callFrames[0].location : null;
|
var location = event.callFrames[0] ? event.callFrames[0].location : null;
|
||||||
if (location && this._scriptIdToPageCallback.has(location.scriptId)) {
|
var inPageCallback = location ? this._scriptIdToPageCallback.get(location.scriptId) : null;
|
||||||
this._handleInPageCallback(location.scriptId);
|
if (inPageCallback) {
|
||||||
|
this._handleInPageCallback(inPageCallback);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._client.send('Debugger.resume');
|
this._client.send('Debugger.resume');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_onScriptParsed(event) {
|
||||||
|
var inPageCallback = this._sourceURLToPageCallback.get(event.url);
|
||||||
|
if (inPageCallback)
|
||||||
|
this._scriptIdToPageCallback.set(event.scriptId, inPageCallback);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {!Object} headers
|
* @param {!Object} headers
|
||||||
* @return {!Promise}
|
* @return {!Promise}
|
||||||
@ -392,6 +398,17 @@ class Page extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class InPageCallback {
|
||||||
|
/**
|
||||||
|
* @param {string} name
|
||||||
|
* @param {function(?):?} callback
|
||||||
|
*/
|
||||||
|
constructor(name, callback) {
|
||||||
|
this.name = name;
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** @enum {string} */
|
/** @enum {string} */
|
||||||
Page.ScreenshotTypes = {
|
Page.ScreenshotTypes = {
|
||||||
PNG: "png",
|
PNG: "png",
|
||||||
|
29
test/test.js
29
test/test.js
@ -1,6 +1,9 @@
|
|||||||
|
var path = require('path');
|
||||||
var Browser = require('../lib/Browser');
|
var Browser = require('../lib/Browser');
|
||||||
|
|
||||||
describe('Page', function() {
|
var EMPTY_PAGE = 'file://' + path.join(__dirname, 'assets', 'empty.html');
|
||||||
|
|
||||||
|
describe('Puppeteer', function() {
|
||||||
var browser;
|
var browser;
|
||||||
var page;
|
var page;
|
||||||
|
|
||||||
@ -29,6 +32,30 @@ describe('Page', function() {
|
|||||||
var result = await page.evaluateAsync(() => Promise.resolve(8 * 7));
|
var result = await page.evaluateAsync(() => Promise.resolve(8 * 7));
|
||||||
expect(result).toBe(56);
|
expect(result).toBe(56);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
describe('Page.setInPageCallback', function() {
|
||||||
|
it('should work', SX(async function() {
|
||||||
|
await page.setInPageCallback('callController', function(a, b) {
|
||||||
|
return a * b;
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = await page.evaluate(function() {
|
||||||
|
return callController(9, 4);
|
||||||
|
});
|
||||||
|
expect(result).toBe(36);
|
||||||
|
}));
|
||||||
|
it('should survive navigation', SX(async function() {
|
||||||
|
await page.setInPageCallback('callController', function(a, b) {
|
||||||
|
return a * b;
|
||||||
|
});
|
||||||
|
|
||||||
|
await page.navigate(EMPTY_PAGE);
|
||||||
|
var result = await page.evaluate(function() {
|
||||||
|
return callController(9, 4);
|
||||||
|
});
|
||||||
|
expect(result).toBe(36);
|
||||||
|
}));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Since Jasmine doesn't like async functions, they should be wrapped
|
// Since Jasmine doesn't like async functions, they should be wrapped
|
||||||
|
Loading…
Reference in New Issue
Block a user