Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions packages/playwright-core/src/server/bidi/bidiNetworkManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,33 @@ export class BidiNetworkManager {
return;
if (redirectedFrom)
this._deleteRequest(redirectedFrom._id);

// handle CORS preflight requests
if (param.request.method === 'OPTIONS') {
// TODO: we should detect preflight requests by looking at param.initiator.type, but the Bidi spec for
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know about other types, but marking preflight sounds non-controversial.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed the condition to if (param.initiator?.type === 'preflight' || requestHeaders['access-control-request-method'])

// the initiator type is incomplete and browser implementations are inconsistent, so we check for an
// Access-Control-Request-Method header instead. See https://github.com/w3c/webdriver-bidi/issues/698.
const requestHeaders = Object.fromEntries(param.request.headers.map(h => [h.name.toLowerCase(), bidiBytesValueToString(h.value)]));
if (param.initiator?.type === 'preflight' || requestHeaders['access-control-request-method']) {
if (param.intercepts) {
// If interception is enabled, we accept all CORS options, assuming that this was intended when setting the route.
const responseHeaders: types.HeadersArray = [
{ name: 'Access-Control-Allow-Origin', value: requestHeaders['origin'] || '*' },
{ name: 'Access-Control-Allow-Methods', value: requestHeaders['access-control-request-method'] },
{ name: 'Access-Control-Allow-Credentials', value: 'true' }
];
if (requestHeaders['access-control-request-headers'])
responseHeaders.push({ name: 'Access-Control-Allow-Headers', value: requestHeaders['access-control-request-headers'] });
this._session.sendMayFail('network.provideResponse', {
request: param.request.request,
statusCode: 204,
headers: toBidiHeaders(responseHeaders),
});
}
return;
}
}

let route;
let headersOverride: types.HeadersArray | undefined;
if (param.intercepts) {
Expand Down
3 changes: 3 additions & 0 deletions tests/bidi/expectations/moz-firefox-nightly-library.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ library/browsercontext-csp.spec.ts › should bypass after cross-process navigat
library/browsercontext-csp.spec.ts › should bypass CSP header [fail]
library/browsercontext-csp.spec.ts › should bypass CSP in iframes as well [fail]
library/browsercontext-csp.spec.ts › should bypass CSP meta tag @smoke [fail]
library/browsercontext-devtools.spec.ts › should close tab via close button [fail]
library/browsercontext-devtools.spec.ts › should show no-pages placeholder when all tabs are closed [fail]
library/browsercontext-devtools.spec.ts › should display screencast image [fail]
library/browsercontext-events.spec.ts › console event should work with element handles [fail]
library/browsercontext-fetch.spec.ts › should support set-cookie with SameSite and without Secure attribute over HTTP [fail]
library/browsercontext-har.spec.ts › should change document URL after redirected navigation [fail]
Expand Down
3 changes: 1 addition & 2 deletions tests/bidi/expectations/moz-firefox-nightly-page.txt
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ page/page-click.spec.ts › should click in a transformed iframe with force [fai
page/page-click.spec.ts › should click the button with fixed position inside an iframe [fail]
page/page-click.spec.ts › should double click the button [fail]
page/page-click.spec.ts › should select the text by triple clicking [fail]
library/page-close.spec.ts › should not treat navigations as new popups [fail]
page/page-drag.spec.ts › Drag and drop › should cancel on escape [fail]
page/page-drag.spec.ts › Drag and drop › should respect the drop effect [fail]
page/page-drag.spec.ts › Drag and drop › should send the right events [fail]
Expand All @@ -95,8 +96,6 @@ page/page-event-console.spec.ts › should not throw when there are console mess
page/page-event-console.spec.ts › should trigger correct Log [timeout]
page/page-event-console.spec.ts › should work @smoke [flaky]
page/page-event-console.spec.ts › should work for different console API calls [fail]
page/page-event-request.spec.ts › should not expose preflight OPTIONS request [fail]
page/page-event-request.spec.ts › should not expose preflight OPTIONS request with network interception [fail]
page/page-event-request.spec.ts › should report requests and responses handled by service worker [fail]
page/page-event-request.spec.ts › should report requests and responses handled by service worker with routing [fail]
page/page-expose-function.spec.ts › should work with setContent [timeout]
Expand Down
4 changes: 2 additions & 2 deletions tests/page/page-event-request.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ it('should not expose preflight OPTIONS request', {

it('should not expose preflight OPTIONS request with network interception', {
annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/36311' }
}, async ({ page, server, browserName }) => {
}, async ({ page, server, browserName, isBidi }) => {
const serverRequests = [];
server.setRoute('/cors', (req, res) => {
serverRequests.push(`${req.method} ${req.url}`);
Expand Down Expand Up @@ -374,7 +374,7 @@ it('should not expose preflight OPTIONS request with network interception', {
}, server.CROSS_PROCESS_PREFIX + '/cors').catch(() => {});
expect(response).toBe('Hello there!');
expect.soft(serverRequests).toEqual([
...(browserName !== 'chromium' ? ['OPTIONS /cors'] : []),
...(browserName === 'chromium' || isBidi ? [] : ['OPTIONS /cors']),
'POST /cors',
]);
expect.soft(clientRequests).toEqual([
Expand Down
4 changes: 2 additions & 2 deletions tests/page/page-network-request.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ it('should get the same headers as the server CORS', async ({ page, server, brow
expect(headers).toEqual(adjustServerHeaders(serverRequest.headers, browserName));
});

it('should not get preflight CORS requests when intercepting', async ({ page, server, browserName, isAndroid }) => {
it('should not get preflight CORS requests when intercepting', async ({ page, server, browserName, isAndroid, isBidi }) => {
it.fail(isAndroid, 'Playwright does not get CORS pre-flight on Android');
await page.goto(server.PREFIX + '/empty.html');

Expand Down Expand Up @@ -200,7 +200,7 @@ it('should not get preflight CORS requests when intercepting', async ({ page, se
expect(text).toBe('done');
// Check that there was no preflight (OPTIONS) request.
expect(routed).toEqual(['DELETE']);
if (browserName === 'firefox')
if (browserName === 'firefox' && !isBidi)
expect(requests).toEqual(['OPTIONS', 'DELETE']);
else
expect(requests).toEqual(['DELETE']);
Expand Down
4 changes: 2 additions & 2 deletions tests/page/page-route.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -777,7 +777,7 @@ it('should not auto-intercept non-preflight OPTIONS without network interception
});

// Make sure this runs in a new context as preflight results could be cached.
it('should not auto-intercept non-preflight OPTIONS with network interception', async ({ page, server, isAndroid, browserName }) => {
it('should not auto-intercept non-preflight OPTIONS with network interception', async ({ page, server, isAndroid, browserName, isBidi }) => {
it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/20469' });
it.fixme(isAndroid);

Expand Down Expand Up @@ -815,7 +815,7 @@ it('should not auto-intercept non-preflight OPTIONS with network interception',
expect.soft(text1).toBe('Hello');
expect.soft(text2).toBe('World');
// Preflight for OPTIONS is auto-fulfilled, then OPTIONS, then GET without preflight.
if (browserName === 'chromium')
if (browserName === 'chromium' || isBidi)
expect.soft(requests).toEqual(['OPTIONS:/something', 'GET:/something']);
else
expect.soft(requests).toEqual(['OPTIONS:/something', 'OPTIONS:/something', 'GET:/something']);
Expand Down
Loading