browserContext.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702
  1. "use strict";
  2. var __create = Object.create;
  3. var __defProp = Object.defineProperty;
  4. var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
  5. var __getOwnPropNames = Object.getOwnPropertyNames;
  6. var __getProtoOf = Object.getPrototypeOf;
  7. var __hasOwnProp = Object.prototype.hasOwnProperty;
  8. var __export = (target, all) => {
  9. for (var name in all)
  10. __defProp(target, name, { get: all[name], enumerable: true });
  11. };
  12. var __copyProps = (to, from, except, desc) => {
  13. if (from && typeof from === "object" || typeof from === "function") {
  14. for (let key of __getOwnPropNames(from))
  15. if (!__hasOwnProp.call(to, key) && key !== except)
  16. __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  17. }
  18. return to;
  19. };
  20. var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  21. // If the importer is in node compatibility mode or this is not an ESM
  22. // file that has been converted to a CommonJS file using a Babel-
  23. // compatible transform (i.e. "__esModule" has not been set), then set
  24. // "default" to the CommonJS "module.exports" for node compatibility.
  25. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  26. mod
  27. ));
  28. var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
  29. var browserContext_exports = {};
  30. __export(browserContext_exports, {
  31. BrowserContext: () => BrowserContext,
  32. normalizeProxySettings: () => normalizeProxySettings,
  33. validateBrowserContextOptions: () => validateBrowserContextOptions,
  34. verifyClientCertificates: () => verifyClientCertificates,
  35. verifyGeolocation: () => verifyGeolocation
  36. });
  37. module.exports = __toCommonJS(browserContext_exports);
  38. var import_fs = __toESM(require("fs"));
  39. var import_path = __toESM(require("path"));
  40. var import_crypto = require("./utils/crypto");
  41. var import_debug = require("./utils/debug");
  42. var import_clock = require("./clock");
  43. var import_debugger = require("./debugger");
  44. var import_dialog = require("./dialog");
  45. var import_fetch = require("./fetch");
  46. var import_fileUtils = require("./utils/fileUtils");
  47. var import_stackTrace = require("../utils/isomorphic/stackTrace");
  48. var import_harRecorder = require("./har/harRecorder");
  49. var import_helper = require("./helper");
  50. var import_instrumentation = require("./instrumentation");
  51. var network = __toESM(require("./network"));
  52. var import_page = require("./page");
  53. var import_page2 = require("./page");
  54. var import_recorderApp = require("./recorder/recorderApp");
  55. var import_selectors = require("./selectors");
  56. var import_tracing = require("./trace/recorder/tracing");
  57. var rawStorageSource = __toESM(require("../generated/storageScriptSource"));
  58. const BrowserContextEvent = {
  59. Console: "console",
  60. Close: "close",
  61. Page: "page",
  62. // Can't use just 'error' due to node.js special treatment of error events.
  63. // @see https://nodejs.org/api/events.html#events_error_events
  64. PageError: "pageerror",
  65. Request: "request",
  66. Response: "response",
  67. RequestFailed: "requestfailed",
  68. RequestFinished: "requestfinished",
  69. RequestAborted: "requestaborted",
  70. RequestFulfilled: "requestfulfilled",
  71. RequestContinued: "requestcontinued",
  72. BeforeClose: "beforeclose",
  73. VideoStarted: "videostarted",
  74. RecorderEvent: "recorderevent"
  75. };
  76. class BrowserContext extends import_instrumentation.SdkObject {
  77. constructor(browser, options, browserContextId) {
  78. super(browser, "browser-context");
  79. this._pageBindings = /* @__PURE__ */ new Map();
  80. this.requestInterceptors = [];
  81. this._closedStatus = "open";
  82. this._permissions = /* @__PURE__ */ new Map();
  83. this._downloads = /* @__PURE__ */ new Set();
  84. this._origins = /* @__PURE__ */ new Set();
  85. this._harRecorders = /* @__PURE__ */ new Map();
  86. this._tempDirs = [];
  87. this._creatingStorageStatePage = false;
  88. this.initScripts = [];
  89. this._routesInFlight = /* @__PURE__ */ new Set();
  90. this._consoleApiExposed = false;
  91. this.attribution.context = this;
  92. this._browser = browser;
  93. this._options = options;
  94. this._browserContextId = browserContextId;
  95. this._isPersistentContext = !browserContextId;
  96. this._closePromise = new Promise((fulfill) => this._closePromiseFulfill = fulfill);
  97. this._selectors = new import_selectors.Selectors(options.selectorEngines || [], options.testIdAttributeName);
  98. this.fetchRequest = new import_fetch.BrowserContextAPIRequestContext(this);
  99. this.tracing = new import_tracing.Tracing(this, browser.options.tracesDir);
  100. this.clock = new import_clock.Clock(this);
  101. this.dialogManager = new import_dialog.DialogManager(this.instrumentation);
  102. }
  103. static {
  104. this.Events = BrowserContextEvent;
  105. }
  106. isPersistentContext() {
  107. return this._isPersistentContext;
  108. }
  109. selectors() {
  110. return this._selectors;
  111. }
  112. async _initialize() {
  113. if (this.attribution.playwright.options.isInternalPlaywright)
  114. return;
  115. this._debugger = new import_debugger.Debugger(this);
  116. if ((0, import_debug.debugMode)() === "inspector")
  117. await import_recorderApp.RecorderApp.show(this, { pauseOnNextStatement: true });
  118. if (this._debugger.isPaused())
  119. import_recorderApp.RecorderApp.showInspectorNoReply(this);
  120. this._debugger.on(import_debugger.Debugger.Events.PausedStateChanged, () => {
  121. if (this._debugger.isPaused())
  122. import_recorderApp.RecorderApp.showInspectorNoReply(this);
  123. });
  124. if ((0, import_debug.debugMode)() === "console")
  125. await this.exposeConsoleApi();
  126. if (this._options.serviceWorkers === "block")
  127. await this.addInitScript(void 0, `
  128. if (navigator.serviceWorker) navigator.serviceWorker.register = async () => { console.warn('Service Worker registration blocked by Playwright'); };
  129. `);
  130. if (this._options.permissions)
  131. await this.grantPermissions(this._options.permissions);
  132. }
  133. debugger() {
  134. return this._debugger;
  135. }
  136. async exposeConsoleApi() {
  137. if (this._consoleApiExposed)
  138. return;
  139. this._consoleApiExposed = true;
  140. await this.extendInjectedScript(`
  141. function installConsoleApi(injectedScript) { injectedScript.consoleApi.install(); }
  142. module.exports = { default: () => installConsoleApi };
  143. `);
  144. }
  145. async _ensureVideosPath() {
  146. if (this._options.recordVideo)
  147. await (0, import_fileUtils.mkdirIfNeeded)(import_path.default.join(this._options.recordVideo.dir, "dummy"));
  148. }
  149. canResetForReuse() {
  150. if (this._closedStatus !== "open")
  151. return false;
  152. return true;
  153. }
  154. static reusableContextHash(params) {
  155. const paramsCopy = { ...params };
  156. if (paramsCopy.selectorEngines?.length === 0)
  157. delete paramsCopy.selectorEngines;
  158. for (const k of Object.keys(paramsCopy)) {
  159. const key = k;
  160. if (paramsCopy[key] === defaultNewContextParamValues[key])
  161. delete paramsCopy[key];
  162. }
  163. for (const key of paramsThatAllowContextReuse)
  164. delete paramsCopy[key];
  165. return JSON.stringify(paramsCopy);
  166. }
  167. async resetForReuse(progress, params) {
  168. await this.tracing.resetForReuse(progress);
  169. if (params) {
  170. for (const key of paramsThatAllowContextReuse)
  171. this._options[key] = params[key];
  172. if (params.testIdAttributeName)
  173. this.selectors().setTestIdAttributeName(params.testIdAttributeName);
  174. }
  175. let page = this.pages()[0];
  176. const otherPages = this.possiblyUninitializedPages().filter((p) => p !== page);
  177. for (const p of otherPages)
  178. await p.close();
  179. if (page && page.hasCrashed()) {
  180. await page.close();
  181. page = void 0;
  182. }
  183. await page?.mainFrame().gotoImpl(progress, "about:blank", {});
  184. await this.clock.uninstall(progress);
  185. await progress.race(this.setUserAgent(this._options.userAgent));
  186. await progress.race(this.doUpdateDefaultEmulatedMedia());
  187. await progress.race(this.doUpdateDefaultViewport());
  188. await this.setStorageState(progress, this._options.storageState, "resetForReuse");
  189. await page?.resetForReuse(progress);
  190. }
  191. _browserClosed() {
  192. for (const page of this.pages())
  193. page._didClose();
  194. this._didCloseInternal();
  195. }
  196. _didCloseInternal() {
  197. if (this._closedStatus === "closed") {
  198. return;
  199. }
  200. this._clientCertificatesProxy?.close().catch(() => {
  201. });
  202. this.tracing.abort();
  203. if (this._isPersistentContext)
  204. this.onClosePersistent();
  205. this._closePromiseFulfill(new Error("Context closed"));
  206. this.emit(BrowserContext.Events.Close);
  207. }
  208. pages() {
  209. return this.possiblyUninitializedPages().filter((page) => page.initializedOrUndefined());
  210. }
  211. async cookies(urls = []) {
  212. if (urls && !Array.isArray(urls))
  213. urls = [urls];
  214. return await this.doGetCookies(urls);
  215. }
  216. async clearCookies(options) {
  217. const currentCookies = await this.cookies();
  218. await this.doClearCookies();
  219. const matches = (cookie, prop, value) => {
  220. if (!value)
  221. return true;
  222. if (value instanceof RegExp) {
  223. value.lastIndex = 0;
  224. return value.test(cookie[prop]);
  225. }
  226. return cookie[prop] === value;
  227. };
  228. const cookiesToReadd = currentCookies.filter((cookie) => {
  229. return !matches(cookie, "name", options.name) || !matches(cookie, "domain", options.domain) || !matches(cookie, "path", options.path);
  230. });
  231. await this.addCookies(cookiesToReadd);
  232. }
  233. setHTTPCredentials(httpCredentials) {
  234. return this.doSetHTTPCredentials(httpCredentials);
  235. }
  236. getBindingClient(name) {
  237. return this._pageBindings.get(name)?.forClient;
  238. }
  239. async exposePlaywrightBindingIfNeeded() {
  240. this._playwrightBindingExposed ??= (async () => {
  241. await this.doExposePlaywrightBinding();
  242. this.bindingsInitScript = import_page2.PageBinding.createInitScript();
  243. this.initScripts.push(this.bindingsInitScript);
  244. await this.doAddInitScript(this.bindingsInitScript);
  245. await this.safeNonStallingEvaluateInAllFrames(this.bindingsInitScript.source, "main");
  246. })();
  247. return await this._playwrightBindingExposed;
  248. }
  249. needsPlaywrightBinding() {
  250. return this._playwrightBindingExposed !== void 0;
  251. }
  252. async exposeBinding(progress, name, needsHandle, playwrightBinding, forClient) {
  253. if (this._pageBindings.has(name))
  254. throw new Error(`Function "${name}" has been already registered`);
  255. for (const page of this.pages()) {
  256. if (page.getBinding(name))
  257. throw new Error(`Function "${name}" has been already registered in one of the pages`);
  258. }
  259. await progress.race(this.exposePlaywrightBindingIfNeeded());
  260. const binding = new import_page2.PageBinding(name, playwrightBinding, needsHandle);
  261. binding.forClient = forClient;
  262. this._pageBindings.set(name, binding);
  263. try {
  264. await progress.race(this.doAddInitScript(binding.initScript));
  265. await progress.race(this.safeNonStallingEvaluateInAllFrames(binding.initScript.source, "main"));
  266. return binding;
  267. } catch (error) {
  268. this._pageBindings.delete(name);
  269. throw error;
  270. }
  271. }
  272. async removeExposedBindings(bindings) {
  273. bindings = bindings.filter((binding) => this._pageBindings.get(binding.name) === binding);
  274. for (const binding of bindings)
  275. this._pageBindings.delete(binding.name);
  276. await this.doRemoveInitScripts(bindings.map((binding) => binding.initScript));
  277. const cleanup = bindings.map((binding) => `{ ${binding.cleanupScript} };
  278. `).join("");
  279. await this.safeNonStallingEvaluateInAllFrames(cleanup, "main");
  280. }
  281. async grantPermissions(permissions, origin) {
  282. let resolvedOrigin = "*";
  283. if (origin) {
  284. const url = new URL(origin);
  285. resolvedOrigin = url.origin;
  286. }
  287. const existing = new Set(this._permissions.get(resolvedOrigin) || []);
  288. permissions.forEach((p) => existing.add(p));
  289. const list = [...existing.values()];
  290. this._permissions.set(resolvedOrigin, list);
  291. await this.doGrantPermissions(resolvedOrigin, list);
  292. }
  293. async clearPermissions() {
  294. this._permissions.clear();
  295. await this.doClearPermissions();
  296. }
  297. async setExtraHTTPHeaders(progress, headers) {
  298. const oldHeaders = this._options.extraHTTPHeaders;
  299. this._options.extraHTTPHeaders = headers;
  300. try {
  301. await progress.race(this.doUpdateExtraHTTPHeaders());
  302. } catch (error) {
  303. this._options.extraHTTPHeaders = oldHeaders;
  304. this.doUpdateExtraHTTPHeaders().catch(() => {
  305. });
  306. throw error;
  307. }
  308. }
  309. async setOffline(progress, offline) {
  310. const oldOffline = this._options.offline;
  311. this._options.offline = offline;
  312. try {
  313. await progress.race(this.doUpdateOffline());
  314. } catch (error) {
  315. this._options.offline = oldOffline;
  316. this.doUpdateOffline().catch(() => {
  317. });
  318. throw error;
  319. }
  320. }
  321. async _loadDefaultContextAsIs(progress) {
  322. if (!this.possiblyUninitializedPages().length) {
  323. const waitForEvent = import_helper.helper.waitForEvent(progress, this, BrowserContext.Events.Page);
  324. await Promise.race([waitForEvent.promise, this._closePromise]);
  325. }
  326. const page = this.possiblyUninitializedPages()[0];
  327. if (!page)
  328. return;
  329. const pageOrError = await progress.race(page.waitForInitializedOrError());
  330. if (pageOrError instanceof Error)
  331. throw pageOrError;
  332. await page.mainFrame().waitForLoadState(progress, "load");
  333. return page;
  334. }
  335. async _loadDefaultContext(progress) {
  336. const defaultPage = await this._loadDefaultContextAsIs(progress);
  337. if (!defaultPage)
  338. return;
  339. const browserName = this._browser.options.name;
  340. if (this._options.isMobile && browserName === "chromium" || this._options.locale && browserName === "webkit") {
  341. await this.newPage(progress);
  342. await defaultPage.close();
  343. }
  344. }
  345. _authenticateProxyViaHeader() {
  346. const proxy = this._options.proxy || this._browser.options.proxy || { username: void 0, password: void 0 };
  347. const { username, password } = proxy;
  348. if (username) {
  349. this._options.httpCredentials = { username, password };
  350. const token = Buffer.from(`${username}:${password}`).toString("base64");
  351. this._options.extraHTTPHeaders = network.mergeHeaders([
  352. this._options.extraHTTPHeaders,
  353. network.singleHeader("Proxy-Authorization", `Basic ${token}`)
  354. ]);
  355. }
  356. }
  357. _authenticateProxyViaCredentials() {
  358. const proxy = this._options.proxy || this._browser.options.proxy;
  359. if (!proxy)
  360. return;
  361. const { username, password } = proxy;
  362. if (username)
  363. this._options.httpCredentials = { username, password: password || "" };
  364. }
  365. async addInitScript(progress, source) {
  366. const initScript = new import_page.InitScript(source);
  367. this.initScripts.push(initScript);
  368. try {
  369. const promise = this.doAddInitScript(initScript);
  370. if (progress)
  371. await progress.race(promise);
  372. else
  373. await promise;
  374. return initScript;
  375. } catch (error) {
  376. this.removeInitScripts([initScript]).catch(() => {
  377. });
  378. throw error;
  379. }
  380. }
  381. async removeInitScripts(initScripts) {
  382. const set = new Set(initScripts);
  383. this.initScripts = this.initScripts.filter((script) => !set.has(script));
  384. await this.doRemoveInitScripts(initScripts);
  385. }
  386. async addRequestInterceptor(progress, handler) {
  387. this.requestInterceptors.push(handler);
  388. await this.doUpdateRequestInterception();
  389. }
  390. async removeRequestInterceptor(handler) {
  391. const index = this.requestInterceptors.indexOf(handler);
  392. if (index === -1)
  393. return;
  394. this.requestInterceptors.splice(index, 1);
  395. await this.notifyRoutesInFlightAboutRemovedHandler(handler);
  396. await this.doUpdateRequestInterception();
  397. }
  398. isClosingOrClosed() {
  399. return this._closedStatus !== "open";
  400. }
  401. async _deleteAllDownloads() {
  402. await Promise.all(Array.from(this._downloads).map((download) => download.artifact.deleteOnContextClose()));
  403. }
  404. async _deleteAllTempDirs() {
  405. await Promise.all(this._tempDirs.map(async (dir) => await import_fs.default.promises.unlink(dir).catch((e) => {
  406. })));
  407. }
  408. setCustomCloseHandler(handler) {
  409. this._customCloseHandler = handler;
  410. }
  411. async close(options) {
  412. if (this._closedStatus === "open") {
  413. if (options.reason)
  414. this._closeReason = options.reason;
  415. this.emit(BrowserContext.Events.BeforeClose);
  416. this._closedStatus = "closing";
  417. for (const harRecorder of this._harRecorders.values())
  418. await harRecorder.flush();
  419. await this.tracing.flush();
  420. const promises = [];
  421. for (const { context, artifact } of this._browser._idToVideo.values()) {
  422. if (context === this)
  423. promises.push(artifact.finishedPromise());
  424. }
  425. if (this._customCloseHandler) {
  426. await this._customCloseHandler();
  427. } else {
  428. await this.doClose(options.reason);
  429. }
  430. promises.push(this._deleteAllDownloads());
  431. promises.push(this._deleteAllTempDirs());
  432. await Promise.all(promises);
  433. if (!this._customCloseHandler)
  434. this._didCloseInternal();
  435. }
  436. await this._closePromise;
  437. }
  438. async newPage(progress, forStorageState) {
  439. let page;
  440. try {
  441. this._creatingStorageStatePage = !!forStorageState;
  442. page = await progress.race(this.doCreateNewPage());
  443. const pageOrError = await progress.race(page.waitForInitializedOrError());
  444. if (pageOrError instanceof import_page2.Page) {
  445. if (pageOrError.isClosed())
  446. throw new Error("Page has been closed.");
  447. return pageOrError;
  448. }
  449. throw pageOrError;
  450. } catch (error) {
  451. await page?.close({ reason: "Failed to create page" }).catch(() => {
  452. });
  453. throw error;
  454. } finally {
  455. this._creatingStorageStatePage = false;
  456. }
  457. }
  458. addVisitedOrigin(origin) {
  459. this._origins.add(origin);
  460. }
  461. async storageState(progress, indexedDB = false) {
  462. const result = {
  463. cookies: await this.cookies(),
  464. origins: []
  465. };
  466. const originsToSave = new Set(this._origins);
  467. const collectScript = `(() => {
  468. const module = {};
  469. ${rawStorageSource.source}
  470. const script = new (module.exports.StorageScript())(${this._browser.options.name === "firefox"});
  471. return script.collect(${indexedDB});
  472. })()`;
  473. for (const page of this.pages()) {
  474. const origin = page.mainFrame().origin();
  475. if (!origin || !originsToSave.has(origin))
  476. continue;
  477. try {
  478. const storage = await page.mainFrame().nonStallingEvaluateInExistingContext(collectScript, "utility");
  479. if (storage.localStorage.length || storage.indexedDB?.length)
  480. result.origins.push({ origin, localStorage: storage.localStorage, indexedDB: storage.indexedDB });
  481. originsToSave.delete(origin);
  482. } catch {
  483. }
  484. }
  485. if (originsToSave.size) {
  486. const page = await this.newPage(
  487. progress,
  488. true
  489. /* forStorageState */
  490. );
  491. try {
  492. await page.addRequestInterceptor(progress, (route) => {
  493. route.fulfill({ body: "<html></html>" }).catch(() => {
  494. });
  495. }, "prepend");
  496. for (const origin of originsToSave) {
  497. const frame = page.mainFrame();
  498. await frame.gotoImpl(progress, origin, {});
  499. const storage = await progress.race(frame.evaluateExpression(collectScript, { world: "utility" }));
  500. if (storage.localStorage.length || storage.indexedDB?.length)
  501. result.origins.push({ origin, localStorage: storage.localStorage, indexedDB: storage.indexedDB });
  502. }
  503. } finally {
  504. await page.close();
  505. }
  506. }
  507. return result;
  508. }
  509. isCreatingStorageStatePage() {
  510. return this._creatingStorageStatePage;
  511. }
  512. async setStorageState(progress, state, mode) {
  513. let page;
  514. let interceptor;
  515. try {
  516. if (mode !== "initial") {
  517. await progress.race(this.clearCache());
  518. await progress.race(this.doClearCookies());
  519. }
  520. if (state?.cookies)
  521. await progress.race(this.addCookies(state.cookies));
  522. const newOrigins = new Map(state?.origins?.map((p) => [p.origin, p]) || []);
  523. const allOrigins = /* @__PURE__ */ new Set([...this._origins, ...newOrigins.keys()]);
  524. if (allOrigins.size) {
  525. if (mode === "resetForReuse")
  526. page = this.pages()[0];
  527. if (!page)
  528. page = await this.newPage(
  529. progress,
  530. mode !== "resetForReuse"
  531. /* forStorageState */
  532. );
  533. interceptor = (route) => {
  534. route.fulfill({ body: "<html></html>" }).catch(() => {
  535. });
  536. };
  537. await page.addRequestInterceptor(progress, interceptor, "prepend");
  538. for (const origin of allOrigins) {
  539. const frame = page.mainFrame();
  540. await frame.gotoImpl(progress, origin, {});
  541. const restoreScript = `(() => {
  542. const module = {};
  543. ${rawStorageSource.source}
  544. const script = new (module.exports.StorageScript())(${this._browser.options.name === "firefox"});
  545. return script.restore(${JSON.stringify(newOrigins.get(origin))});
  546. })()`;
  547. await progress.race(frame.evaluateExpression(restoreScript, { world: "utility" }));
  548. }
  549. }
  550. this._origins = /* @__PURE__ */ new Set([...newOrigins.keys()]);
  551. } catch (error) {
  552. (0, import_stackTrace.rewriteErrorMessage)(error, `Error setting storage state:
  553. ` + error.message);
  554. throw error;
  555. } finally {
  556. if (mode !== "resetForReuse")
  557. await page?.close();
  558. else if (interceptor)
  559. await page?.removeRequestInterceptor(interceptor);
  560. }
  561. }
  562. async extendInjectedScript(source, arg) {
  563. const installInFrame = (frame) => frame.extendInjectedScript(source, arg).catch(() => {
  564. });
  565. const installInPage = (page) => {
  566. page.on(import_page2.Page.Events.InternalFrameNavigatedToNewDocument, installInFrame);
  567. return Promise.all(page.frames().map(installInFrame));
  568. };
  569. this.on(BrowserContext.Events.Page, installInPage);
  570. return Promise.all(this.pages().map(installInPage));
  571. }
  572. async safeNonStallingEvaluateInAllFrames(expression, world, options = {}) {
  573. await Promise.all(this.pages().map((page) => page.safeNonStallingEvaluateInAllFrames(expression, world, options)));
  574. }
  575. harStart(page, options) {
  576. const harId = (0, import_crypto.createGuid)();
  577. this._harRecorders.set(harId, new import_harRecorder.HarRecorder(this, page, options));
  578. return harId;
  579. }
  580. async harExport(harId) {
  581. const recorder = this._harRecorders.get(harId || "");
  582. return recorder.export();
  583. }
  584. addRouteInFlight(route) {
  585. this._routesInFlight.add(route);
  586. }
  587. removeRouteInFlight(route) {
  588. this._routesInFlight.delete(route);
  589. }
  590. async notifyRoutesInFlightAboutRemovedHandler(handler) {
  591. await Promise.all([...this._routesInFlight].map((route) => route.removeHandler(handler)));
  592. }
  593. }
  594. function validateBrowserContextOptions(options, browserOptions) {
  595. if (options.noDefaultViewport && options.deviceScaleFactor !== void 0)
  596. throw new Error(`"deviceScaleFactor" option is not supported with null "viewport"`);
  597. if (options.noDefaultViewport && !!options.isMobile)
  598. throw new Error(`"isMobile" option is not supported with null "viewport"`);
  599. if (options.acceptDownloads === void 0 && browserOptions.name !== "electron")
  600. options.acceptDownloads = "accept";
  601. else if (options.acceptDownloads === void 0 && browserOptions.name === "electron")
  602. options.acceptDownloads = "internal-browser-default";
  603. if (!options.viewport && !options.noDefaultViewport)
  604. options.viewport = { width: 1280, height: 720 };
  605. if (options.recordVideo) {
  606. if (!options.recordVideo.size) {
  607. if (options.noDefaultViewport) {
  608. options.recordVideo.size = { width: 800, height: 600 };
  609. } else {
  610. const size = options.viewport;
  611. const scale = Math.min(1, 800 / Math.max(size.width, size.height));
  612. options.recordVideo.size = {
  613. width: Math.floor(size.width * scale),
  614. height: Math.floor(size.height * scale)
  615. };
  616. }
  617. }
  618. options.recordVideo.size.width &= ~1;
  619. options.recordVideo.size.height &= ~1;
  620. }
  621. if (options.proxy)
  622. options.proxy = normalizeProxySettings(options.proxy);
  623. verifyGeolocation(options.geolocation);
  624. }
  625. function verifyGeolocation(geolocation) {
  626. if (!geolocation)
  627. return;
  628. geolocation.accuracy = geolocation.accuracy || 0;
  629. const { longitude, latitude, accuracy } = geolocation;
  630. if (longitude < -180 || longitude > 180)
  631. throw new Error(`geolocation.longitude: precondition -180 <= LONGITUDE <= 180 failed.`);
  632. if (latitude < -90 || latitude > 90)
  633. throw new Error(`geolocation.latitude: precondition -90 <= LATITUDE <= 90 failed.`);
  634. if (accuracy < 0)
  635. throw new Error(`geolocation.accuracy: precondition 0 <= ACCURACY failed.`);
  636. }
  637. function verifyClientCertificates(clientCertificates) {
  638. if (!clientCertificates)
  639. return;
  640. for (const cert of clientCertificates) {
  641. if (!cert.origin)
  642. throw new Error(`clientCertificates.origin is required`);
  643. if (!cert.cert && !cert.key && !cert.passphrase && !cert.pfx)
  644. throw new Error("None of cert, key, passphrase or pfx is specified");
  645. if (cert.cert && !cert.key)
  646. throw new Error("cert is specified without key");
  647. if (!cert.cert && cert.key)
  648. throw new Error("key is specified without cert");
  649. if (cert.pfx && (cert.cert || cert.key))
  650. throw new Error("pfx is specified together with cert, key or passphrase");
  651. }
  652. }
  653. function normalizeProxySettings(proxy) {
  654. let { server, bypass } = proxy;
  655. let url;
  656. try {
  657. url = new URL(server);
  658. if (!url.host || !url.protocol)
  659. url = new URL("http://" + server);
  660. } catch (e) {
  661. url = new URL("http://" + server);
  662. }
  663. if (url.protocol === "socks4:" && (proxy.username || proxy.password))
  664. throw new Error(`Socks4 proxy protocol does not support authentication`);
  665. if (url.protocol === "socks5:" && (proxy.username || proxy.password))
  666. throw new Error(`Browser does not support socks5 proxy authentication`);
  667. server = url.protocol + "//" + url.host;
  668. if (bypass)
  669. bypass = bypass.split(",").map((t) => t.trim()).join(",");
  670. return { ...proxy, server, bypass };
  671. }
  672. const paramsThatAllowContextReuse = [
  673. "colorScheme",
  674. "forcedColors",
  675. "reducedMotion",
  676. "contrast",
  677. "screen",
  678. "userAgent",
  679. "viewport",
  680. "testIdAttributeName"
  681. ];
  682. const defaultNewContextParamValues = {
  683. noDefaultViewport: false,
  684. ignoreHTTPSErrors: false,
  685. javaScriptEnabled: true,
  686. bypassCSP: false,
  687. offline: false,
  688. isMobile: false,
  689. hasTouch: false,
  690. acceptDownloads: "accept",
  691. strictSelectors: false,
  692. serviceWorkers: "allow",
  693. locale: "en-US"
  694. };
  695. // Annotate the CommonJS export names for ESM import in node:
  696. 0 && (module.exports = {
  697. BrowserContext,
  698. normalizeProxySettings,
  699. validateBrowserContextOptions,
  700. verifyClientCertificates,
  701. verifyGeolocation
  702. });