Rename Page.setInPageCallback into Page.addBinding (#236)

This seems to be a much better name which is actually used for a similar
purposes in chromium/v8.
This commit is contained in:
Andrey Lushnikov 2017-08-10 21:44:49 -07:00 committed by GitHub
parent 25465525c1
commit 6347a049ba
3 changed files with 86 additions and 44 deletions

View File

@ -25,6 +25,7 @@
+ [event: 'requestfailed'](#event-requestfailed)
+ [event: 'requestfinished'](#event-requestfinished)
+ [event: 'response'](#event-response)
+ [page.addBinding(name, puppeteerFunction)](#pageaddbindingname-puppeteerfunction)
+ [page.addScriptTag(url)](#pageaddscripttagurl)
+ [page.click(selector[, options])](#pageclickselector-options)
+ [page.close()](#pageclose)
@ -48,7 +49,6 @@
+ [page.screenshot([options])](#pagescreenshotoptions)
+ [page.setContent(html)](#pagesetcontenthtml)
+ [page.setExtraHTTPHeaders(headers)](#pagesetextrahttpheadersheaders)
+ [page.setInPageCallback(name, callback)](#pagesetinpagecallbackname-callback)
+ [page.setRequestInterceptor(interceptor)](#pagesetrequestinterceptorinterceptor)
+ [page.setUserAgent(userAgent)](#pagesetuseragentuseragent)
+ [page.setViewport(viewport)](#pagesetviewportviewport)
@ -313,6 +313,69 @@ Emitted when a request is successfully finished.
Emitted when a [response] is received.
#### page.addBinding(name, puppeteerFunction)
- `name` <[string]> Name of the binding on window object
- `puppeteerFunction` <[function]> Callback function which will be called in puppeteer's context.
- returns: <[Promise]> Promise which resolves with the result of `puppeteerFunction`.
The method adds a function called `name` on `window` object.
When called, the function executes `puppeteerFunction` function in puppeteer context and returns
a promise that resolves with the puppeteer's result.
If the `puppeteerFunction` returns a promise, it would be awaited.
> **NOTE** All the bindings installed via the `page.addBinding` survive navigations.
An example of adding `window.md5` binding to the page:
```js
const {Browser} = require('puppeteer');
const browser = new Browser();
const crypto = require('crypto');
browser.newPage().then(async page => {
page.on('console', console.log);
await page.setInPageCallback('md5', text => crypto.createHash('md5').update(text).digest('hex'));
await page.evaluate(async () => {
// use window.md5 to compute hashes
let myString = 'PUPPETEER';
let myHash = await window.md5(myString);
console.log(`md5 of ${myString} is ${myHash}`);
});
browser.close();
});
```
An example of adding `window.readfile` binding to the page:
```js
const {Browser} = require('puppeteer');
const browser = new Browser();
const fs = require('fs');
browser.newPage().then(async page => {
page.on('console', console.log);
await page.setInPageCallback('readfile', async filePath => {
return new Promise((resolve, reject) => {
fs.readFile(filePath, 'utf8', (err, text) => {
if (err)
reject(err);
else
resolve(text);
});
});
});
await page.evaluate(async () => {
// use window.readfile to read contents of a file
let content = await window.readfile('/etc/hosts');
console.log(content);
});
browser.close();
});
```
#### page.addScriptTag(url)
- `url` <[string]> Url of a script to be added
- returns: <[Promise]> Promise which resolves as the script gets added and loads.
@ -554,27 +617,6 @@ The extra HTTP headers will be sent with every request the page initiates.
> **NOTE** page.setExtraHTTPHeaders does not guarantee the order of headers in the outgoing requests.
#### page.setInPageCallback(name, callback)
- `name` <[string]> Name of the callback to be assigned on window object
- `callback` <[function]> Callback function which will be called in puppeteer's context.
- returns: <[Promise]> Promise which resolves when callback is successfully initialized
The in-page callback allows page to asynchronously reach back to the Puppeteer.
An example of a page showing amount of CPU's:
```js
const os = require('os');
const {Browser} = require('puppeteer');
const browser = new Browser();
browser.newPage().then(async page =>
await page.setInPageCallback('getCPUCount', () => os.cpus().length);
await page.evaluate(async () => {
alert(await window.getCPUCount());
});
browser.close();
});
```
#### page.setRequestInterceptor(interceptor)
- `interceptor` <[function]> Callback function which accepts a single argument of type <[InterceptedRequest]>.
- returns: <[Promise]> Promise which resolves when request interceptor is successfully installed on the page.
@ -966,7 +1008,7 @@ Returns frame's name attribute as specified in the tag.
If the name is empty, returns the id attribute instead.
Note: This value is calculated once when the frame is created, and will not update if the attribute is changed later.
> **NOTE** This value is calculated once when the frame is created, and will not update if the attribute is changed later.
#### frame.parentFrame()
- returns: <[Frame]> Returns parent frame, if any. Detached frames and main frames return `null`.

View File

@ -65,7 +65,7 @@ class Page extends EventEmitter {
this._emulationManager = new EmulationManager(client);
this._tracing = new Tracing(client);
/** @type {!Map<string, function>} */
this._inPageCallbacks = new Map();
this._pageBindings = new Map();
this._ignoreHTTPSErrors = ignoreHTTPSErrors;
this._screenshotTaskQueue = screenshotTaskQueue;
@ -152,20 +152,20 @@ class Page extends EventEmitter {
/**
* @param {string} name
* @param {function(?)} callback
* @param {function(?)} puppeteerFunction
*/
async setInPageCallback(name, callback) {
if (this._inPageCallbacks[name])
throw new Error(`Failed to set in-page callback with name ${name}: window['${name}'] already exists!`);
this._inPageCallbacks[name] = callback;
async addBinding(name, puppeteerFunction) {
if (this._pageBindings[name])
throw new Error(`Failed to add page binding with name ${name}: window['${name}'] already exists!`);
this._pageBindings[name] = puppeteerFunction;
let expression = helper.evaluationString(inPageCallback, name);
let expression = helper.evaluationString(addPageBinding, name);
await this._client.send('Page.addScriptToEvaluateOnNewDocument', { source: expression });
await this._client.send('Runtime.evaluate', { expression, returnByValue: true });
function inPageCallback(callbackName) {
window[callbackName] = async(...args) => {
const me = window[callbackName];
function addPageBinding(bindingName) {
window[bindingName] = async(...args) => {
const me = window[bindingName];
let callbacks = me['callbacks'];
if (!callbacks) {
callbacks = new Map();
@ -175,7 +175,7 @@ class Page extends EventEmitter {
me['lastSeq'] = seq;
const promise = new Promise(fulfill => callbacks.set(seq, fulfill));
// eslint-disable-next-line no-console
console.debug('driver:InPageCallback', JSON.stringify({name: callbackName, seq, args}));
console.debug('driver:page-binding', JSON.stringify({name: bindingName, seq, args}));
return promise;
};
}
@ -206,9 +206,9 @@ class Page extends EventEmitter {
}
async _onConsoleAPI(event) {
if (event.type === 'debug' && event.args.length && event.args[0].value === 'driver:InPageCallback') {
if (event.type === 'debug' && event.args.length && event.args[0].value === 'driver:page-binding') {
let {name, seq, args} = JSON.parse(event.args[1].value);
let result = await this._inPageCallbacks[name](...args);
let result = await this._pageBindings[name](...args);
let expression = helper.evaluationString(deliverResult, name, seq, result);
this._client.send('Runtime.evaluate', { expression });

View File

@ -166,7 +166,7 @@ describe('Page', function() {
}));
it('should work from-inside inPageCallback', SX(async function() {
// Setup inpage callback, which calls Page.evaluate
await page.setInPageCallback('callController', async function(a, b) {
await page.addBinding('callController', async function(a, b) {
return await page.evaluate((a, b) => a * b, a, b);
});
let result = await page.evaluate(async function() {
@ -713,34 +713,34 @@ describe('Page', function() {
}));
});
describe('Page.setInPageCallback', function() {
describe('Page.addBinding', function() {
it('should work', SX(async function() {
await page.setInPageCallback('callController', function(a, b) {
await page.addBinding('compute', function(a, b) {
return a * b;
});
let result = await page.evaluate(async function() {
return await callController(9, 4);
return await compute(9, 4);
});
expect(result).toBe(36);
}));
it('should survive navigation', SX(async function() {
await page.setInPageCallback('callController', function(a, b) {
await page.addBinding('compute', function(a, b) {
return a * b;
});
await page.goto(EMPTY_PAGE);
let result = await page.evaluate(async function() {
return await callController(9, 4);
return await compute(9, 4);
});
expect(result).toBe(36);
}));
it('should await returned promise', SX(async function() {
await page.setInPageCallback('callController', function(a, b) {
await page.addBinding('compute', function(a, b) {
return Promise.resolve(a * b);
});
let result = await page.evaluate(async function() {
return await callController(3, 5);
return await compute(3, 5);
});
expect(result).toBe(15);
}));