mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
fix(ownerFrame): correctly handle adopted node usecase (#677)
This commit is contained in:
parent
b3cd7a4365
commit
541fa95ce4
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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)!;
|
||||||
|
@ -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 {
|
||||||
|
11
src/dom.ts
11
src/dom.ts
@ -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> {
|
||||||
|
@ -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
|
||||||
|
@ -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 {
|
||||||
|
@ -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>;
|
||||||
|
@ -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)!;
|
||||||
|
@ -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 {
|
||||||
|
@ -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))
|
||||||
|
@ -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'),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user