mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
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:
parent
2061dd4718
commit
55432f88e9
@ -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};
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -205,6 +205,13 @@ module.exports.addTests = function({testRunner, expect, product}) {
|
||||
await frame.waitForSelector('div');
|
||||
});
|
||||
|
||||
it('should work with removed MutationObserver', async({page, server}) => {
|
||||
await page.evaluate(() => delete window.MutationObserver);
|
||||
const waitForSelector = page.waitForSelector('.zombo');
|
||||
await page.setContent(`<div class='zombo'>anything</div>`);
|
||||
expect(await page.evaluate(x => x.textContent, await waitForSelector)).toBe('anything');
|
||||
});
|
||||
|
||||
it('should resolve promise when node is added', async({page, server}) => {
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
const frame = page.mainFrame();
|
||||
@ -247,15 +254,6 @@ module.exports.addTests = function({testRunner, expect, product}) {
|
||||
expect(eHandle.executionContext().frame()).toBe(frame2);
|
||||
});
|
||||
|
||||
it('should throw if evaluation failed', async({page, server}) => {
|
||||
await page.evaluateOnNewDocument(function() {
|
||||
document.querySelector = null;
|
||||
});
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
let error = null;
|
||||
await page.waitForSelector('*').catch(e => error = e);
|
||||
expect(error.message).toContain('document.querySelector is not a function');
|
||||
});
|
||||
it('should throw when frame is detached', async({page, server}) => {
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.frames()[1];
|
||||
@ -394,15 +392,6 @@ module.exports.addTests = function({testRunner, expect, product}) {
|
||||
const eHandle = await waitForXPathPromise;
|
||||
expect(eHandle.executionContext().frame()).toBe(frame2);
|
||||
});
|
||||
it('should throw if evaluation failed', async({page, server}) => {
|
||||
await page.evaluateOnNewDocument(function() {
|
||||
document.evaluate = null;
|
||||
});
|
||||
await page.goto(server.EMPTY_PAGE);
|
||||
let error = null;
|
||||
await page.waitForXPath('*').catch(e => error = e);
|
||||
expect(error.message).toContain('document.evaluate is not a function');
|
||||
});
|
||||
it('should throw when frame is detached', async({page, server}) => {
|
||||
await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||
const frame = page.frames()[1];
|
||||
|
Loading…
Reference in New Issue
Block a user