Skip to content

Commit 9fd30e5

Browse files
authored
feat(rpc): ensure feature-detection works as before (#2898)
For this, some tests are migrated from skip() to feature detection, like our users would do.
1 parent 2151757 commit 9fd30e5

18 files changed

+235
-148
lines changed

src/rpc/channels.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,11 +98,7 @@ export interface BrowserContextChannel extends Channel {
9898
on(event: 'crServiceWorker', callback: (params: WorkerChannel) => void): this;
9999
crNewCDPSession(params: { page: PageChannel }): Promise<CDPSessionChannel>;
100100
}
101-
export type BrowserContextInitializer = {
102-
pages: PageChannel[],
103-
crBackgroundPages: PageChannel[],
104-
crServiceWorkers: WorkerChannel[],
105-
};
101+
export type BrowserContextInitializer = {};
106102

107103

108104
export interface PageChannel extends Channel {
@@ -117,7 +113,6 @@ export interface PageChannel extends Channel {
117113
on(event: 'frameAttached', callback: (params: FrameChannel) => void): this;
118114
on(event: 'frameDetached', callback: (params: FrameChannel) => void): this;
119115
on(event: 'frameNavigated', callback: (params: { frame: FrameChannel, url: string, name: string }) => void): this;
120-
on(event: 'frameNavigated', callback: (params: { frame: FrameChannel, url: string, name: string }) => void): this;
121116
on(event: 'load', callback: () => void): this;
122117
on(event: 'pageError', callback: (params: { error: types.Error }) => void): this;
123118
on(event: 'popup', callback: (params: PageChannel) => void): this;

src/rpc/client/browser.ts

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,15 @@ import { BrowserContext } from './browserContext';
2020
import { Page } from './page';
2121
import { ChannelOwner } from './channelOwner';
2222
import { Events } from '../../events';
23-
import { CDPSession } from './cdpSession';
2423
import { LoggerSink } from '../../loggerSink';
24+
import { BrowserType } from './browserType';
2525

2626
export class Browser extends ChannelOwner<BrowserChannel, BrowserInitializer> {
2727
readonly _contexts = new Set<BrowserContext>();
2828
private _isConnected = true;
2929
private _isClosedOrClosing = false;
3030
private _closedPromise: Promise<void>;
31+
readonly _browserType: BrowserType;
3132

3233
static from(browser: BrowserChannel): Browser {
3334
return (browser as any)._object;
@@ -39,6 +40,7 @@ export class Browser extends ChannelOwner<BrowserChannel, BrowserInitializer> {
3940

4041
constructor(parent: ChannelOwner, type: string, guid: string, initializer: BrowserInitializer) {
4142
super(parent, type, guid, initializer, true);
43+
this._browserType = parent as BrowserType;
4244
this._channel.on('close', () => {
4345
this._isConnected = false;
4446
this.emit(Events.Browser.Disconnected);
@@ -54,7 +56,6 @@ export class Browser extends ChannelOwner<BrowserChannel, BrowserInitializer> {
5456
const context = BrowserContext.from(await this._channel.newContext(options));
5557
this._contexts.add(context);
5658
context._logger = logger || this._logger;
57-
context._browser = this;
5859
return context;
5960
}
6061

@@ -81,16 +82,4 @@ export class Browser extends ChannelOwner<BrowserChannel, BrowserInitializer> {
8182
}
8283
await this._closedPromise;
8384
}
84-
85-
async newBrowserCDPSession(): Promise<CDPSession> {
86-
return CDPSession.from(await this._channel.crNewBrowserCDPSession());
87-
}
88-
89-
async startTracing(page?: Page, options: { path?: string; screenshots?: boolean; categories?: string[]; } = {}) {
90-
await this._channel.crStartTracing({ ...options, page: page ? page._channel : undefined });
91-
}
92-
93-
async stopTracing(): Promise<Buffer> {
94-
return Buffer.from(await this._channel.crStopTracing(), 'base64');
95-
}
9685
}

src/rpc/client/browserContext.ts

Lines changed: 11 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,13 @@ import { helper } from '../../helper';
2525
import { Browser } from './browser';
2626
import { Events } from '../../events';
2727
import { TimeoutSettings } from '../../timeoutSettings';
28-
import { CDPSession } from './cdpSession';
29-
import { Events as ChromiumEvents } from '../../chromium/events';
30-
import { Worker } from './worker';
28+
import { BrowserType } from './browserType';
3129

3230
export class BrowserContext extends ChannelOwner<BrowserContextChannel, BrowserContextInitializer> {
3331
_pages = new Set<Page>();
34-
_crBackgroundPages = new Set<Page>();
35-
_crServiceWorkers = new Set<Worker>();
3632
private _routes: { url: types.URLMatch, handler: network.RouteHandler }[] = [];
37-
_browser: Browser | undefined;
33+
readonly _browser: Browser | undefined;
34+
readonly _browserType: BrowserType;
3835
readonly _bindings = new Map<string, frames.FunctionWithSource>();
3936
private _pendingWaitForEvents = new Map<(error: Error) => void, string>();
4037
_timeoutSettings = new TimeoutSettings();
@@ -52,43 +49,22 @@ export class BrowserContext extends ChannelOwner<BrowserContextChannel, BrowserC
5249

5350
constructor(parent: ChannelOwner, type: string, guid: string, initializer: BrowserContextInitializer) {
5451
super(parent, type, guid, initializer, true);
55-
initializer.pages.forEach(p => {
56-
const page = Page.from(p);
57-
this._pages.add(page);
58-
page._setBrowserContext(this);
59-
});
52+
if (parent instanceof Browser) {
53+
this._browser = parent;
54+
this._browserType = this._browser._browserType;
55+
} else {
56+
// Launching persistent context does not produce a browser at all.
57+
this._browserType = parent as BrowserType;
58+
}
59+
6060
this._channel.on('bindingCall', bindingCall => this._onBinding(BindingCall.from(bindingCall)));
6161
this._channel.on('close', () => this._onClose());
6262
this._channel.on('page', page => this._onPage(Page.from(page)));
6363
this._channel.on('route', ({ route, request }) => this._onRoute(network.Route.from(route), network.Request.from(request)));
6464
this._closedPromise = new Promise(f => this.once(Events.BrowserContext.Close, f));
65-
66-
initializer.crBackgroundPages.forEach(p => {
67-
const page = Page.from(p);
68-
this._crBackgroundPages.add(page);
69-
page._setBrowserContext(this);
70-
});
71-
this._channel.on('crBackgroundPage', pageChannel => {
72-
const page = Page.from(pageChannel);
73-
page._setBrowserContext(this);
74-
this._crBackgroundPages.add(page);
75-
this.emit(ChromiumEvents.CRBrowserContext.BackgroundPage, page);
76-
});
77-
initializer.crServiceWorkers.forEach(w => {
78-
const worker = Worker.from(w);
79-
worker._context = this;
80-
this._crServiceWorkers.add(worker);
81-
});
82-
this._channel.on('crServiceWorker', serviceWorkerChannel => {
83-
const worker = Worker.from(serviceWorkerChannel);
84-
worker._context = this;
85-
this._crServiceWorkers.add(worker);
86-
this.emit(ChromiumEvents.CRBrowserContext.ServiceWorker, worker);
87-
});
8865
}
8966

9067
private _onPage(page: Page): void {
91-
page._setBrowserContext(this);
9268
this._pages.add(page);
9369
this.emit(Events.BrowserContext.Page, page);
9470
}
@@ -234,16 +210,4 @@ export class BrowserContext extends ChannelOwner<BrowserContextChannel, BrowserC
234210
}
235211
await this._closedPromise;
236212
}
237-
238-
async newCDPSession(page: Page): Promise<CDPSession> {
239-
return CDPSession.from(await this._channel.crNewCDPSession({ page: page._channel }));
240-
}
241-
242-
backgroundPages(): Page[] {
243-
return [...this._crBackgroundPages];
244-
}
245-
246-
serviceWorkers(): Worker[] {
247-
return [...this._crServiceWorkers];
248-
}
249213
}

src/rpc/client/browserType.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export class BrowserType extends ChannelOwner<BrowserTypeChannel, BrowserTypeIni
2929
}
3030

3131
constructor(parent: ChannelOwner, type: string, guid: string, initializer: BrowserTypeInitializer) {
32-
super(parent, type, guid, initializer);
32+
super(parent, type, guid, initializer, true);
3333
}
3434

3535
executablePath(): string {

src/rpc/client/chromiumBrowser.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* Copyright (c) Microsoft Corporation.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { Page } from './page';
18+
import { CDPSession } from './cdpSession';
19+
import { Browser } from './browser';
20+
21+
export class ChromiumBrowser extends Browser {
22+
async newBrowserCDPSession(): Promise<CDPSession> {
23+
return CDPSession.from(await this._channel.crNewBrowserCDPSession());
24+
}
25+
26+
async startTracing(page?: Page, options: { path?: string; screenshots?: boolean; categories?: string[]; } = {}) {
27+
await this._channel.crStartTracing({ ...options, page: page ? page._channel : undefined });
28+
}
29+
30+
async stopTracing(): Promise<Buffer> {
31+
return Buffer.from(await this._channel.crStopTracing(), 'base64');
32+
}
33+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* Copyright 2017 Google Inc. All rights reserved.
3+
* Modifications copyright (c) Microsoft Corporation.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import { Page } from './page';
19+
import { BrowserContextInitializer } from '../channels';
20+
import { ChannelOwner } from './channelOwner';
21+
import { CDPSession } from './cdpSession';
22+
import { Events as ChromiumEvents } from '../../chromium/events';
23+
import { Worker } from './worker';
24+
import { BrowserContext } from './browserContext';
25+
26+
export class ChromiumBrowserContext extends BrowserContext {
27+
_backgroundPages = new Set<Page>();
28+
_serviceWorkers = new Set<Worker>();
29+
30+
constructor(parent: ChannelOwner, type: string, guid: string, initializer: BrowserContextInitializer) {
31+
super(parent, type, guid, initializer);
32+
this._channel.on('crBackgroundPage', pageChannel => {
33+
const page = Page.from(pageChannel);
34+
this._backgroundPages.add(page);
35+
this.emit(ChromiumEvents.CRBrowserContext.BackgroundPage, page);
36+
});
37+
this._channel.on('crServiceWorker', serviceWorkerChannel => {
38+
const worker = Worker.from(serviceWorkerChannel);
39+
worker._context = this;
40+
this._serviceWorkers.add(worker);
41+
this.emit(ChromiumEvents.CRBrowserContext.ServiceWorker, worker);
42+
});
43+
}
44+
45+
backgroundPages(): Page[] {
46+
return [...this._backgroundPages];
47+
}
48+
49+
serviceWorkers(): Worker[] {
50+
return [...this._serviceWorkers];
51+
}
52+
53+
async newCDPSession(page: Page): Promise<CDPSession> {
54+
return CDPSession.from(await this._channel.crNewCDPSession({ page: page._channel }));
55+
}
56+
}

src/rpc/client/connection.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ import { BrowserServer } from './browserServer';
3333
import { CDPSession } from './cdpSession';
3434
import { Playwright } from './playwright';
3535
import { Channel } from '../channels';
36+
import { ChromiumBrowser } from './chromiumBrowser';
37+
import { ChromiumBrowserContext } from './chromiumBrowserContext';
3638

3739
class Root extends ChannelOwner<Channel, {}> {
3840
constructor(connection: Connection) {
@@ -133,7 +135,10 @@ export class Connection {
133135
result = new BindingCall(parent, type, guid, initializer);
134136
break;
135137
case 'browser':
136-
result = new Browser(parent, type, guid, initializer);
138+
if ((parent as BrowserType).name() === 'chromium')
139+
result = new ChromiumBrowser(parent, type, guid, initializer);
140+
else
141+
result = new Browser(parent, type, guid, initializer);
137142
break;
138143
case 'browserServer':
139144
result = new BrowserServer(parent, type, guid, initializer);
@@ -145,7 +150,12 @@ export class Connection {
145150
result = new CDPSession(parent, type, guid, initializer);
146151
break;
147152
case 'context':
148-
result = new BrowserContext(parent, type, guid, initializer);
153+
// Launching persistent context produces BrowserType parent directly for BrowserContext.
154+
const browserType = parent instanceof Browser ? parent._browserType : parent as BrowserType;
155+
if (browserType.name() === 'chromium')
156+
result = new ChromiumBrowserContext(parent, type, guid, initializer);
157+
else
158+
result = new BrowserContext(parent, type, guid, initializer);
149159
break;
150160
case 'consoleMessage':
151161
result = new ConsoleMessage(parent, type, guid, initializer);

0 commit comments

Comments
 (0)