mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore: do not initialize full sessions for pages used in session restore (#12886)
This commit is contained in:
parent
4aaa63beaa
commit
98ed81dc00
@ -38,9 +38,7 @@ export class BrowserDispatcher extends Dispatcher<Browser, channels.BrowserChann
|
||||
}
|
||||
|
||||
async newContext(params: channels.BrowserNewContextParams, metadata: CallMetadata): Promise<channels.BrowserNewContextResult> {
|
||||
const context = await this._object.newContext(params);
|
||||
if (params.storageState)
|
||||
await context.setStorageState(metadata, params.storageState);
|
||||
const context = await this._object.newContext(metadata, params);
|
||||
return { context: new BrowserContextDispatcher(this._scope, context) };
|
||||
}
|
||||
|
||||
@ -91,12 +89,10 @@ export class ConnectedBrowserDispatcher extends Dispatcher<Browser, channels.Bro
|
||||
async newContext(params: channels.BrowserNewContextParams, metadata: CallMetadata): Promise<channels.BrowserNewContextResult> {
|
||||
if (params.recordVideo)
|
||||
params.recordVideo.dir = this._object.options.artifactsDir;
|
||||
const context = await this._object.newContext(params);
|
||||
const context = await this._object.newContext(metadata, params);
|
||||
this._contexts.add(context);
|
||||
context._setSelectors(this.selectors);
|
||||
context.on(BrowserContext.Events.Close, () => this._contexts.delete(context));
|
||||
if (params.storageState)
|
||||
await context.setStorageState(metadata, params.storageState);
|
||||
return { context: new BrowserContextDispatcher(this._scope, context) };
|
||||
}
|
||||
|
||||
|
@ -15,13 +15,13 @@
|
||||
*/
|
||||
|
||||
import * as types from './types';
|
||||
import { BrowserContext } from './browserContext';
|
||||
import { BrowserContext, validateBrowserContextOptions } from './browserContext';
|
||||
import { Page } from './page';
|
||||
import { Download } from './download';
|
||||
import { ProxySettings } from './types';
|
||||
import { ChildProcess } from 'child_process';
|
||||
import { RecentLogsCollector } from '../utils/debugLogger';
|
||||
import { SdkObject } from './instrumentation';
|
||||
import { CallMetadata, SdkObject } from './instrumentation';
|
||||
import { Artifact } from './artifact';
|
||||
import { Selectors } from './selectors';
|
||||
|
||||
@ -74,12 +74,20 @@ export abstract class Browser extends SdkObject {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
abstract newContext(options: types.BrowserContextOptions): Promise<BrowserContext>;
|
||||
abstract doCreateNewContext(options: types.BrowserContextOptions): Promise<BrowserContext>;
|
||||
abstract contexts(): BrowserContext[];
|
||||
abstract isConnected(): boolean;
|
||||
abstract version(): string;
|
||||
abstract userAgent(): string;
|
||||
|
||||
async newContext(metadata: CallMetadata, options: types.BrowserContextOptions): Promise<BrowserContext> {
|
||||
validateBrowserContextOptions(options, this.options);
|
||||
const context = await this.doCreateNewContext(options);
|
||||
if (options.storageState)
|
||||
await context.setStorageState(metadata, options.storageState);
|
||||
return context;
|
||||
}
|
||||
|
||||
_downloadCreated(page: Page, uuid: string, url: string, suggestedFilename?: string) {
|
||||
const download = new Download(page, this.options.downloadsPath || '', uuid, url, suggestedFilename);
|
||||
this._downloads.set(uuid, download);
|
||||
|
@ -68,6 +68,7 @@ export abstract class BrowserContext extends SdkObject {
|
||||
readonly fetchRequest: BrowserContextAPIRequestContext;
|
||||
private _customCloseHandler?: () => Promise<any>;
|
||||
readonly _tempDirs: string[] = [];
|
||||
private _settingStorageState = false;
|
||||
|
||||
constructor(browser: Browser, options: types.BrowserContextOptions, browserContextId: string | undefined) {
|
||||
super(browser, 'browser-context');
|
||||
@ -375,25 +376,34 @@ export abstract class BrowserContext extends SdkObject {
|
||||
return result;
|
||||
}
|
||||
|
||||
isSettingStorageState(): boolean {
|
||||
return this._settingStorageState;
|
||||
}
|
||||
|
||||
async setStorageState(metadata: CallMetadata, state: types.SetStorageState) {
|
||||
if (state.cookies)
|
||||
await this.addCookies(state.cookies);
|
||||
if (state.origins && state.origins.length) {
|
||||
const internalMetadata = serverSideCallMetadata();
|
||||
const page = await this.newPage(internalMetadata);
|
||||
await page._setServerRequestInterceptor(handler => {
|
||||
handler.fulfill({ body: '<html></html>' }).catch(() => {});
|
||||
});
|
||||
for (const originState of state.origins) {
|
||||
const frame = page.mainFrame();
|
||||
await frame.goto(metadata, originState.origin);
|
||||
await frame.evaluateExpression(`
|
||||
originState => {
|
||||
for (const { name, value } of (originState.localStorage || []))
|
||||
localStorage.setItem(name, value);
|
||||
}`, true, originState, 'utility');
|
||||
this._settingStorageState = true;
|
||||
try {
|
||||
if (state.cookies)
|
||||
await this.addCookies(state.cookies);
|
||||
if (state.origins && state.origins.length) {
|
||||
const internalMetadata = serverSideCallMetadata();
|
||||
const page = await this.newPage(internalMetadata);
|
||||
await page._setServerRequestInterceptor(handler => {
|
||||
handler.fulfill({ body: '<html></html>' }).catch(() => {});
|
||||
});
|
||||
for (const originState of state.origins) {
|
||||
const frame = page.mainFrame();
|
||||
await frame.goto(metadata, originState.origin);
|
||||
await frame.evaluateExpression(`
|
||||
originState => {
|
||||
for (const { name, value } of (originState.localStorage || []))
|
||||
localStorage.setItem(name, value);
|
||||
}`, true, originState, 'utility');
|
||||
}
|
||||
await page.close(internalMetadata);
|
||||
}
|
||||
await page.close(internalMetadata);
|
||||
} finally {
|
||||
this._settingStorageState = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
import { Browser, BrowserOptions } from '../browser';
|
||||
import { assertBrowserContextIsNotOwned, BrowserContext, validateBrowserContextOptions, verifyGeolocation } from '../browserContext';
|
||||
import { assertBrowserContextIsNotOwned, BrowserContext, verifyGeolocation } from '../browserContext';
|
||||
import { assert } from '../../utils/utils';
|
||||
import * as network from '../network';
|
||||
import { Page, PageBinding, PageDelegate, Worker } from '../page';
|
||||
@ -92,9 +92,7 @@ export class CRBrowser extends Browser {
|
||||
this._session.on('Browser.downloadProgress', this._onDownloadProgress.bind(this));
|
||||
}
|
||||
|
||||
async newContext(options: types.BrowserContextOptions): Promise<BrowserContext> {
|
||||
validateBrowserContextOptions(options, this.options);
|
||||
|
||||
async doCreateNewContext(options: types.BrowserContextOptions): Promise<BrowserContext> {
|
||||
let proxyBypassList = undefined;
|
||||
if (options.proxy) {
|
||||
if (process.env.PLAYWRIGHT_DISABLE_FORCED_CHROMIUM_PROXIED_LOOPBACK)
|
||||
|
@ -439,7 +439,8 @@ class FrameSession {
|
||||
}
|
||||
|
||||
async _initialize(hasUIWindow: boolean) {
|
||||
if (hasUIWindow &&
|
||||
const isSettingStorageState = this._page._browserContext.isSettingStorageState();
|
||||
if (!isSettingStorageState && hasUIWindow &&
|
||||
!this._crPage._browserContext._browser.isClank() &&
|
||||
!this._crPage._browserContext._options.noDefaultViewport) {
|
||||
const { windowId } = await this._client.send('Browser.getWindowForTarget');
|
||||
@ -447,7 +448,7 @@ class FrameSession {
|
||||
}
|
||||
|
||||
let screencastOptions: types.PageScreencastOptions | undefined;
|
||||
if (this._isMainFrame() && this._crPage._browserContext._options.recordVideo && hasUIWindow) {
|
||||
if (!isSettingStorageState && this._isMainFrame() && this._crPage._browserContext._options.recordVideo && hasUIWindow) {
|
||||
const screencastId = createGuid();
|
||||
const outputFile = path.join(this._crPage._browserContext._options.recordVideo.dir, screencastId + '.webm');
|
||||
screencastOptions = {
|
||||
@ -512,41 +513,43 @@ class FrameSession {
|
||||
this._networkManager.initialize(),
|
||||
this._client.send('Target.setAutoAttach', { autoAttach: true, waitForDebuggerOnStart: true, flatten: true }),
|
||||
];
|
||||
if (this._isMainFrame())
|
||||
promises.push(this._client.send('Emulation.setFocusEmulationEnabled', { enabled: true }));
|
||||
const options = this._crPage._browserContext._options;
|
||||
if (options.bypassCSP)
|
||||
promises.push(this._client.send('Page.setBypassCSP', { enabled: true }));
|
||||
if (options.ignoreHTTPSErrors)
|
||||
promises.push(this._client.send('Security.setIgnoreCertificateErrors', { ignore: true }));
|
||||
if (this._isMainFrame())
|
||||
promises.push(this._updateViewport());
|
||||
if (options.hasTouch)
|
||||
promises.push(this._client.send('Emulation.setTouchEmulationEnabled', { enabled: true }));
|
||||
if (options.javaScriptEnabled === false)
|
||||
promises.push(this._client.send('Emulation.setScriptExecutionDisabled', { value: true }));
|
||||
if (options.userAgent || options.locale)
|
||||
promises.push(this._client.send('Emulation.setUserAgentOverride', { userAgent: options.userAgent || '', acceptLanguage: options.locale }));
|
||||
if (options.locale)
|
||||
promises.push(emulateLocale(this._client, options.locale));
|
||||
if (options.timezoneId)
|
||||
promises.push(emulateTimezone(this._client, options.timezoneId));
|
||||
if (!this._crPage._browserContext._browser.options.headful)
|
||||
promises.push(this._setDefaultFontFamilies(this._client));
|
||||
promises.push(this._updateGeolocation(true));
|
||||
promises.push(this._updateExtraHTTPHeaders(true));
|
||||
promises.push(this._updateRequestInterception());
|
||||
promises.push(this._updateOffline(true));
|
||||
promises.push(this._updateHttpCredentials(true));
|
||||
promises.push(this._updateEmulateMedia(true));
|
||||
for (const binding of this._crPage._page.allBindings())
|
||||
promises.push(this._initBinding(binding));
|
||||
for (const source of this._crPage._browserContext._evaluateOnNewDocumentSources)
|
||||
promises.push(this._evaluateOnNewDocument(source, 'main'));
|
||||
for (const source of this._crPage._page._evaluateOnNewDocumentSources)
|
||||
promises.push(this._evaluateOnNewDocument(source, 'main'));
|
||||
if (screencastOptions)
|
||||
promises.push(this._startVideoRecording(screencastOptions));
|
||||
if (!isSettingStorageState) {
|
||||
if (this._isMainFrame())
|
||||
promises.push(this._client.send('Emulation.setFocusEmulationEnabled', { enabled: true }));
|
||||
const options = this._crPage._browserContext._options;
|
||||
if (options.bypassCSP)
|
||||
promises.push(this._client.send('Page.setBypassCSP', { enabled: true }));
|
||||
if (options.ignoreHTTPSErrors)
|
||||
promises.push(this._client.send('Security.setIgnoreCertificateErrors', { ignore: true }));
|
||||
if (this._isMainFrame())
|
||||
promises.push(this._updateViewport());
|
||||
if (options.hasTouch)
|
||||
promises.push(this._client.send('Emulation.setTouchEmulationEnabled', { enabled: true }));
|
||||
if (options.javaScriptEnabled === false)
|
||||
promises.push(this._client.send('Emulation.setScriptExecutionDisabled', { value: true }));
|
||||
if (options.userAgent || options.locale)
|
||||
promises.push(this._client.send('Emulation.setUserAgentOverride', { userAgent: options.userAgent || '', acceptLanguage: options.locale }));
|
||||
if (options.locale)
|
||||
promises.push(emulateLocale(this._client, options.locale));
|
||||
if (options.timezoneId)
|
||||
promises.push(emulateTimezone(this._client, options.timezoneId));
|
||||
if (!this._crPage._browserContext._browser.options.headful)
|
||||
promises.push(this._setDefaultFontFamilies(this._client));
|
||||
promises.push(this._updateGeolocation(true));
|
||||
promises.push(this._updateExtraHTTPHeaders(true));
|
||||
promises.push(this._updateRequestInterception());
|
||||
promises.push(this._updateOffline(true));
|
||||
promises.push(this._updateHttpCredentials(true));
|
||||
promises.push(this._updateEmulateMedia(true));
|
||||
for (const binding of this._crPage._page.allBindings())
|
||||
promises.push(this._initBinding(binding));
|
||||
for (const source of this._crPage._browserContext._evaluateOnNewDocumentSources)
|
||||
promises.push(this._evaluateOnNewDocument(source, 'main'));
|
||||
for (const source of this._crPage._page._evaluateOnNewDocumentSources)
|
||||
promises.push(this._evaluateOnNewDocument(source, 'main'));
|
||||
if (screencastOptions)
|
||||
promises.push(this._startVideoRecording(screencastOptions));
|
||||
}
|
||||
promises.push(this._client.send('Runtime.runIfWaitingForDebugger'));
|
||||
promises.push(this._firstNonInitialNavigationCommittedPromise);
|
||||
await Promise.all(promises);
|
||||
|
@ -18,7 +18,7 @@
|
||||
import { kBrowserClosedError } from '../../utils/errors';
|
||||
import { assert } from '../../utils/utils';
|
||||
import { Browser, BrowserOptions } from '../browser';
|
||||
import { assertBrowserContextIsNotOwned, BrowserContext, validateBrowserContextOptions, verifyGeolocation } from '../browserContext';
|
||||
import { assertBrowserContextIsNotOwned, BrowserContext, verifyGeolocation } from '../browserContext';
|
||||
import * as network from '../network';
|
||||
import { Page, PageBinding, PageDelegate } from '../page';
|
||||
import { ConnectionTransport } from '../transport';
|
||||
@ -76,8 +76,7 @@ export class FFBrowser extends Browser {
|
||||
return !this._connection._closed;
|
||||
}
|
||||
|
||||
async newContext(options: types.BrowserContextOptions): Promise<BrowserContext> {
|
||||
validateBrowserContextOptions(options, this.options);
|
||||
async doCreateNewContext(options: types.BrowserContextOptions): Promise<BrowserContext> {
|
||||
if (options.isMobile)
|
||||
throw new Error('options.isMobile is not supported in Firefox');
|
||||
const { browserContextId } = await this._connection.send('Browser.createBrowserContext', { removeOnDetach: true });
|
||||
|
@ -257,6 +257,7 @@ export type BrowserContextOptions = {
|
||||
omitContent?: boolean,
|
||||
path: string
|
||||
},
|
||||
storageState?: SetStorageState,
|
||||
strictSelectors?: boolean,
|
||||
proxy?: ProxySettings,
|
||||
baseURL?: string,
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
import { Browser, BrowserOptions } from '../browser';
|
||||
import { assertBrowserContextIsNotOwned, BrowserContext, validateBrowserContextOptions, verifyGeolocation } from '../browserContext';
|
||||
import { assertBrowserContextIsNotOwned, BrowserContext, verifyGeolocation } from '../browserContext';
|
||||
import { eventsHelper, RegisteredListener } from '../../utils/eventsHelper';
|
||||
import { assert } from '../../utils/utils';
|
||||
import * as network from '../network';
|
||||
@ -79,8 +79,7 @@ export class WKBrowser extends Browser {
|
||||
this._didClose();
|
||||
}
|
||||
|
||||
async newContext(options: types.BrowserContextOptions): Promise<BrowserContext> {
|
||||
validateBrowserContextOptions(options, this.options);
|
||||
async doCreateNewContext(options: types.BrowserContextOptions): Promise<BrowserContext> {
|
||||
const createOptions = options.proxy ? {
|
||||
proxyServer: options.proxy.server,
|
||||
proxyBypassList: options.proxy.bypass
|
||||
|
@ -114,6 +114,8 @@ export class WKPage implements PageDelegate {
|
||||
}
|
||||
|
||||
private async _initializePageProxySession() {
|
||||
if (this._page._browserContext.isSettingStorageState())
|
||||
return;
|
||||
const promises: Promise<any>[] = [
|
||||
this._pageProxySession.send('Dialog.enable'),
|
||||
this._pageProxySession.send('Emulation.setActiveAndFocused', { active: true }),
|
||||
@ -183,6 +185,10 @@ export class WKPage implements PageDelegate {
|
||||
promises.push(session.send('Network.setInterceptionEnabled', { enabled: true }));
|
||||
promises.push(session.send('Network.addInterception', { url: '.*', stage: 'request', isRegex: true }));
|
||||
}
|
||||
if (this._page._browserContext.isSettingStorageState()) {
|
||||
await Promise.all(promises);
|
||||
return;
|
||||
}
|
||||
|
||||
const contextOptions = this._browserContext._options;
|
||||
if (contextOptions.userAgent)
|
||||
|
@ -48,8 +48,8 @@ it('should capture local storage', async ({ contextFactory }) => {
|
||||
}]);
|
||||
});
|
||||
|
||||
it('should set local storage', async ({ browser }) => {
|
||||
const context = await browser.newContext({
|
||||
it('should set local storage', async ({ contextFactory }) => {
|
||||
const context = await contextFactory({
|
||||
storageState: {
|
||||
cookies: [],
|
||||
origins: [
|
||||
@ -162,3 +162,35 @@ it('should not emit events about internal page', async ({ contextFactory }) => {
|
||||
await context.storageState();
|
||||
expect(events).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should not restore localStorage twice', async ({ contextFactory }) => {
|
||||
const context = await contextFactory({
|
||||
storageState: {
|
||||
cookies: [],
|
||||
origins: [
|
||||
{
|
||||
origin: 'https://www.example.com',
|
||||
localStorage: [{
|
||||
name: 'name1',
|
||||
value: 'value1'
|
||||
}]
|
||||
},
|
||||
]
|
||||
}
|
||||
});
|
||||
const page = await context.newPage();
|
||||
await page.route('**/*', route => {
|
||||
route.fulfill({ body: '<html></html>' }).catch(() => {});
|
||||
});
|
||||
await page.goto('https://www.example.com');
|
||||
const localStorage1 = await page.evaluate('window.localStorage');
|
||||
expect(localStorage1).toEqual({ name1: 'value1' });
|
||||
|
||||
await page.evaluate(() => window.localStorage['name1'] = 'value2');
|
||||
|
||||
await page.goto('https://www.example.com');
|
||||
const localStorage2 = await page.evaluate('window.localStorage');
|
||||
expect(localStorage2).toEqual({ name1: 'value2' });
|
||||
|
||||
await context.close();
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user