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 { export interface BrowserContextDelegate {
pages(): Promise<Page[]>; pages(): Promise<Page[]>;
existingPages(): Page[];
newPage(): Promise<Page>; newPage(): Promise<Page>;
close(): Promise<void>; close(): Promise<void>;
@ -69,6 +70,10 @@ export class BrowserContext {
await this.setGeolocation(this._options.geolocation); await this.setGeolocation(this._options.geolocation);
} }
_existingPages(): Page[] {
return this._delegate.existingPages();
}
async pages(): Promise<Page[]> { async pages(): Promise<Page[]> {
return this._delegate.pages(); 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[]; 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> => { newPage: async (): Promise<Page> => {
const { targetId } = await this._client.send('Target.createTarget', { url: 'about:blank', browserContextId: contextId || undefined }); const { targetId } = await this._client.send('Target.createTarget', { url: 'about:blank', browserContextId: contextId || undefined });
const target = this._targets.get(targetId)!; 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); 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. // document.documentElement has frameId of the owner frame.
const documentElement = await handle.evaluateHandle(node => { const documentElement = await handle.evaluateHandle(node => {
const doc = node as Document; const doc = node as Document;
@ -440,10 +440,10 @@ export class CRPage implements PageDelegate {
const nodeInfo = await this._client.send('DOM.describeNode', { const nodeInfo = await this._client.send('DOM.describeNode', {
objectId: remoteObject.objectId objectId: remoteObject.objectId
}); });
const frame = nodeInfo && typeof nodeInfo.node.frameId === 'string' ? const frameId = nodeInfo && typeof nodeInfo.node.frameId === 'string' ?
this._page._frameManager.frame(nodeInfo.node.frameId) : null; nodeInfo.node.frameId : null;
await documentElement.dispose(); await documentElement.dispose();
return frame; return frameId;
} }
isElementHandle(remoteObject: any): boolean { 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> { 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> { 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); 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> => { newPage: async (): Promise<Page> => {
const {targetId} = await this._connection.send('Target.newPage', { const {targetId} = await this._connection.send('Target.newPage', {
browserContextId: browserContextId || undefined browserContextId: browserContextId || undefined

View File

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

View File

@ -62,7 +62,7 @@ export interface PageDelegate {
isElementHandle(remoteObject: any): boolean; isElementHandle(remoteObject: any): boolean;
adoptElementHandle<T extends Node>(handle: dom.ElementHandle<T>, to: dom.FrameExecutionContext): Promise<dom.ElementHandle<T>>; 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. 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>; getContentQuads(handle: dom.ElementHandle): Promise<types.Quad[] | null>;
layoutViewport(): Promise<{ width: number, height: number }>; layoutViewport(): Promise<{ width: number, height: number }>;
setInputFiles(handle: dom.ElementHandle<HTMLInputElement>, files: types.FilePayload[]): Promise<void>; 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())); 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> => { newPage: async (): Promise<Page> => {
const { pageProxyId } = await this._browserSession.send('Browser.createPage', { browserContextId }); const { pageProxyId } = await this._browserSession.send('Browser.createPage', { browserContextId });
const pageProxy = this._pageProxies.get(pageProxyId)!; const pageProxy = this._pageProxies.get(pageProxyId)!;

View File

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

View File

@ -99,6 +99,10 @@ export class WKPageProxy {
return this._pagePromise; return this._pagePromise;
} }
existingPage(): Page | undefined {
return this._wkPage ? this._wkPage._page : undefined;
}
onPopupCreated(popupPageProxy: WKPageProxy) { onPopupCreated(popupPageProxy: WKPageProxy) {
const wkPage = this._wkPage; const wkPage = this._wkPage;
if (!wkPage || !wkPage._page.listenerCount(Events.Page.Popup)) 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()); 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); await page.goto(server.EMPTY_PAGE);
const [popup] = await Promise.all([ const [popup] = await Promise.all([
page.waitForEvent('popup'), page.waitForEvent('popup'),