fix(scrollIntoView): make it work for nested frames case
This commit is contained in:
parent
8828228702
commit
51bf9f15c8
|
|
@ -445,10 +445,13 @@ export class FrameManager implements PageDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getContentFrame(handle: dom.ElementHandle): Promise<frames.Frame | null> {
|
async getContentFrame(handle: dom.ElementHandle): Promise<frames.Frame | null> {
|
||||||
|
const doc = await handle.evaluateHandle(node => node.ownerDocument ? node.ownerDocument.documentElement : null);
|
||||||
|
if (!doc)
|
||||||
|
return null;
|
||||||
const nodeInfo = await this._client.send('DOM.describeNode', {
|
const nodeInfo = await this._client.send('DOM.describeNode', {
|
||||||
objectId: toRemoteObject(handle).objectId
|
objectId: toRemoteObject(doc).objectId
|
||||||
});
|
});
|
||||||
if (typeof nodeInfo.node.frameId !== 'string')
|
if (!nodeInfo || typeof nodeInfo.node.frameId !== 'string')
|
||||||
return null;
|
return null;
|
||||||
return this._page._frameManager.frame(nodeInfo.node.frameId);
|
return this._page._frameManager.frame(nodeInfo.node.frameId);
|
||||||
}
|
}
|
||||||
|
|
@ -510,6 +513,16 @@ export class FrameManager implements PageDelegate {
|
||||||
throw new Error('Unable to adopt element handle from a different document');
|
throw new Error('Unable to adopt element handle from a different document');
|
||||||
return to._createHandle(result.object).asElement()!;
|
return to._createHandle(result.object).asElement()!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getFrameOwner(frame: frames.Frame): Promise<dom.ElementHandle | null> {
|
||||||
|
if (!frame.parentFrame())
|
||||||
|
return null;
|
||||||
|
const result = await this._client.send('DOM.getFrameOwner', { frameId: frame._id });
|
||||||
|
if (!result)
|
||||||
|
return null;
|
||||||
|
const utilityContext = await frame.parentFrame()._utilityContext();
|
||||||
|
return this.adoptBackendNodeId(result.backendNodeId, utilityContext);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toRemoteObject(handle: dom.ElementHandle): Protocol.Runtime.RemoteObject {
|
function toRemoteObject(handle: dom.ElementHandle): Protocol.Runtime.RemoteObject {
|
||||||
|
|
|
||||||
12
src/dom.ts
12
src/dom.ts
|
|
@ -152,6 +152,18 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||||
}
|
}
|
||||||
|
|
||||||
async _scrollIntoViewIfNeeded() {
|
async _scrollIntoViewIfNeeded() {
|
||||||
|
const path: frames.Frame[] = [];
|
||||||
|
for (let frame = await this.contentFrame(); frame; frame = frame.parentFrame())
|
||||||
|
path.push(frame);
|
||||||
|
for (const frame of path.reverse()) {
|
||||||
|
const owner = await frame._page._delegate.getFrameOwner(frame);
|
||||||
|
if (owner)
|
||||||
|
await owner._scrollIntoViewIfNeededWithinFrame();
|
||||||
|
}
|
||||||
|
this._scrollIntoViewIfNeededWithinFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
async _scrollIntoViewIfNeededWithinFrame() {
|
||||||
const error = await this.evaluate(async (node: Node, pageJavascriptEnabled: boolean) => {
|
const error = await this.evaluate(async (node: Node, pageJavascriptEnabled: boolean) => {
|
||||||
if (!node.isConnected)
|
if (!node.isConnected)
|
||||||
return 'Node is detached from document';
|
return 'Node is detached from document';
|
||||||
|
|
|
||||||
|
|
@ -406,6 +406,10 @@ export class FrameManager implements PageDelegate {
|
||||||
assert(false, 'Multiple isolated worlds are not implemented');
|
assert(false, 'Multiple isolated worlds are not implemented');
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getFrameOwner(frame: frames.Frame): Promise<dom.ElementHandle | null> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function normalizeWaitUntil(waitUntil: frames.LifecycleEvent | frames.LifecycleEvent[]): frames.LifecycleEvent[] {
|
export function normalizeWaitUntil(waitUntil: frames.LifecycleEvent | frames.LifecycleEvent[]): frames.LifecycleEvent[] {
|
||||||
|
|
|
||||||
|
|
@ -62,6 +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>;
|
getContentFrame(handle: dom.ElementHandle): Promise<frames.Frame | null>;
|
||||||
|
getFrameOwner(frame: frames.Frame): Promise<dom.ElementHandle | null>;
|
||||||
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, files: input.FilePayload[]): Promise<void>;
|
setInputFiles(handle: dom.ElementHandle, files: input.FilePayload[]): Promise<void>;
|
||||||
|
|
|
||||||
|
|
@ -487,6 +487,10 @@ export class FrameManager implements PageDelegate {
|
||||||
});
|
});
|
||||||
return to._createHandle(result.object) as dom.ElementHandle<T>;
|
return to._createHandle(result.object) as dom.ElementHandle<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getFrameOwner(frame: frames.Frame): Promise<dom.ElementHandle | null> {
|
||||||
|
throw new Error('Not implemented');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function toRemoteObject(handle: dom.ElementHandle): Protocol.Runtime.RemoteObject {
|
function toRemoteObject(handle: dom.ElementHandle): Protocol.Runtime.RemoteObject {
|
||||||
|
|
|
||||||
|
|
@ -261,8 +261,7 @@ module.exports.addTests = function({testRunner, expect, playwright, FFOX, CHROME
|
||||||
await button.click();
|
await button.click();
|
||||||
expect(await frame.evaluate(() => window.result)).toBe('Clicked');
|
expect(await frame.evaluate(() => window.result)).toBe('Clicked');
|
||||||
});
|
});
|
||||||
// @see https://github.com/GoogleChrome/puppeteer/issues/4110
|
it.skip(WEBKIT || FFOX)('should click the button with fixed position inside an iframe', async({page, server}) => {
|
||||||
xit('should click the button with fixed position inside an iframe', async({page, server}) => {
|
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
await page.setViewport({width: 500, height: 500});
|
await page.setViewport({width: 500, height: 500});
|
||||||
await page.setContent('<div style="width:100px;height:2000px">spacer</div>');
|
await page.setContent('<div style="width:100px;height:2000px">spacer</div>');
|
||||||
|
|
|
||||||
|
|
@ -105,10 +105,6 @@ const utils = module.exports = {
|
||||||
frame.src = url;
|
frame.src = url;
|
||||||
frame.id = frameId;
|
frame.id = frameId;
|
||||||
document.body.appendChild(frame);
|
document.body.appendChild(frame);
|
||||||
// TODO(einbinder) do this right
|
|
||||||
// Access a scriptable global object to ensure JS context is
|
|
||||||
// initialized. WebKit will create it lazily only as need be.
|
|
||||||
frame.contentWindow;
|
|
||||||
await new Promise(x => frame.onload = x);
|
await new Promise(x => frame.onload = x);
|
||||||
return frame;
|
return frame;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue