mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
fix(popups): do not override popup size from window features (#2139)
We usually force window size from the browser context. However, popups that have window features insist on a specific window size, so we respect that.
This commit is contained in:
parent
e2972ad5ba
commit
aac5bf24ec
@ -56,6 +56,13 @@ export class CRPage implements PageDelegate {
|
|||||||
private readonly _pagePromise: Promise<Page | Error>;
|
private readonly _pagePromise: Promise<Page | Error>;
|
||||||
_initializedPage: Page | null = null;
|
_initializedPage: Page | null = null;
|
||||||
|
|
||||||
|
// Holds window features for the next popup being opened via window.open,
|
||||||
|
// until the popup target arrives. This could be racy if two oopifs
|
||||||
|
// simultaneously call window.open with window features: the order
|
||||||
|
// of their Page.windowOpen events is not guaranteed to match the order
|
||||||
|
// of new popup targets.
|
||||||
|
readonly _nextWindowOpenPopupFeatures: string[][] = [];
|
||||||
|
|
||||||
constructor(client: CRSession, targetId: string, browserContext: CRBrowserContext, opener: CRPage | null, hasUIWindow: boolean) {
|
constructor(client: CRSession, targetId: string, browserContext: CRBrowserContext, opener: CRPage | null, hasUIWindow: boolean) {
|
||||||
this._targetId = targetId;
|
this._targetId = targetId;
|
||||||
this._opener = opener;
|
this._opener = opener;
|
||||||
@ -68,6 +75,12 @@ export class CRPage implements PageDelegate {
|
|||||||
this._mainFrameSession = new FrameSession(this, client, targetId, null);
|
this._mainFrameSession = new FrameSession(this, client, targetId, null);
|
||||||
this._sessions.set(targetId, this._mainFrameSession);
|
this._sessions.set(targetId, this._mainFrameSession);
|
||||||
client.once(CRSessionEvents.Disconnected, () => this._page._didDisconnect());
|
client.once(CRSessionEvents.Disconnected, () => this._page._didDisconnect());
|
||||||
|
if (opener && browserContext._options.viewport !== null) {
|
||||||
|
const features = opener._nextWindowOpenPopupFeatures.shift() || [];
|
||||||
|
const viewportSize = helper.getViewportSizeFromWindowFeatures(features);
|
||||||
|
if (viewportSize)
|
||||||
|
this._page._state.viewportSize = viewportSize;
|
||||||
|
}
|
||||||
this._pagePromise = this._mainFrameSession._initialize(hasUIWindow).then(() => this._initializedPage = this._page).catch(e => e);
|
this._pagePromise = this._mainFrameSession._initialize(hasUIWindow).then(() => this._initializedPage = this._page).catch(e => e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -372,6 +385,7 @@ class FrameSession {
|
|||||||
helper.addEventListener(this._client, 'Runtime.executionContextsCleared', event => this._onExecutionContextsCleared()),
|
helper.addEventListener(this._client, 'Runtime.executionContextsCleared', event => this._onExecutionContextsCleared()),
|
||||||
helper.addEventListener(this._client, 'Target.attachedToTarget', event => this._onAttachedToTarget(event)),
|
helper.addEventListener(this._client, 'Target.attachedToTarget', event => this._onAttachedToTarget(event)),
|
||||||
helper.addEventListener(this._client, 'Target.detachedFromTarget', event => this._onDetachedFromTarget(event)),
|
helper.addEventListener(this._client, 'Target.detachedFromTarget', event => this._onDetachedFromTarget(event)),
|
||||||
|
helper.addEventListener(this._client, 'Page.windowOpen', event => this._onWindowOpen(event)),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -591,6 +605,10 @@ class FrameSession {
|
|||||||
this._page._removeWorker(event.sessionId);
|
this._page._removeWorker(event.sessionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_onWindowOpen(event: Protocol.Page.windowOpenPayload) {
|
||||||
|
this._crPage._nextWindowOpenPopupFeatures.push(event.windowFeatures);
|
||||||
|
}
|
||||||
|
|
||||||
async _onConsoleAPI(event: Protocol.Runtime.consoleAPICalledPayload) {
|
async _onConsoleAPI(event: Protocol.Runtime.consoleAPICalledPayload) {
|
||||||
if (event.executionContextId === 0) {
|
if (event.executionContextId === 0) {
|
||||||
// DevTools protocol stores the last 1000 console messages. These
|
// DevTools protocol stores the last 1000 console messages. These
|
||||||
|
|||||||
@ -346,6 +346,16 @@ class Helper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getViewportSizeFromWindowFeatures(features: string[]): types.Size | null {
|
||||||
|
const widthString = features.find(f => f.startsWith('width='));
|
||||||
|
const heightString = features.find(f => f.startsWith('height='));
|
||||||
|
const width = widthString ? parseInt(widthString.substring(6), 10) : NaN;
|
||||||
|
const height = heightString ? parseInt(heightString.substring(7), 10) : NaN;
|
||||||
|
if (!Number.isNaN(width) && !Number.isNaN(height))
|
||||||
|
return { width, height };
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function assert(value: any, message?: string): asserts value {
|
export function assert(value: any, message?: string): asserts value {
|
||||||
|
|||||||
@ -57,6 +57,7 @@ export class WKBrowser extends BrowserBase {
|
|||||||
helper.addEventListener(this._browserSession, 'Playwright.pageProxyCreated', this._onPageProxyCreated.bind(this)),
|
helper.addEventListener(this._browserSession, 'Playwright.pageProxyCreated', this._onPageProxyCreated.bind(this)),
|
||||||
helper.addEventListener(this._browserSession, 'Playwright.pageProxyDestroyed', this._onPageProxyDestroyed.bind(this)),
|
helper.addEventListener(this._browserSession, 'Playwright.pageProxyDestroyed', this._onPageProxyDestroyed.bind(this)),
|
||||||
helper.addEventListener(this._browserSession, 'Playwright.provisionalLoadFailed', event => this._onProvisionalLoadFailed(event)),
|
helper.addEventListener(this._browserSession, 'Playwright.provisionalLoadFailed', event => this._onProvisionalLoadFailed(event)),
|
||||||
|
helper.addEventListener(this._browserSession, 'Playwright.windowOpen', event => this._onWindowOpen(event)),
|
||||||
helper.addEventListener(this._browserSession, 'Playwright.downloadCreated', this._onDownloadCreated.bind(this)),
|
helper.addEventListener(this._browserSession, 'Playwright.downloadCreated', this._onDownloadCreated.bind(this)),
|
||||||
helper.addEventListener(this._browserSession, 'Playwright.downloadFilenameSuggested', this._onDownloadFilenameSuggested.bind(this)),
|
helper.addEventListener(this._browserSession, 'Playwright.downloadFilenameSuggested', this._onDownloadFilenameSuggested.bind(this)),
|
||||||
helper.addEventListener(this._browserSession, 'Playwright.downloadFinished', this._onDownloadFinished.bind(this)),
|
helper.addEventListener(this._browserSession, 'Playwright.downloadFinished', this._onDownloadFinished.bind(this)),
|
||||||
@ -180,6 +181,13 @@ export class WKBrowser extends BrowserBase {
|
|||||||
wkPage.handleProvisionalLoadFailed(event);
|
wkPage.handleProvisionalLoadFailed(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_onWindowOpen(event: Protocol.Playwright.windowOpenPayload) {
|
||||||
|
const wkPage = this._wkPages.get(event.pageProxyId);
|
||||||
|
if (!wkPage)
|
||||||
|
return;
|
||||||
|
wkPage.handleWindowOpen(event);
|
||||||
|
}
|
||||||
|
|
||||||
isConnected(): boolean {
|
isConnected(): boolean {
|
||||||
return !this._connection.isClosed();
|
return !this._connection.isClosed();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import * as frames from '../frames';
|
import * as frames from '../frames';
|
||||||
import { helper, RegisteredListener, assert } from '../helper';
|
import { helper, RegisteredListener, assert, debugAssert } from '../helper';
|
||||||
import * as dom from '../dom';
|
import * as dom from '../dom';
|
||||||
import * as network from '../network';
|
import * as network from '../network';
|
||||||
import { WKSession } from './wkConnection';
|
import { WKSession } from './wkConnection';
|
||||||
@ -68,6 +68,10 @@ export class WKPage implements PageDelegate {
|
|||||||
_firstNonInitialNavigationCommittedReject = (e: Error) => {};
|
_firstNonInitialNavigationCommittedReject = (e: Error) => {};
|
||||||
private _lastConsoleMessage: { derivedType: string, text: string, handles: JSHandle[]; count: number, location: ConsoleMessageLocation; } | null = null;
|
private _lastConsoleMessage: { derivedType: string, text: string, handles: JSHandle[]; count: number, location: ConsoleMessageLocation; } | null = null;
|
||||||
|
|
||||||
|
// Holds window features for the next popup being opened via window.open,
|
||||||
|
// until the popup page proxy arrives.
|
||||||
|
private _nextWindowOpenPopupFeatures?: string[];
|
||||||
|
|
||||||
constructor(browserContext: WKBrowserContext, pageProxySession: WKSession, opener: WKPage | null) {
|
constructor(browserContext: WKBrowserContext, pageProxySession: WKSession, opener: WKPage | null) {
|
||||||
this._pageProxySession = pageProxySession;
|
this._pageProxySession = pageProxySession;
|
||||||
this._opener = opener;
|
this._opener = opener;
|
||||||
@ -90,6 +94,12 @@ export class WKPage implements PageDelegate {
|
|||||||
this._firstNonInitialNavigationCommittedFulfill = f;
|
this._firstNonInitialNavigationCommittedFulfill = f;
|
||||||
this._firstNonInitialNavigationCommittedReject = r;
|
this._firstNonInitialNavigationCommittedReject = r;
|
||||||
});
|
});
|
||||||
|
if (opener && browserContext._options.viewport !== null && opener._nextWindowOpenPopupFeatures) {
|
||||||
|
const viewportSize = helper.getViewportSizeFromWindowFeatures(opener._nextWindowOpenPopupFeatures);
|
||||||
|
opener._nextWindowOpenPopupFeatures = undefined;
|
||||||
|
if (viewportSize)
|
||||||
|
this._page._state.viewportSize = viewportSize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _initializePageProxySession() {
|
private async _initializePageProxySession() {
|
||||||
@ -243,6 +253,11 @@ export class WKPage implements PageDelegate {
|
|||||||
this._page._frameManager.provisionalLoadFailed(this._page.mainFrame(), event.loaderId, errorText);
|
this._page._frameManager.provisionalLoadFailed(this._page.mainFrame(), event.loaderId, errorText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleWindowOpen(event: Protocol.Playwright.windowOpenPayload) {
|
||||||
|
debugAssert(!this._nextWindowOpenPopupFeatures);
|
||||||
|
this._nextWindowOpenPopupFeatures = event.windowFeatures;
|
||||||
|
}
|
||||||
|
|
||||||
async pageOrError(): Promise<Page | Error> {
|
async pageOrError(): Promise<Page | Error> {
|
||||||
return this._pagePromise;
|
return this._pagePromise;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -138,6 +138,26 @@ describe('window.open', function() {
|
|||||||
await context.close();
|
await context.close();
|
||||||
expect(size).toEqual({width: 400, height: 500});
|
expect(size).toEqual({width: 400, height: 500});
|
||||||
});
|
});
|
||||||
|
it('should use viewport size from window features', async function({browser, server}) {
|
||||||
|
const context = await browser.newContext({
|
||||||
|
viewport: { width: 700, height: 700 }
|
||||||
|
});
|
||||||
|
const page = await context.newPage();
|
||||||
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
const [size, popup] = await Promise.all([
|
||||||
|
page.evaluate(() => {
|
||||||
|
const win = window.open(window.location.href, 'Title', 'toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes,width=600,height=300,top=0,left=0');
|
||||||
|
return { width: win.innerWidth, height: win.innerHeight };
|
||||||
|
}),
|
||||||
|
page.waitForEvent('popup'),
|
||||||
|
]);
|
||||||
|
await popup.setViewportSize({ width: 500, height: 400 });
|
||||||
|
await popup.waitForLoadState();
|
||||||
|
const resized = await popup.evaluate(() => ({ width: window.innerWidth, height: window.innerHeight }));
|
||||||
|
await context.close();
|
||||||
|
expect(size).toEqual({width: 600, height: 300});
|
||||||
|
expect(resized).toEqual({width: 500, height: 400});
|
||||||
|
});
|
||||||
it('should respect routes from browser context', async function({browser, server}) {
|
it('should respect routes from browser context', async function({browser, server}) {
|
||||||
const context = await browser.newContext();
|
const context = await browser.newContext();
|
||||||
const page = await context.newPage();
|
const page = await context.newPage();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user