crBrowser.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.CRBrowserContext = exports.CRBrowser = void 0;
  6. var _path = _interopRequireDefault(require("path"));
  7. var _browser = require("../browser");
  8. var _browserContext = require("../browserContext");
  9. var _utils = require("../../utils");
  10. var network = _interopRequireWildcard(require("../network"));
  11. var _page = require("../page");
  12. var _frames = require("../frames");
  13. var _crConnection = require("./crConnection");
  14. var _crPage = require("./crPage");
  15. var _crProtocolHelper = require("./crProtocolHelper");
  16. var _crServiceWorker = require("./crServiceWorker");
  17. var _artifact = require("../artifact");
  18. function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
  19. function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
  20. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  21. /**
  22. * Copyright 2017 Google Inc. All rights reserved.
  23. * Modifications copyright (c) Microsoft Corporation.
  24. *
  25. * Licensed under the Apache License, Version 2.0 (the "License");
  26. * you may not use this file except in compliance with the License.
  27. * You may obtain a copy of the License at
  28. *
  29. * http://www.apache.org/licenses/LICENSE-2.0
  30. *
  31. * Unless required by applicable law or agreed to in writing, software
  32. * distributed under the License is distributed on an "AS IS" BASIS,
  33. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  34. * See the License for the specific language governing permissions and
  35. * limitations under the License.
  36. */
  37. class CRBrowser extends _browser.Browser {
  38. static async connect(parent, transport, options, devtools) {
  39. // Make a copy in case we need to update `headful` property below.
  40. options = {
  41. ...options
  42. };
  43. const connection = new _crConnection.CRConnection(transport, options.protocolLogger, options.browserLogsCollector);
  44. const browser = new CRBrowser(parent, connection, options);
  45. browser._devtools = devtools;
  46. if (browser.isClank()) browser._isCollocatedWithServer = false;
  47. const session = connection.rootSession;
  48. if (options.__testHookOnConnectToBrowser) await options.__testHookOnConnectToBrowser();
  49. const version = await session.send('Browser.getVersion');
  50. browser._version = version.product.substring(version.product.indexOf('/') + 1);
  51. browser._userAgent = version.userAgent;
  52. // We don't trust the option as it may lie in case of connectOverCDP where remote browser
  53. // may have been launched with different options.
  54. browser.options.headful = !version.userAgent.includes('Headless');
  55. if (!options.persistent) {
  56. await session.send('Target.setAutoAttach', {
  57. autoAttach: true,
  58. waitForDebuggerOnStart: true,
  59. flatten: true
  60. });
  61. return browser;
  62. }
  63. browser._defaultContext = new CRBrowserContext(browser, undefined, options.persistent);
  64. await Promise.all([session.send('Target.setAutoAttach', {
  65. autoAttach: true,
  66. waitForDebuggerOnStart: true,
  67. flatten: true
  68. }).then(async () => {
  69. // Target.setAutoAttach has a bug where it does not wait for new Targets being attached.
  70. // However making a dummy call afterwards fixes this.
  71. // This can be removed after https://chromium-review.googlesource.com/c/chromium/src/+/2885888 lands in stable.
  72. await session.send('Target.getTargetInfo');
  73. }), browser._defaultContext._initialize()]);
  74. await browser._waitForAllPagesToBeInitialized();
  75. return browser;
  76. }
  77. constructor(parent, connection, options) {
  78. super(parent, options);
  79. this._connection = void 0;
  80. this._session = void 0;
  81. this._clientRootSessionPromise = null;
  82. this._contexts = new Map();
  83. this._crPages = new Map();
  84. this._backgroundPages = new Map();
  85. this._serviceWorkers = new Map();
  86. this._devtools = void 0;
  87. this._version = '';
  88. this._tracingRecording = false;
  89. this._tracingClient = void 0;
  90. this._userAgent = '';
  91. this._connection = connection;
  92. this._session = this._connection.rootSession;
  93. this._connection.on(_crConnection.ConnectionEvents.Disconnected, () => this._didDisconnect());
  94. this._session.on('Target.attachedToTarget', this._onAttachedToTarget.bind(this));
  95. this._session.on('Target.detachedFromTarget', this._onDetachedFromTarget.bind(this));
  96. this._session.on('Browser.downloadWillBegin', this._onDownloadWillBegin.bind(this));
  97. this._session.on('Browser.downloadProgress', this._onDownloadProgress.bind(this));
  98. }
  99. async doCreateNewContext(options) {
  100. let proxyBypassList = undefined;
  101. if (options.proxy) {
  102. if (process.env.PLAYWRIGHT_DISABLE_FORCED_CHROMIUM_PROXIED_LOOPBACK) proxyBypassList = options.proxy.bypass;else proxyBypassList = '<-loopback>' + (options.proxy.bypass ? `,${options.proxy.bypass}` : '');
  103. }
  104. const {
  105. browserContextId
  106. } = await this._session.send('Target.createBrowserContext', {
  107. disposeOnDetach: true,
  108. proxyServer: options.proxy ? options.proxy.server : undefined,
  109. proxyBypassList
  110. });
  111. const context = new CRBrowserContext(this, browserContextId, options);
  112. await context._initialize();
  113. this._contexts.set(browserContextId, context);
  114. return context;
  115. }
  116. contexts() {
  117. return Array.from(this._contexts.values());
  118. }
  119. version() {
  120. return this._version;
  121. }
  122. userAgent() {
  123. return this._userAgent;
  124. }
  125. _platform() {
  126. if (this._userAgent.includes('Windows')) return 'win';
  127. if (this._userAgent.includes('Macintosh')) return 'mac';
  128. return 'linux';
  129. }
  130. isClank() {
  131. return this.options.name === 'clank';
  132. }
  133. async _waitForAllPagesToBeInitialized() {
  134. await Promise.all([...this._crPages.values()].map(page => page.pageOrError()));
  135. }
  136. _onAttachedToTarget({
  137. targetInfo,
  138. sessionId,
  139. waitingForDebugger
  140. }) {
  141. if (targetInfo.type === 'browser') return;
  142. const session = this._session.createChildSession(sessionId);
  143. (0, _utils.assert)(targetInfo.browserContextId, 'targetInfo: ' + JSON.stringify(targetInfo, null, 2));
  144. let context = this._contexts.get(targetInfo.browserContextId) || null;
  145. if (!context) {
  146. // TODO: auto attach only to pages from our contexts.
  147. // assert(this._defaultContext);
  148. context = this._defaultContext;
  149. }
  150. if (targetInfo.type === 'other' && targetInfo.url.startsWith('devtools://devtools') && this._devtools) {
  151. this._devtools.install(session);
  152. return;
  153. }
  154. const treatOtherAsPage = targetInfo.type === 'other' && process.env.PW_CHROMIUM_ATTACH_TO_OTHER;
  155. if (!context || targetInfo.type === 'other' && !treatOtherAsPage) {
  156. session.detach().catch(() => {});
  157. return;
  158. }
  159. (0, _utils.assert)(!this._crPages.has(targetInfo.targetId), 'Duplicate target ' + targetInfo.targetId);
  160. (0, _utils.assert)(!this._backgroundPages.has(targetInfo.targetId), 'Duplicate target ' + targetInfo.targetId);
  161. (0, _utils.assert)(!this._serviceWorkers.has(targetInfo.targetId), 'Duplicate target ' + targetInfo.targetId);
  162. if (targetInfo.type === 'background_page') {
  163. const backgroundPage = new _crPage.CRPage(session, targetInfo.targetId, context, null, {
  164. hasUIWindow: false,
  165. isBackgroundPage: true
  166. });
  167. this._backgroundPages.set(targetInfo.targetId, backgroundPage);
  168. return;
  169. }
  170. if (targetInfo.type === 'page' || treatOtherAsPage) {
  171. const opener = targetInfo.openerId ? this._crPages.get(targetInfo.openerId) || null : null;
  172. const crPage = new _crPage.CRPage(session, targetInfo.targetId, context, opener, {
  173. hasUIWindow: targetInfo.type === 'page',
  174. isBackgroundPage: false
  175. });
  176. this._crPages.set(targetInfo.targetId, crPage);
  177. return;
  178. }
  179. if (targetInfo.type === 'service_worker') {
  180. const serviceWorker = new _crServiceWorker.CRServiceWorker(context, session, targetInfo.url);
  181. this._serviceWorkers.set(targetInfo.targetId, serviceWorker);
  182. context.emit(CRBrowserContext.CREvents.ServiceWorker, serviceWorker);
  183. return;
  184. }
  185. // Detach from any targets we are not interested in, to avoid side-effects.
  186. //
  187. // One example of a side effect: upon shared worker restart, we receive
  188. // Inspector.targetReloadedAfterCrash and backend waits for Runtime.runIfWaitingForDebugger
  189. // from any attached client. If we do not resume, shared worker will stall.
  190. session.detach().catch(() => {});
  191. }
  192. _onDetachedFromTarget(payload) {
  193. const targetId = payload.targetId;
  194. const crPage = this._crPages.get(targetId);
  195. if (crPage) {
  196. this._crPages.delete(targetId);
  197. crPage.didClose();
  198. return;
  199. }
  200. const backgroundPage = this._backgroundPages.get(targetId);
  201. if (backgroundPage) {
  202. this._backgroundPages.delete(targetId);
  203. backgroundPage.didClose();
  204. return;
  205. }
  206. const serviceWorker = this._serviceWorkers.get(targetId);
  207. if (serviceWorker) {
  208. this._serviceWorkers.delete(targetId);
  209. serviceWorker.didClose();
  210. return;
  211. }
  212. }
  213. _didDisconnect() {
  214. for (const crPage of this._crPages.values()) crPage.didClose();
  215. this._crPages.clear();
  216. for (const backgroundPage of this._backgroundPages.values()) backgroundPage.didClose();
  217. this._backgroundPages.clear();
  218. for (const serviceWorker of this._serviceWorkers.values()) serviceWorker.didClose();
  219. this._serviceWorkers.clear();
  220. this._didClose();
  221. }
  222. _findOwningPage(frameId) {
  223. for (const crPage of this._crPages.values()) {
  224. const frame = crPage._page._frameManager.frame(frameId);
  225. if (frame) return crPage;
  226. }
  227. return null;
  228. }
  229. _onDownloadWillBegin(payload) {
  230. const page = this._findOwningPage(payload.frameId);
  231. if (!page) {
  232. // There might be no page when download originates from something unusual, like
  233. // a DevTools window or maybe an extension page.
  234. // See https://github.com/microsoft/playwright/issues/22551.
  235. return;
  236. }
  237. page.willBeginDownload();
  238. let originPage = page._initializedPage;
  239. // If it's a new window download, report it on the opener page.
  240. if (!originPage && page._opener) originPage = page._opener._initializedPage;
  241. if (!originPage) return;
  242. this._downloadCreated(originPage, payload.guid, payload.url, payload.suggestedFilename);
  243. }
  244. _onDownloadProgress(payload) {
  245. if (payload.state === 'completed') this._downloadFinished(payload.guid, '');
  246. if (payload.state === 'canceled') this._downloadFinished(payload.guid, this._closeReason || 'canceled');
  247. }
  248. async _closePage(crPage) {
  249. await this._session.send('Target.closeTarget', {
  250. targetId: crPage._targetId
  251. });
  252. }
  253. async newBrowserCDPSession() {
  254. return await this._connection.createBrowserSession();
  255. }
  256. async startTracing(page, options = {}) {
  257. (0, _utils.assert)(!this._tracingRecording, 'Cannot start recording trace while already recording trace.');
  258. this._tracingClient = page ? page._delegate._mainFrameSession._client : this._session;
  259. const defaultCategories = ['-*', 'devtools.timeline', 'v8.execute', 'disabled-by-default-devtools.timeline', 'disabled-by-default-devtools.timeline.frame', 'toplevel', 'blink.console', 'blink.user_timing', 'latencyInfo', 'disabled-by-default-devtools.timeline.stack', 'disabled-by-default-v8.cpu_profiler', 'disabled-by-default-v8.cpu_profiler.hires'];
  260. const {
  261. screenshots = false,
  262. categories = defaultCategories
  263. } = options;
  264. if (screenshots) categories.push('disabled-by-default-devtools.screenshot');
  265. this._tracingRecording = true;
  266. await this._tracingClient.send('Tracing.start', {
  267. transferMode: 'ReturnAsStream',
  268. categories: categories.join(',')
  269. });
  270. }
  271. async stopTracing() {
  272. (0, _utils.assert)(this._tracingClient, 'Tracing was not started.');
  273. const [event] = await Promise.all([new Promise(f => this._tracingClient.once('Tracing.tracingComplete', f)), this._tracingClient.send('Tracing.end')]);
  274. const tracingPath = _path.default.join(this.options.artifactsDir, (0, _utils.createGuid)() + '.crtrace');
  275. await (0, _crProtocolHelper.saveProtocolStream)(this._tracingClient, event.stream, tracingPath);
  276. this._tracingRecording = false;
  277. const artifact = new _artifact.Artifact(this, tracingPath);
  278. artifact.reportFinished();
  279. return artifact;
  280. }
  281. isConnected() {
  282. return !this._connection._closed;
  283. }
  284. async _clientRootSession() {
  285. if (!this._clientRootSessionPromise) this._clientRootSessionPromise = this._connection.createBrowserSession();
  286. return this._clientRootSessionPromise;
  287. }
  288. }
  289. exports.CRBrowser = CRBrowser;
  290. class CRBrowserContext extends _browserContext.BrowserContext {
  291. constructor(browser, browserContextId, options) {
  292. super(browser, options, browserContextId);
  293. this._authenticateProxyViaCredentials();
  294. }
  295. async _initialize() {
  296. (0, _utils.assert)(!Array.from(this._browser._crPages.values()).some(page => page._browserContext === this));
  297. const promises = [super._initialize()];
  298. if (this._browser.options.name !== 'electron' && this._browser.options.name !== 'clank' && this._options.acceptDownloads !== 'internal-browser-default') {
  299. promises.push(this._browser._session.send('Browser.setDownloadBehavior', {
  300. behavior: this._options.acceptDownloads === 'accept' ? 'allowAndName' : 'deny',
  301. browserContextId: this._browserContextId,
  302. downloadPath: this._browser.options.downloadsPath,
  303. eventsEnabled: true
  304. }));
  305. }
  306. await Promise.all(promises);
  307. }
  308. _crPages() {
  309. return [...this._browser._crPages.values()].filter(crPage => crPage._browserContext === this);
  310. }
  311. pages() {
  312. return this._crPages().map(crPage => crPage._initializedPage).filter(Boolean);
  313. }
  314. async newPageDelegate() {
  315. (0, _browserContext.assertBrowserContextIsNotOwned)(this);
  316. const oldKeys = this._browser.isClank() ? new Set(this._browser._crPages.keys()) : undefined;
  317. let {
  318. targetId
  319. } = await this._browser._session.send('Target.createTarget', {
  320. url: 'about:blank',
  321. browserContextId: this._browserContextId
  322. });
  323. if (oldKeys) {
  324. // Chrome for Android returns tab ids (1, 2, 3, 4, 5) instead of content target ids here, work around it via the
  325. // heuristic assuming that there is only one page created at a time.
  326. const newKeys = new Set(this._browser._crPages.keys());
  327. // Remove old keys.
  328. for (const key of oldKeys) newKeys.delete(key);
  329. // Remove potential concurrent popups.
  330. for (const key of newKeys) {
  331. const page = this._browser._crPages.get(key);
  332. if (page._opener) newKeys.delete(key);
  333. }
  334. (0, _utils.assert)(newKeys.size === 1);
  335. [targetId] = [...newKeys];
  336. }
  337. return this._browser._crPages.get(targetId);
  338. }
  339. async doGetCookies(urls) {
  340. const {
  341. cookies
  342. } = await this._browser._session.send('Storage.getCookies', {
  343. browserContextId: this._browserContextId
  344. });
  345. return network.filterCookies(cookies.map(c => {
  346. const copy = {
  347. sameSite: 'Lax',
  348. ...c
  349. };
  350. delete copy.size;
  351. delete copy.priority;
  352. delete copy.session;
  353. delete copy.sameParty;
  354. delete copy.sourceScheme;
  355. delete copy.sourcePort;
  356. return copy;
  357. }), urls);
  358. }
  359. async addCookies(cookies) {
  360. await this._browser._session.send('Storage.setCookies', {
  361. cookies: network.rewriteCookies(cookies),
  362. browserContextId: this._browserContextId
  363. });
  364. }
  365. async clearCookies() {
  366. await this._browser._session.send('Storage.clearCookies', {
  367. browserContextId: this._browserContextId
  368. });
  369. }
  370. async doGrantPermissions(origin, permissions) {
  371. const webPermissionToProtocol = new Map([['geolocation', 'geolocation'], ['midi', 'midi'], ['notifications', 'notifications'], ['camera', 'videoCapture'], ['microphone', 'audioCapture'], ['background-sync', 'backgroundSync'], ['ambient-light-sensor', 'sensors'], ['accelerometer', 'sensors'], ['gyroscope', 'sensors'], ['magnetometer', 'sensors'], ['accessibility-events', 'accessibilityEvents'], ['clipboard-read', 'clipboardReadWrite'], ['clipboard-write', 'clipboardSanitizedWrite'], ['payment-handler', 'paymentHandler'],
  372. // chrome-specific permissions we have.
  373. ['midi-sysex', 'midiSysex']]);
  374. const filtered = permissions.map(permission => {
  375. const protocolPermission = webPermissionToProtocol.get(permission);
  376. if (!protocolPermission) throw new Error('Unknown permission: ' + permission);
  377. return protocolPermission;
  378. });
  379. await this._browser._session.send('Browser.grantPermissions', {
  380. origin: origin === '*' ? undefined : origin,
  381. browserContextId: this._browserContextId,
  382. permissions: filtered
  383. });
  384. }
  385. async doClearPermissions() {
  386. await this._browser._session.send('Browser.resetPermissions', {
  387. browserContextId: this._browserContextId
  388. });
  389. }
  390. async setGeolocation(geolocation) {
  391. (0, _browserContext.verifyGeolocation)(geolocation);
  392. this._options.geolocation = geolocation;
  393. for (const page of this.pages()) await page._delegate.updateGeolocation();
  394. }
  395. async setExtraHTTPHeaders(headers) {
  396. this._options.extraHTTPHeaders = headers;
  397. for (const page of this.pages()) await page._delegate.updateExtraHTTPHeaders();
  398. for (const sw of this.serviceWorkers()) await sw.updateExtraHTTPHeaders(false);
  399. }
  400. async setUserAgent(userAgent) {
  401. this._options.userAgent = userAgent;
  402. for (const page of this.pages()) await page._delegate.updateUserAgent();
  403. // TODO: service workers don't have Emulation domain?
  404. }
  405. async setOffline(offline) {
  406. this._options.offline = offline;
  407. for (const page of this.pages()) await page._delegate.updateOffline();
  408. for (const sw of this.serviceWorkers()) await sw.updateOffline(false);
  409. }
  410. async doSetHTTPCredentials(httpCredentials) {
  411. this._options.httpCredentials = httpCredentials;
  412. for (const page of this.pages()) await page._delegate.updateHttpCredentials();
  413. for (const sw of this.serviceWorkers()) await sw.updateHttpCredentials(false);
  414. }
  415. async doAddInitScript(source) {
  416. for (const page of this.pages()) await page._delegate.addInitScript(source);
  417. }
  418. async doRemoveInitScripts() {
  419. for (const page of this.pages()) await page._delegate.removeInitScripts();
  420. }
  421. async doExposeBinding(binding) {
  422. for (const page of this.pages()) await page._delegate.exposeBinding(binding);
  423. }
  424. async doRemoveExposedBindings() {
  425. for (const page of this.pages()) await page._delegate.removeExposedBindings();
  426. }
  427. async doUpdateRequestInterception() {
  428. for (const page of this.pages()) await page._delegate.updateRequestInterception();
  429. for (const sw of this.serviceWorkers()) await sw.updateRequestInterception();
  430. }
  431. async doClose(reason) {
  432. // Headful chrome cannot dispose browser context with opened 'beforeunload'
  433. // dialogs, so we should close all that are currently opened.
  434. // We also won't get new ones since `Target.disposeBrowserContext` does not trigger
  435. // beforeunload.
  436. const openedBeforeUnloadDialogs = [];
  437. for (const crPage of this._crPages()) {
  438. const dialogs = [...crPage._page._frameManager._openedDialogs].filter(dialog => dialog.type() === 'beforeunload');
  439. openedBeforeUnloadDialogs.push(...dialogs);
  440. }
  441. await Promise.all(openedBeforeUnloadDialogs.map(dialog => dialog.dismiss()));
  442. if (!this._browserContextId) {
  443. await this.stopVideoRecording();
  444. // Closing persistent context should close the browser.
  445. await this._browser.close({
  446. reason
  447. });
  448. return;
  449. }
  450. await this._browser._session.send('Target.disposeBrowserContext', {
  451. browserContextId: this._browserContextId
  452. });
  453. this._browser._contexts.delete(this._browserContextId);
  454. for (const [targetId, serviceWorker] of this._browser._serviceWorkers) {
  455. if (serviceWorker._browserContext !== this) continue;
  456. // When closing a browser context, service workers are shutdown
  457. // asynchronously and we get detached from them later.
  458. // To avoid the wrong order of notifications, we manually fire
  459. // "close" event here and forget about the service worker.
  460. serviceWorker.didClose();
  461. this._browser._serviceWorkers.delete(targetId);
  462. }
  463. }
  464. async stopVideoRecording() {
  465. await Promise.all(this._crPages().map(crPage => crPage._mainFrameSession._stopVideoRecording()));
  466. }
  467. onClosePersistent() {
  468. // When persistent context is closed, we do not necessary get Target.detachedFromTarget
  469. // for all the background pages.
  470. for (const [targetId, backgroundPage] of this._browser._backgroundPages.entries()) {
  471. if (backgroundPage._browserContext === this && backgroundPage._initializedPage) {
  472. backgroundPage.didClose();
  473. this._browser._backgroundPages.delete(targetId);
  474. }
  475. }
  476. }
  477. async clearCache() {
  478. for (const page of this._crPages()) await page._mainFrameSession._networkManager.clearCache();
  479. }
  480. async cancelDownload(guid) {
  481. // The upstream CDP method is implemented in a way that no explicit error would be given
  482. // regarding the requested `guid`, even if the download is in a state not suitable for
  483. // cancellation (finished, cancelled, etc.) or the guid is invalid at all.
  484. await this._browser._session.send('Browser.cancelDownload', {
  485. guid: guid,
  486. browserContextId: this._browserContextId
  487. });
  488. }
  489. backgroundPages() {
  490. const result = [];
  491. for (const backgroundPage of this._browser._backgroundPages.values()) {
  492. if (backgroundPage._browserContext === this && backgroundPage._initializedPage) result.push(backgroundPage._initializedPage);
  493. }
  494. return result;
  495. }
  496. serviceWorkers() {
  497. return Array.from(this._browser._serviceWorkers.values()).filter(serviceWorker => serviceWorker._browserContext === this);
  498. }
  499. async newCDPSession(page) {
  500. let targetId = null;
  501. if (page instanceof _page.Page) {
  502. targetId = page._delegate._targetId;
  503. } else if (page instanceof _frames.Frame) {
  504. const session = page._page._delegate._sessions.get(page._id);
  505. if (!session) throw new Error(`This frame does not have a separate CDP session, it is a part of the parent frame's session`);
  506. targetId = session._targetId;
  507. } else {
  508. throw new Error('page: expected Page or Frame');
  509. }
  510. const rootSession = await this._browser._clientRootSession();
  511. return rootSession.attachToTarget(targetId);
  512. }
  513. }
  514. exports.CRBrowserContext = CRBrowserContext;
  515. CRBrowserContext.CREvents = {
  516. BackgroundPage: 'backgroundpage',
  517. ServiceWorker: 'serviceworker'
  518. };