fix(page): execute frame.waitFor{Selector,XPath} in secondary world (#3856)

This patch starts executing frame.waitForSelector and frame.waitForXPath
in secondary world. As a result, websites that mutate page global
context (e.g. removing global MutationObserver) don't break Puppeteer's
behavior.

Fixes #609
This commit is contained in:
Andrey Lushnikov
2019-01-28 12:24:27 -08:00
committed by GitHub
parent 2061dd4718
commit 55432f88e9
3 changed files with 39 additions and 22 deletions

View File

@@ -173,6 +173,22 @@ class ExecutionContext {
});
return createJSHandle(this, response.objects);
}
/**
* @param {Puppeteer.ElementHandle} elementHandle
* @return {Promise<Puppeteer.ElementHandle>}
*/
async _adoptElementHandle(elementHandle) {
assert(elementHandle.executionContext() !== this, 'Cannot adopt handle that already belongs to this execution context');
assert(this._world, 'Cannot adopt handle without DOMWorld');
const nodeInfo = await this._client.send('DOM.describeNode', {
objectId: elementHandle._remoteObject.objectId,
});
const {object} = await this._client.send('DOM.resolveNode', {
backendNodeId: nodeInfo.node.backendNodeId,
});
return /** @type {Puppeteer.ElementHandle}*/(createJSHandle(this, object));
}
}
module.exports = {ExecutionContext, EVALUATION_SCRIPT_URL};

View File

@@ -608,8 +608,14 @@ class Frame {
* @param {!{visible?: boolean, hidden?: boolean, timeout?: number}=} options
* @return {!Promise<?Puppeteer.ElementHandle>}
*/
waitForSelector(selector, options) {
return this._mainWorld.waitForSelector(selector, options);
async waitForSelector(selector, options) {
const handle = await this._secondaryWorld.waitForSelector(selector, options);
if (!handle)
return null;
const mainExecutionContext = await this._mainWorld.executionContext();
const result = await mainExecutionContext._adoptElementHandle(handle);
await handle.dispose();
return result;
}
/**
@@ -617,8 +623,14 @@ class Frame {
* @param {!{visible?: boolean, hidden?: boolean, timeout?: number}=} options
* @return {!Promise<?Puppeteer.ElementHandle>}
*/
waitForXPath(xpath, options) {
return this._mainWorld.waitForXPath(xpath, options);
async waitForXPath(xpath, options) {
const handle = await this._secondaryWorld.waitForXPath(xpath, options);
if (!handle)
return null;
const mainExecutionContext = await this._mainWorld.executionContext();
const result = await mainExecutionContext._adoptElementHandle(handle);
await handle.dispose();
return result;
}
/**