-
Notifications
You must be signed in to change notification settings - Fork 9k
/
BrowserConnector.ts
133 lines (124 loc) · 4.37 KB
/
BrowserConnector.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/*
* Copyright 2023 Google Inc. All rights reserved.
*
* 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 type {BrowserCloseCallback} from '../api/Browser.js';
import {Connection} from '../cdp/Connection.js';
import type {ConnectionTransport} from '../common/ConnectionTransport.js';
import type {
BrowserConnectOptions,
ConnectOptions,
} from '../common/ConnectOptions.js';
import {UnsupportedOperation} from '../common/Errors.js';
import {debugError, DEFAULT_VIEWPORT} from '../common/util.js';
import type {BidiBrowser} from './Browser.js';
import type {BidiConnection} from './Connection.js';
/**
* Users should never call this directly; it's called when calling `puppeteer.connect`
* with `protocol: 'webDriverBiDi'`. This method attaches Puppeteer to an existing browser
* instance. First it tries to connect to the browser using pure BiDi. If the protocol is
* not supported, connects to the browser using BiDi over CDP.
*
* @internal
*/
export async function _connectToBiDiBrowser(
connectionTransport: ConnectionTransport,
url: string,
options: BrowserConnectOptions & ConnectOptions
): Promise<BidiBrowser> {
const {ignoreHTTPSErrors = false, defaultViewport = DEFAULT_VIEWPORT} =
options;
const {bidiConnection, closeCallback} = await getBiDiConnection(
connectionTransport,
url,
options
);
const BiDi = await import(/* webpackIgnore: true */ './bidi.js');
const bidiBrowser = await BiDi.BidiBrowser.create({
connection: bidiConnection,
closeCallback,
process: undefined,
defaultViewport: defaultViewport,
ignoreHTTPSErrors: ignoreHTTPSErrors,
});
return bidiBrowser;
}
/**
* Returns a BiDiConnection established to the endpoint specified by the options and a
* callback closing the browser. Callback depends on whether the connection is pure BiDi
* or BiDi over CDP.
* The method tries to connect to the browser using pure BiDi protocol, and falls back
* to BiDi over CDP.
*/
async function getBiDiConnection(
connectionTransport: ConnectionTransport,
url: string,
options: BrowserConnectOptions
): Promise<{
bidiConnection: BidiConnection;
closeCallback: BrowserCloseCallback;
}> {
const BiDi = await import(/* webpackIgnore: true */ './bidi.js');
const {ignoreHTTPSErrors = false, slowMo = 0, protocolTimeout} = options;
// Try pure BiDi first.
const pureBidiConnection = new BiDi.BidiConnection(
url,
connectionTransport,
slowMo,
protocolTimeout
);
try {
const result = await pureBidiConnection.send('session.status', {});
if ('type' in result && result.type === 'success') {
// The `browserWSEndpoint` points to an endpoint supporting pure WebDriver BiDi.
return {
bidiConnection: pureBidiConnection,
closeCallback: async () => {
await pureBidiConnection.send('browser.close', {}).catch(debugError);
},
};
}
} catch (e: any) {
if (!('name' in e && e.name === 'ProtocolError')) {
// Unexpected exception not related to BiDi / CDP. Rethrow.
throw e;
}
}
// Unbind the connection to avoid memory leaks.
pureBidiConnection.unbind();
// Fall back to CDP over BiDi reusing the WS connection.
const cdpConnection = new Connection(
url,
connectionTransport,
slowMo,
protocolTimeout
);
const version = await cdpConnection.send('Browser.getVersion');
if (version.product.toLowerCase().includes('firefox')) {
throw new UnsupportedOperation(
'Firefox is not supported in BiDi over CDP mode.'
);
}
// TODO: use other options too.
const bidiOverCdpConnection = await BiDi.connectBidiOverCdp(cdpConnection, {
acceptInsecureCerts: ignoreHTTPSErrors,
});
return {
bidiConnection: bidiOverCdpConnection,
closeCallback: async () => {
// In case of BiDi over CDP, we need to close browser via CDP.
await cdpConnection.send('Browser.close').catch(debugError);
},
};
}