diff --git a/docs/api.md b/docs/api.md
index 22a94365..71a463b9 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -79,13 +79,17 @@
* [class: Frame](#class-frame)
+ [frame.$(selector, pageFunction, ...args)](#frameselector-pagefunction-args)
+ [frame.$$(selector, pageFunction, ...args)](#frameselector-pagefunction-args)
+ + [frame.addScriptTag(url)](#frameaddscripttagurl)
+ [frame.childFrames()](#framechildframes)
+ [frame.click(selector[, options])](#frameclickselector-options)
+ [frame.evaluate(pageFunction, ...args)](#frameevaluatepagefunction-args)
+ + [frame.focus(selector)](#framefocusselector)
+ [frame.hover(selector)](#framehoverselector)
+ + [frame.injectFile(filePath)](#frameinjectfilefilepath)
+ [frame.isDetached()](#frameisdetached)
+ [frame.name()](#framename)
+ [frame.parentFrame()](#frameparentframe)
+ + [frame.title()](#frametitle)
+ [frame.url()](#frameurl)
+ [frame.waitFor(selectorOrTimeout[, options])](#framewaitforselectorortimeout-options)
+ [frame.waitForSelector(selector[, options])](#framewaitforselectorselector-options)
@@ -347,7 +351,7 @@ Shortcut for [page.mainFrame().$$(selector, pageFunction, ...args)](#pageselecto
- `url` <[string]> Url of a script to be added
- returns: <[Promise]> Promise which resolves as the script gets added and loads.
-Adds a `` tag to the page with the desired url. Alternatively, javascript could be injected to the page via `page.injectFile` method.
+Adds a `` tag to the page with the desired url. Alternatively, javascript could be injected to the page via [`page.injectFile`](#pageinjectfilefilepath) method.
#### page.click(selector[, options])
- `selector` <[string]> A query selector to search for element to click. If there are multiple elements satisfying the selector, the first will be clicked.
@@ -791,6 +795,12 @@ browser.newPage().then(async page => {
- `...args` <...[string]> Arguments to pass to `pageFunction`
- returns: <[Promise]<[Array]<[Object]>>> Promise which resolves to array of function return values.
+#### frame.addScriptTag(url)
+- `url` <[string]> Url of a script to be added
+- returns: <[Promise]> Promise which resolves as the script gets added and loads.
+
+Adds a `` tag to the frame with the desired url. Alternatively, javascript could be injected to the frame via [`frame.injectFile`](#frameinjectfilefilepath) method.
+
#### frame.childFrames()
- returns: <[Array]<[Frame]>>
@@ -820,10 +830,18 @@ browser.newPage().then(async page =>
});
```
+#### frame.focus(selector)
+- `selector` <[string]> A query selector of element to focus. If there are multiple elements satisfying the selector, the first will be focused.
+- returns: <[Promise]> Promise which resolves when the element matching `selector` is successfully focused. Promise gets rejected if there's no element matching `selector`.
+
#### frame.hover(selector)
- `selector` <[string]> A query selector to search for element to hover. If there are multiple elements satisfying the selector, the first will be hovered.
- returns: <[Promise]> Promise which resolves when the element matching `selector` is successfully hovered. Promise gets rejected if there's no element matching `selector`.
+#### frame.injectFile(filePath)
+- `filePath` <[string]> Path to the javascript file to be injected into frame.
+- returns: <[Promise]> Promise which resolves when file gets successfully evaluated in frame.
+
#### frame.isDetached()
- returns: <[boolean]>
@@ -837,6 +855,9 @@ Returns frame's name as specified in the tag.
#### frame.parentFrame()
- returns: <[Frame]> Returns parent frame, if any. Detached frames and main frames return `null`.
+#### frame.title()
+- returns: <[Promise]<[string]>> Returns page's title.
+
#### frame.url()
- returns: <[string]>
diff --git a/lib/FrameManager.js b/lib/FrameManager.js
index 74180e32..5564b8e3 100644
--- a/lib/FrameManager.js
+++ b/lib/FrameManager.js
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+let fs = require('fs');
let EventEmitter = require('events');
let helper = require('./helper');
@@ -157,21 +158,43 @@ class FrameManager extends EventEmitter {
/**
* @param {!Frame} frame
- * @param {string} expression
- * @return {!Promise<(!Object|undefined)>}
+ * @return {number}
*/
- async _evaluateOnFrame(frame, expression) {
+ _contextIdForFrame(frame) {
let contextId = undefined;
if (frame !== this._mainFrame) {
contextId = this._frameIdToExecutionContextId.get(frame._id);
console.assert(contextId, 'Frame does not have default context to evaluate in!');
}
+ return contextId;
+ }
+
+ /**
+ * @param {!Frame} frame
+ * @param {string} expression
+ * @return {!Promise<(!Object|undefined)>}
+ */
+ async _evaluateOnFrame(frame, expression) {
+ let contextId = this._contextIdForFrame(frame);
expression = `Promise.resolve(${expression})`;
let { exceptionDetails, result: remoteObject } = await this._client.send('Runtime.evaluate', { expression, contextId, returnByValue: false, awaitPromise: true });
if (exceptionDetails)
throw new Error('Evaluation failed: ' + helper.getExceptionMessage(exceptionDetails));
return await helper.serializeRemoteObject(this._client, remoteObject);
}
+
+ /**
+ * @param {!Frame} frame
+ * @param {string} expression
+ * @return {!Promise<(!Object|undefined)>}
+ */
+ async _rawEvaluateOnFrame(frame, expression) {
+ let contextId = this._contextIdForFrame(frame);
+ let { exceptionDetails, result: remoteObject } = await this._client.send('Runtime.evaluate', { expression, contextId, returnByValue: false });
+ if (exceptionDetails)
+ throw new Error('Evaluation failed: ' + helper.getExceptionMessage(exceptionDetails));
+ return await helper.serializeRemoteObject(this._client, remoteObject);
+ }
}
/** @enum {string} */
@@ -251,6 +274,40 @@ class Frame {
return this._detached;
}
+ /**
+ * @param {string} filePath
+ * @return {!Promise}
+ */
+ async injectFile(filePath) {
+ let contents = await new Promise((resolve, reject) => {
+ fs.readFile(filePath, 'utf8', (err, data) => {
+ if (err) return reject(err);
+ resolve(data);
+ });
+ });
+ contents += `//# sourceURL=` + filePath.replace(/\n/g,'');
+ return this._frameManager._rawEvaluateOnFrame(this, contents);
+ }
+
+ /**
+ * @param {string} url
+ * @return {!Promise}
+ */
+ async addScriptTag(url) {
+ return this.evaluate(addScriptTag, url);
+
+ /**
+ * @param {string} url
+ */
+ function addScriptTag(url) {
+ let script = document.createElement('script');
+ script.src = url;
+ let promise = new Promise(x => script.onload = x);
+ document.head.appendChild(script);
+ return promise;
+ }
+ }
+
/**
* @param {(string|number)} selectorOrTimeout
* @param {!Object=} options
@@ -313,6 +370,13 @@ class Frame {
return this._frameManager._evaluateOnFrame(this, expression);
}
+ /**
+ * @return {!Promise}
+ */
+ async title() {
+ return this.evaluate(() => document.title);
+ }
+
/**
* @param {string} selector
* @return {!Promise}
@@ -346,6 +410,22 @@ class Frame {
await this.evaluate(() => new Promise(f => requestAnimationFrame(f)));
}
+ /**
+ * @param {string} selector
+ * @return {!Promise}
+ */
+ async focus(selector) {
+ let success = await this.evaluate(selector => {
+ let node = document.querySelector(selector);
+ if (!node)
+ return false;
+ node.focus();
+ return true;
+ }, selector);
+ if (!success)
+ throw new Error('No node found for selector: ' + selector);
+ }
+
/**
* @param {?Object} framePayload
*/
diff --git a/lib/Page.js b/lib/Page.js
index e34e5183..27e36404 100644
--- a/lib/Page.js
+++ b/lib/Page.js
@@ -119,18 +119,7 @@ class Page extends EventEmitter {
* @return {!Promise}
*/
async addScriptTag(url) {
- return this.evaluate(addScriptTag, url);
-
- /**
- * @param {string} url
- */
- function addScriptTag(url) {
- let script = document.createElement('script');
- script.src = url;
- let promise = new Promise(x => script.onload = x);
- document.head.appendChild(script);
- return promise;
- }
+ return this.mainFrame().addScriptTag(url);
}
/**
@@ -138,14 +127,7 @@ class Page extends EventEmitter {
* @return {!Promise}
*/
async injectFile(filePath) {
- let contents = await new Promise((resolve, reject) => {
- fs.readFile(filePath, 'utf8', (err, data) => {
- if (err) return reject(err);
- resolve(data);
- });
- });
- contents += `//# sourceURL=` + filePath.replace(/\n/g,'');
- return this._client.send('Runtime.evaluate', { expression: contents, returnByValue: true });
+ return this.mainFrame().injectFile(filePath);
}
/**
@@ -498,7 +480,7 @@ class Page extends EventEmitter {
* @return {!Promise}
*/
async title() {
- return this.evaluate(() => document.title);
+ return this.mainFrame().title();
}
/**
@@ -552,15 +534,7 @@ class Page extends EventEmitter {
* @return {!Promise}
*/
async focus(selector) {
- let success = await this.evaluate(selector => {
- let node = document.querySelector(selector);
- if (!node)
- return false;
- node.focus();
- return true;
- }, selector);
- if (!success)
- throw new Error('No node found for selector: ' + selector);
+ return this.mainFrame().focus(selector);
}
/**