diff --git a/docs/src/api/params.md b/docs/src/api/params.md index 3aa5d9bc94..212bb00097 100644 --- a/docs/src/api/params.md +++ b/docs/src/api/params.md @@ -637,10 +637,18 @@ contexts override the proxy, global proxy will be never used and can be any stri ## context-option-strict - `strictSelectors` <[boolean]> -It specified, enables strict selectors mode for this context. In the strict selectors mode all operations +If specified, enables strict selectors mode for this context. In the strict selectors mode all operations on selectors that imply single target DOM element will throw when more than one element matches the selector. See [Locator] to learn more about the strict mode. +## context-option-service-worker-policy +- `serviceWorkers` <[ServiceWorkerPolicy]<"allow"|"block">> + +* `"allow"`: [Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) can be registered by sites. +* `"block"`: Playwright will block all registration of Service Workers. + +Defaults to `"allow"`. + ## select-options-values * langs: java, js, csharp - `values` <[null]|[string]|[ElementHandle]|[Array]<[string]>|[Object]|[Array]<[ElementHandle]>|[Array]<[Object]>> @@ -796,6 +804,7 @@ An acceptable perceived color difference in the [YIQ color space](https://en.wik - %%-context-option-recordvideo-dir-%% - %%-context-option-recordvideo-size-%% - %%-context-option-strict-%% +- %%-context-option-service-worker-policy-%% ## browser-option-args - `args` <[Array]<[string]>> @@ -1020,4 +1029,3 @@ When set to `"hide"`, screenshot will hide text caret. When set to `"initial"`, - %%-screenshot-option-type-%% - %%-screenshot-option-mask-%% - %%-input-timeout-%% - diff --git a/docs/src/test-api/class-testoptions.md b/docs/src/test-api/class-testoptions.md index 0152d29c32..44036c8bcc 100644 --- a/docs/src/test-api/class-testoptions.md +++ b/docs/src/test-api/class-testoptions.md @@ -215,3 +215,4 @@ Learn more about [recording video](../test-configuration.md#record-video). ## property: TestOptions.viewport = %%-context-option-viewport-%% +## property: TestOptions.serviceWorkers = %%-context-option-service-worker-policy-%% diff --git a/packages/playwright-core/src/client/browserContext.ts b/packages/playwright-core/src/client/browserContext.ts index 275f9a0304..2cfd36123c 100644 --- a/packages/playwright-core/src/client/browserContext.ts +++ b/packages/playwright-core/src/client/browserContext.ts @@ -413,6 +413,7 @@ export async function prepareBrowserContextParams(options: BrowserContextOptions noDefaultViewport: options.viewport === null, extraHTTPHeaders: options.extraHTTPHeaders ? headersObjectToArray(options.extraHTTPHeaders) : undefined, storageState: await prepareStorageState(options), + serviceWorkers: options.serviceWorkers, recordHar: prepareRecordHarOptions(options.recordHar), }; if (!contextParams.recordVideo && options.videosPath) { diff --git a/packages/playwright-core/src/protocol/channels.ts b/packages/playwright-core/src/protocol/channels.ts index 614a07d024..020e7a3e3e 100644 --- a/packages/playwright-core/src/protocol/channels.ts +++ b/packages/playwright-core/src/protocol/channels.ts @@ -747,6 +747,7 @@ export type BrowserTypeLaunchPersistentContextParams = { }, recordHar?: RecordHarOptions, strictSelectors?: boolean, + serviceWorkers?: 'allow' | 'block', userDataDir: string, slowMo?: number, }; @@ -816,6 +817,7 @@ export type BrowserTypeLaunchPersistentContextOptions = { }, recordHar?: RecordHarOptions, strictSelectors?: boolean, + serviceWorkers?: 'allow' | 'block', slowMo?: number, }; export type BrowserTypeLaunchPersistentContextResult = { @@ -909,6 +911,7 @@ export type BrowserNewContextParams = { }, recordHar?: RecordHarOptions, strictSelectors?: boolean, + serviceWorkers?: 'allow' | 'block', proxy?: { server: string, bypass?: string, @@ -965,6 +968,7 @@ export type BrowserNewContextOptions = { }, recordHar?: RecordHarOptions, strictSelectors?: boolean, + serviceWorkers?: 'allow' | 'block', proxy?: { server: string, bypass?: string, @@ -3984,6 +3988,7 @@ export type AndroidDeviceLaunchBrowserParams = { }, recordHar?: RecordHarOptions, strictSelectors?: boolean, + serviceWorkers?: 'allow' | 'block', pkg?: string, proxy?: { server: string, @@ -4037,6 +4042,7 @@ export type AndroidDeviceLaunchBrowserOptions = { }, recordHar?: RecordHarOptions, strictSelectors?: boolean, + serviceWorkers?: 'allow' | 'block', pkg?: string, proxy?: { server: string, diff --git a/packages/playwright-core/src/protocol/protocol.yml b/packages/playwright-core/src/protocol/protocol.yml index 168d0ba785..4d881778a1 100644 --- a/packages/playwright-core/src/protocol/protocol.yml +++ b/packages/playwright-core/src/protocol/protocol.yml @@ -455,6 +455,11 @@ ContextOptions: height: number recordHar: RecordHarOptions? strictSelectors: boolean? + serviceWorkers: + type: enum? + literals: + - allow + - block LocalUtils: type: interface diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts index e7e0db8b84..f6d320b530 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -354,6 +354,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { })), recordHar: tOptional(tType('RecordHarOptions')), strictSelectors: tOptional(tBoolean), + serviceWorkers: tOptional(tEnum(['allow', 'block'])), userDataDir: tString, slowMo: tOptional(tNumber), }); @@ -410,6 +411,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { })), recordHar: tOptional(tType('RecordHarOptions')), strictSelectors: tOptional(tBoolean), + serviceWorkers: tOptional(tEnum(['allow', 'block'])), proxy: tOptional(tObject({ server: tString, bypass: tOptional(tString), @@ -1441,6 +1443,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { })), recordHar: tOptional(tType('RecordHarOptions')), strictSelectors: tOptional(tBoolean), + serviceWorkers: tOptional(tEnum(['allow', 'block'])), pkg: tOptional(tString), proxy: tOptional(tObject({ server: tString, diff --git a/packages/playwright-core/src/server/browserContext.ts b/packages/playwright-core/src/server/browserContext.ts index c9cba9c1f3..fdc24a6bd9 100644 --- a/packages/playwright-core/src/server/browserContext.ts +++ b/packages/playwright-core/src/server/browserContext.ts @@ -122,6 +122,8 @@ export abstract class BrowserContext extends SdkObject { if (debugMode() === 'console') await this.extendInjectedScript(consoleApiSource.source); + if (this._options.serviceWorkers === 'block') + await this.addInitScript(`\nnavigator.serviceWorker.register = () => { console.warn('Service Worker registration blocked by Playwright'); };\n`); } async _ensureVideosPath() { diff --git a/packages/playwright-core/src/server/types.ts b/packages/playwright-core/src/server/types.ts index 1caa9eb47f..14a34f4298 100644 --- a/packages/playwright-core/src/server/types.ts +++ b/packages/playwright-core/src/server/types.ts @@ -266,6 +266,7 @@ export type BrowserContextOptions = { strictSelectors?: boolean, proxy?: ProxySettings, baseURL?: string, + serviceWorkers?: 'allow' | 'block', }; export type EnvArray = { name: string, value: string }[]; diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index 0af1a0dd02..2ce8411ad3 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -10371,13 +10371,22 @@ export interface BrowserType { height: number; }; + /** + * - `"allow"`: [Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) can be registered + * by sites. + * - `"block"`: Playwright will block all registration of Service Workers. + * + * Defaults to `"allow"`. + */ + serviceWorkers?: "allow"|"block"; + /** * Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on. */ slowMo?: number; /** - * It specified, enables strict selectors mode for this context. In the strict selectors mode all operations on selectors + * If specified, enables strict selectors mode for this context. In the strict selectors mode all operations on selectors * that imply single target DOM element will throw when more than one element matches the selector. See [Locator] to learn * more about the strict mode. */ @@ -11528,7 +11537,16 @@ export interface AndroidDevice { }; /** - * It specified, enables strict selectors mode for this context. In the strict selectors mode all operations on selectors + * - `"allow"`: [Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) can be registered + * by sites. + * - `"block"`: Playwright will block all registration of Service Workers. + * + * Defaults to `"allow"`. + */ + serviceWorkers?: "allow"|"block"; + + /** + * If specified, enables strict selectors mode for this context. In the strict selectors mode all operations on selectors * that imply single target DOM element will throw when more than one element matches the selector. See [Locator] to learn * more about the strict mode. */ @@ -13060,6 +13078,15 @@ export interface Browser extends EventEmitter { height: number; }; + /** + * - `"allow"`: [Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) can be registered + * by sites. + * - `"block"`: Playwright will block all registration of Service Workers. + * + * Defaults to `"allow"`. + */ + serviceWorkers?: "allow"|"block"; + /** * Populates context with given storage state. This option can be used to initialize context with logged-in information * obtained via @@ -13115,7 +13142,7 @@ export interface Browser extends EventEmitter { }; /** - * It specified, enables strict selectors mode for this context. In the strict selectors mode all operations on selectors + * If specified, enables strict selectors mode for this context. In the strict selectors mode all operations on selectors * that imply single target DOM element will throw when more than one element matches the selector. See [Locator] to learn * more about the strict mode. */ @@ -15521,6 +15548,15 @@ export interface BrowserContextOptions { height: number; }; + /** + * - `"allow"`: [Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) can be registered + * by sites. + * - `"block"`: Playwright will block all registration of Service Workers. + * + * Defaults to `"allow"`. + */ + serviceWorkers?: "allow"|"block"; + /** * Populates context with given storage state. This option can be used to initialize context with logged-in information * obtained via @@ -15576,7 +15612,7 @@ export interface BrowserContextOptions { }; /** - * It specified, enables strict selectors mode for this context. In the strict selectors mode all operations on selectors + * If specified, enables strict selectors mode for this context. In the strict selectors mode all operations on selectors * that imply single target DOM element will throw when more than one element matches the selector. See [Locator] to learn * more about the strict mode. */ diff --git a/packages/playwright-test/src/index.ts b/packages/playwright-test/src/index.ts index d605966763..cb4b6990c9 100644 --- a/packages/playwright-test/src/index.ts +++ b/packages/playwright-test/src/index.ts @@ -159,6 +159,7 @@ export const test = _baseTest.extend({ baseURL: [ async ({ }, use) => { await use(process.env.PLAYWRIGHT_TEST_BASE_URL); }, { option: true } ], + serviceWorkers: [ 'allow', { option: true } ], contextOptions: [ {}, { option: true } ], _combinedContextOptions: async ({ @@ -183,6 +184,7 @@ export const test = _baseTest.extend({ userAgent, baseURL, contextOptions, + serviceWorkers, }, use) => { const options: BrowserContextOptions = {}; if (acceptDownloads !== undefined) @@ -225,6 +227,8 @@ export const test = _baseTest.extend({ options.viewport = viewport; if (baseURL !== undefined) options.baseURL = baseURL; + if (serviceWorkers !== undefined) + options.serviceWorkers = serviceWorkers; await use({ ...contextOptions, ...options, diff --git a/packages/playwright-test/src/mount.ts b/packages/playwright-test/src/mount.ts index b76a967b65..22901efe3a 100644 --- a/packages/playwright-test/src/mount.ts +++ b/packages/playwright-test/src/mount.ts @@ -152,6 +152,7 @@ function contextHash(context: BrowserContextOptions): string { timezoneId: context.timezoneId, userAgent: context.userAgent, deviceScaleFactor: context.deviceScaleFactor, + serviceWorkers: context.serviceWorkers, }; return JSON.stringify(hash); } diff --git a/packages/playwright-test/types/test.d.ts b/packages/playwright-test/types/test.d.ts index e013500181..6f69184f51 100644 --- a/packages/playwright-test/types/test.d.ts +++ b/packages/playwright-test/types/test.d.ts @@ -2494,6 +2494,7 @@ type ColorScheme = Exclude; type ExtraHTTPHeaders = Exclude; type Proxy = Exclude; type StorageState = Exclude; +type ServiceWorkerPolicy = Exclude; type ConnectOptions = { /** * A browser websocket endpoint to connect to. @@ -2798,6 +2799,14 @@ export interface PlaywrightTestOptions { * Learn more about [various timeouts](https://playwright.dev/docs/test-timeouts). */ navigationTimeout: number | undefined; + /** + * - `"allow"`: [Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) can be registered + * by sites. + * - `"block"`: Playwright will block all registration of Service Workers. + * + * Defaults to `"allow"`. + */ + serviceWorkers: ServiceWorkerPolicy | undefined; } diff --git a/tests/library/browsercontext-service-worker-policy.spec.ts b/tests/library/browsercontext-service-worker-policy.spec.ts new file mode 100644 index 0000000000..213cf1461a --- /dev/null +++ b/tests/library/browsercontext-service-worker-policy.spec.ts @@ -0,0 +1,32 @@ +/** + * Copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { browserTest as it, expect } from '../config/browserTest'; + +it('should allow service workers by default', async ({ page, server }) => { + await page.goto(server.PREFIX + '/serviceworkers/empty/sw.html'); + await expect(page.evaluate(() => window['registrationPromise'])).resolves.toBeTruthy(); +}); + +it.describe('block', () => { + it.use({ serviceWorkers: 'block' }); + + it('blocks service worker registration', async ({ page, server }) => { + await Promise.all([ + page.waitForEvent('console', evt => evt.text() === 'Service Worker registration blocked by Playwright'), + page.goto(server.PREFIX + '/serviceworkers/empty/sw.html'), + ]); + }); +}); diff --git a/utils/generate_types/overrides-test.d.ts b/utils/generate_types/overrides-test.d.ts index b1e8648114..45adcd1b58 100644 --- a/utils/generate_types/overrides-test.d.ts +++ b/utils/generate_types/overrides-test.d.ts @@ -175,6 +175,7 @@ type ColorScheme = Exclude; type ExtraHTTPHeaders = Exclude; type Proxy = Exclude; type StorageState = Exclude; +type ServiceWorkerPolicy = Exclude; type ConnectOptions = { /** * A browser websocket endpoint to connect to. @@ -231,6 +232,7 @@ export interface PlaywrightTestOptions { contextOptions: BrowserContextOptions; actionTimeout: number | undefined; navigationTimeout: number | undefined; + serviceWorkers: ServiceWorkerPolicy | undefined; }