fix(frames): make sure evaluation does not hang in detached iframes (#3770)
Fix #3261
This commit is contained in:
parent
02ae552ac4
commit
9083c111ca
@ -424,6 +424,8 @@ class Frame {
|
|||||||
* @return {!Promise<!ExecutionContext>}
|
* @return {!Promise<!ExecutionContext>}
|
||||||
*/
|
*/
|
||||||
executionContext() {
|
executionContext() {
|
||||||
|
if (this._detached)
|
||||||
|
throw new Error(`Execution Context is not available in detached frame "${this.url()}" (are you trying to evaluate?)`);
|
||||||
return this._contextPromise;
|
return this._contextPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -433,7 +435,7 @@ class Frame {
|
|||||||
* @return {!Promise<!Puppeteer.JSHandle>}
|
* @return {!Promise<!Puppeteer.JSHandle>}
|
||||||
*/
|
*/
|
||||||
async evaluateHandle(pageFunction, ...args) {
|
async evaluateHandle(pageFunction, ...args) {
|
||||||
const context = await this._contextPromise;
|
const context = await this.executionContext();
|
||||||
return context.evaluateHandle(pageFunction, ...args);
|
return context.evaluateHandle(pageFunction, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -443,7 +445,7 @@ class Frame {
|
|||||||
* @return {!Promise<*>}
|
* @return {!Promise<*>}
|
||||||
*/
|
*/
|
||||||
async evaluate(pageFunction, ...args) {
|
async evaluate(pageFunction, ...args) {
|
||||||
const context = await this._contextPromise;
|
const context = await this.executionContext();
|
||||||
return context.evaluate(pageFunction, ...args);
|
return context.evaluate(pageFunction, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -463,7 +465,7 @@ class Frame {
|
|||||||
async _document() {
|
async _document() {
|
||||||
if (this._documentPromise)
|
if (this._documentPromise)
|
||||||
return this._documentPromise;
|
return this._documentPromise;
|
||||||
this._documentPromise = this._contextPromise.then(async context => {
|
this._documentPromise = this.executionContext().then(async context => {
|
||||||
const document = await context.evaluateHandle('document');
|
const document = await context.evaluateHandle('document');
|
||||||
return document.asElement();
|
return document.asElement();
|
||||||
});
|
});
|
||||||
@ -601,7 +603,7 @@ class Frame {
|
|||||||
} = options;
|
} = options;
|
||||||
if (url !== null) {
|
if (url !== null) {
|
||||||
try {
|
try {
|
||||||
const context = await this._contextPromise;
|
const context = await this.executionContext();
|
||||||
return (await context.evaluateHandle(addScriptUrl, url, type)).asElement();
|
return (await context.evaluateHandle(addScriptUrl, url, type)).asElement();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(`Loading script from ${url} failed`);
|
throw new Error(`Loading script from ${url} failed`);
|
||||||
@ -611,12 +613,12 @@ class Frame {
|
|||||||
if (path !== null) {
|
if (path !== null) {
|
||||||
let contents = await readFileAsync(path, 'utf8');
|
let contents = await readFileAsync(path, 'utf8');
|
||||||
contents += '//# sourceURL=' + path.replace(/\n/g, '');
|
contents += '//# sourceURL=' + path.replace(/\n/g, '');
|
||||||
const context = await this._contextPromise;
|
const context = await this.executionContext();
|
||||||
return (await context.evaluateHandle(addScriptContent, contents, type)).asElement();
|
return (await context.evaluateHandle(addScriptContent, contents, type)).asElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (content !== null) {
|
if (content !== null) {
|
||||||
const context = await this._contextPromise;
|
const context = await this.executionContext();
|
||||||
return (await context.evaluateHandle(addScriptContent, content, type)).asElement();
|
return (await context.evaluateHandle(addScriptContent, content, type)).asElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -671,7 +673,7 @@ class Frame {
|
|||||||
} = options;
|
} = options;
|
||||||
if (url !== null) {
|
if (url !== null) {
|
||||||
try {
|
try {
|
||||||
const context = await this._contextPromise;
|
const context = await this.executionContext();
|
||||||
return (await context.evaluateHandle(addStyleUrl, url)).asElement();
|
return (await context.evaluateHandle(addStyleUrl, url)).asElement();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(`Loading style from ${url} failed`);
|
throw new Error(`Loading style from ${url} failed`);
|
||||||
@ -681,12 +683,12 @@ class Frame {
|
|||||||
if (path !== null) {
|
if (path !== null) {
|
||||||
let contents = await readFileAsync(path, 'utf8');
|
let contents = await readFileAsync(path, 'utf8');
|
||||||
contents += '/*# sourceURL=' + path.replace(/\n/g, '') + '*/';
|
contents += '/*# sourceURL=' + path.replace(/\n/g, '') + '*/';
|
||||||
const context = await this._contextPromise;
|
const context = await this.executionContext();
|
||||||
return (await context.evaluateHandle(addStyleContent, contents)).asElement();
|
return (await context.evaluateHandle(addStyleContent, contents)).asElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (content !== null) {
|
if (content !== null) {
|
||||||
const context = await this._contextPromise;
|
const context = await this.executionContext();
|
||||||
return (await context.evaluateHandle(addStyleContent, content)).asElement();
|
return (await context.evaluateHandle(addStyleContent, content)).asElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +57,16 @@ module.exports.addTests = function({testRunner, expect}) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Frame.evaluate', function() {
|
||||||
|
it('should throw for detached frames', async({page, server}) => {
|
||||||
|
const frame1 = await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||||
|
await utils.detachFrame(page, 'frame1');
|
||||||
|
let error = null;
|
||||||
|
await frame1.evaluate(() => 7 * 8).catch(e => error = e);
|
||||||
|
expect(error.message).toContain('Execution Context is not available in detached frame');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('Frame Management', function() {
|
describe('Frame Management', function() {
|
||||||
it('should handle nested frames', async({page, server}) => {
|
it('should handle nested frames', async({page, server}) => {
|
||||||
await page.goto(server.PREFIX + '/frames/nested-frames.html');
|
await page.goto(server.PREFIX + '/frames/nested-frames.html');
|
||||||
@ -146,5 +156,19 @@ module.exports.addTests = function({testRunner, expect}) {
|
|||||||
expect(page.frames()[1].parentFrame()).toBe(page.mainFrame());
|
expect(page.frames()[1].parentFrame()).toBe(page.mainFrame());
|
||||||
expect(page.frames()[2].parentFrame()).toBe(page.mainFrame());
|
expect(page.frames()[2].parentFrame()).toBe(page.mainFrame());
|
||||||
});
|
});
|
||||||
|
it('should report different frame instance when frame re-attaches', async({page, server}) => {
|
||||||
|
const frame1 = await utils.attachFrame(page, 'frame1', server.EMPTY_PAGE);
|
||||||
|
await page.evaluate(() => {
|
||||||
|
window.frame = document.querySelector('#frame1');
|
||||||
|
window.frame.remove();
|
||||||
|
});
|
||||||
|
expect(frame1.isDetached()).toBe(true);
|
||||||
|
const [frame2] = await Promise.all([
|
||||||
|
utils.waitEvent(page, 'frameattached'),
|
||||||
|
page.evaluate(() => document.body.appendChild(window.frame)),
|
||||||
|
]);
|
||||||
|
expect(frame2.isDetached()).toBe(false);
|
||||||
|
expect(frame1).not.toBe(frame2);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user