fix(ownerFrame): correctly handle adopted node usecase (#677)

This commit is contained in:
Dmitry Gozman 2020-01-27 11:43:43 -08:00 committed by Yury Semikhatsky
parent b3cd7a4365
commit 541fa95ce4
11 changed files with 59 additions and 15 deletions

View File

@ -22,6 +22,7 @@ import { helper } from './helper';
export interface BrowserContextDelegate {
pages(): Promise<Page[]>;
existingPages(): Page[];
newPage(): Promise<Page>;
close(): Promise<void>;
@ -69,6 +70,10 @@ export class BrowserContext {
await this.setGeolocation(this._options.geolocation);
}
_existingPages(): Page[] {
return this._delegate.existingPages();
}
async pages(): Promise<Page[]> {
return this._delegate.pages();
}

View File

@ -74,6 +74,15 @@ export class CRBrowser extends platform.EventEmitter implements Browser {
return pages.filter(page => !!page) as Page[];
},
existingPages: (): Page[] => {
const pages: Page[] = [];
for (const target of this._allTargets()) {
if (target.browserContext() === context && target._crPage)
pages.push(target._crPage.page());
}
return pages;
},
newPage: async (): Promise<Page> => {
const { targetId } = await this._client.send('Target.createTarget', { url: 'about:blank', browserContextId: contextId || undefined });
const target = this._targets.get(targetId)!;

View File

@ -424,7 +424,7 @@ export class CRPage implements PageDelegate {
return this._page._frameManager.frame(nodeInfo.node.frameId);
}
async getOwnerFrame(handle: dom.ElementHandle): Promise<frames.Frame | null> {
async getOwnerFrame(handle: dom.ElementHandle): Promise<string | null> {
// document.documentElement has frameId of the owner frame.
const documentElement = await handle.evaluateHandle(node => {
const doc = node as Document;
@ -440,10 +440,10 @@ export class CRPage implements PageDelegate {
const nodeInfo = await this._client.send('DOM.describeNode', {
objectId: remoteObject.objectId
});
const frame = nodeInfo && typeof nodeInfo.node.frameId === 'string' ?
this._page._frameManager.frame(nodeInfo.node.frameId) : null;
const frameId = nodeInfo && typeof nodeInfo.node.frameId === 'string' ?
nodeInfo.node.frameId : null;
await documentElement.dispose();
return frame;
return frameId;
}
isElementHandle(remoteObject: any): boolean {

View File

@ -135,7 +135,16 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
}
async ownerFrame(): Promise<frames.Frame | null> {
return this._page._delegate.getOwnerFrame(this);
const frameId = await this._page._delegate.getOwnerFrame(this);
if (!frameId)
return null;
const pages = this._page.browserContext()._existingPages();
for (const page of pages) {
const frame = page._frameManager.frame(frameId);
if (frame)
return frame;
}
return null;
}
async contentFrame(): Promise<frames.Frame | null> {

View File

@ -164,6 +164,15 @@ export class FFBrowser extends platform.EventEmitter implements Browser {
return pages.filter(page => !!page);
},
existingPages: (): Page[] => {
const pages: Page[] = [];
for (const target of this._allTargets()) {
if (target.browserContext() === context && target._ffPage)
pages.push(target._ffPage._page);
}
return pages;
},
newPage: async (): Promise<Page> => {
const {targetId} = await this._connection.send('Target.newPage', {
browserContextId: browserContextId || undefined

View File

@ -373,14 +373,12 @@ export class FFPage implements PageDelegate {
return this._page._frameManager.frame(contentFrameId);
}
async getOwnerFrame(handle: dom.ElementHandle): Promise<frames.Frame | null> {
async getOwnerFrame(handle: dom.ElementHandle): Promise<string | null> {
const { ownerFrameId } = await this._session.send('Page.describeNode', {
frameId: handle._context.frame._id,
objectId: toRemoteObject(handle).objectId!,
});
if (!ownerFrameId)
return null;
return this._page._frameManager.frame(ownerFrameId);
return ownerFrameId || null;
}
isElementHandle(remoteObject: any): boolean {

View File

@ -62,7 +62,7 @@ export interface PageDelegate {
isElementHandle(remoteObject: any): boolean;
adoptElementHandle<T extends Node>(handle: dom.ElementHandle<T>, to: dom.FrameExecutionContext): Promise<dom.ElementHandle<T>>;
getContentFrame(handle: dom.ElementHandle): Promise<frames.Frame | null>; // Only called for frame owner elements.
getOwnerFrame(handle: dom.ElementHandle): Promise<frames.Frame | null>;
getOwnerFrame(handle: dom.ElementHandle): Promise<string | null>; // Returns frameId.
getContentQuads(handle: dom.ElementHandle): Promise<types.Quad[] | null>;
layoutViewport(): Promise<{ width: number, height: number }>;
setInputFiles(handle: dom.ElementHandle<HTMLInputElement>, files: types.FilePayload[]): Promise<void>;

View File

@ -168,6 +168,18 @@ export class WKBrowser extends platform.EventEmitter implements Browser {
return await Promise.all(pageProxies.map(proxy => proxy.page()));
},
existingPages: (): Page[] => {
const pages: Page[] = [];
for (const pageProxy of this._pageProxies.values()) {
if (pageProxy._browserContext !== context)
continue;
const page = pageProxy.existingPage();
if (page)
pages.push(page);
}
return pages;
},
newPage: async (): Promise<Page> => {
const { pageProxyId } = await this._browserSession.send('Browser.createPage', { browserContextId });
const pageProxy = this._pageProxies.get(pageProxyId)!;

View File

@ -484,16 +484,14 @@ export class WKPage implements PageDelegate {
return this._page._frameManager.frame(nodeInfo.contentFrameId);
}
async getOwnerFrame(handle: dom.ElementHandle): Promise<frames.Frame | null> {
async getOwnerFrame(handle: dom.ElementHandle): Promise<string | null> {
const remoteObject = toRemoteObject(handle);
if (!remoteObject.objectId)
return null;
const nodeInfo = await this._session.send('DOM.describeNode', {
objectId: remoteObject.objectId
});
if (!nodeInfo.ownerFrameId)
return null;
return this._page._frameManager.frame(nodeInfo.ownerFrameId);
return nodeInfo.ownerFrameId || null;
}
isElementHandle(remoteObject: any): boolean {

View File

@ -99,6 +99,10 @@ export class WKPageProxy {
return this._pagePromise;
}
existingPage(): Page | undefined {
return this._wkPage ? this._wkPage._page : undefined;
}
onPopupCreated(popupPageProxy: WKPageProxy) {
const wkPage = this._wkPage;
if (!wkPage || !wkPage._page.listenerCount(Events.Page.Popup))

View File

@ -155,7 +155,7 @@ module.exports.describe = function({testRunner, expect, FFOX, CHROMIUM, WEBKIT})
});
expect(await divHandle.ownerFrame()).toBe(page.mainFrame());
});
xit('should work for adopted elements', async({page,server}) => {
it.skip(FFOX)('should work for adopted elements', async({page,server}) => {
await page.goto(server.EMPTY_PAGE);
const [popup] = await Promise.all([
page.waitForEvent('popup'),