browserContext.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547
  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. prepareBrowserContextParams: () => prepareBrowserContextParams,
  33. toClientCertificatesProtocol: () => toClientCertificatesProtocol
  34. });
  35. module.exports = __toCommonJS(browserContext_exports);
  36. var import_artifact = require("./artifact");
  37. var import_cdpSession = require("./cdpSession");
  38. var import_channelOwner = require("./channelOwner");
  39. var import_clientHelper = require("./clientHelper");
  40. var import_clock = require("./clock");
  41. var import_consoleMessage = require("./consoleMessage");
  42. var import_dialog = require("./dialog");
  43. var import_errors = require("./errors");
  44. var import_events = require("./events");
  45. var import_fetch = require("./fetch");
  46. var import_frame = require("./frame");
  47. var import_harRouter = require("./harRouter");
  48. var network = __toESM(require("./network"));
  49. var import_page = require("./page");
  50. var import_tracing = require("./tracing");
  51. var import_waiter = require("./waiter");
  52. var import_webError = require("./webError");
  53. var import_worker = require("./worker");
  54. var import_timeoutSettings = require("./timeoutSettings");
  55. var import_fileUtils = require("./fileUtils");
  56. var import_headers = require("../utils/isomorphic/headers");
  57. var import_urlMatch = require("../utils/isomorphic/urlMatch");
  58. var import_rtti = require("../utils/isomorphic/rtti");
  59. var import_stackTrace = require("../utils/isomorphic/stackTrace");
  60. class BrowserContext extends import_channelOwner.ChannelOwner {
  61. constructor(parent, type, guid, initializer) {
  62. super(parent, type, guid, initializer);
  63. this._pages = /* @__PURE__ */ new Set();
  64. this._routes = [];
  65. this._webSocketRoutes = [];
  66. // Browser is null for browser contexts created outside of normal browser, e.g. android or electron.
  67. this._browser = null;
  68. this._bindings = /* @__PURE__ */ new Map();
  69. this._forReuse = false;
  70. this._serviceWorkers = /* @__PURE__ */ new Set();
  71. this._harRecorders = /* @__PURE__ */ new Map();
  72. this._closingStatus = "none";
  73. this._harRouters = [];
  74. this._options = initializer.options;
  75. this._timeoutSettings = new import_timeoutSettings.TimeoutSettings(this._platform);
  76. this.tracing = import_tracing.Tracing.from(initializer.tracing);
  77. this.request = import_fetch.APIRequestContext.from(initializer.requestContext);
  78. this.request._timeoutSettings = this._timeoutSettings;
  79. this.clock = new import_clock.Clock(this);
  80. this._channel.on("bindingCall", ({ binding }) => this._onBinding(import_page.BindingCall.from(binding)));
  81. this._channel.on("close", () => this._onClose());
  82. this._channel.on("page", ({ page }) => this._onPage(import_page.Page.from(page)));
  83. this._channel.on("route", ({ route }) => this._onRoute(network.Route.from(route)));
  84. this._channel.on("webSocketRoute", ({ webSocketRoute }) => this._onWebSocketRoute(network.WebSocketRoute.from(webSocketRoute)));
  85. this._channel.on("serviceWorker", ({ worker }) => {
  86. const serviceWorker = import_worker.Worker.from(worker);
  87. serviceWorker._context = this;
  88. this._serviceWorkers.add(serviceWorker);
  89. this.emit(import_events.Events.BrowserContext.ServiceWorker, serviceWorker);
  90. });
  91. this._channel.on("console", (event) => {
  92. const worker = import_worker.Worker.fromNullable(event.worker);
  93. const page = import_page.Page.fromNullable(event.page);
  94. const consoleMessage = new import_consoleMessage.ConsoleMessage(this._platform, event, page, worker);
  95. worker?.emit(import_events.Events.Worker.Console, consoleMessage);
  96. page?.emit(import_events.Events.Page.Console, consoleMessage);
  97. if (worker && this._serviceWorkers.has(worker)) {
  98. const scope = this._serviceWorkerScope(worker);
  99. for (const page2 of this._pages) {
  100. if (scope && page2.url().startsWith(scope))
  101. page2.emit(import_events.Events.Page.Console, consoleMessage);
  102. }
  103. }
  104. this.emit(import_events.Events.BrowserContext.Console, consoleMessage);
  105. });
  106. this._channel.on("pageError", ({ error, page }) => {
  107. const pageObject = import_page.Page.from(page);
  108. const parsedError = (0, import_errors.parseError)(error);
  109. this.emit(import_events.Events.BrowserContext.WebError, new import_webError.WebError(pageObject, parsedError));
  110. if (pageObject)
  111. pageObject.emit(import_events.Events.Page.PageError, parsedError);
  112. });
  113. this._channel.on("dialog", ({ dialog }) => {
  114. const dialogObject = import_dialog.Dialog.from(dialog);
  115. let hasListeners = this.emit(import_events.Events.BrowserContext.Dialog, dialogObject);
  116. const page = dialogObject.page();
  117. if (page)
  118. hasListeners = page.emit(import_events.Events.Page.Dialog, dialogObject) || hasListeners;
  119. if (!hasListeners) {
  120. if (dialogObject.type() === "beforeunload")
  121. dialog.accept({}).catch(() => {
  122. });
  123. else
  124. dialog.dismiss().catch(() => {
  125. });
  126. }
  127. });
  128. this._channel.on("request", ({ request, page }) => this._onRequest(network.Request.from(request), import_page.Page.fromNullable(page)));
  129. this._channel.on("requestFailed", ({ request, failureText, responseEndTiming, page }) => this._onRequestFailed(network.Request.from(request), responseEndTiming, failureText, import_page.Page.fromNullable(page)));
  130. this._channel.on("requestFinished", (params) => this._onRequestFinished(params));
  131. this._channel.on("response", ({ response, page }) => this._onResponse(network.Response.from(response), import_page.Page.fromNullable(page)));
  132. this._channel.on("recorderEvent", ({ event, data, page, code }) => {
  133. if (event === "actionAdded")
  134. this._onRecorderEventSink?.actionAdded?.(import_page.Page.from(page), data, code);
  135. else if (event === "actionUpdated")
  136. this._onRecorderEventSink?.actionUpdated?.(import_page.Page.from(page), data, code);
  137. else if (event === "signalAdded")
  138. this._onRecorderEventSink?.signalAdded?.(import_page.Page.from(page), data);
  139. });
  140. this._closedPromise = new Promise((f) => this.once(import_events.Events.BrowserContext.Close, f));
  141. this._setEventToSubscriptionMapping(/* @__PURE__ */ new Map([
  142. [import_events.Events.BrowserContext.Console, "console"],
  143. [import_events.Events.BrowserContext.Dialog, "dialog"],
  144. [import_events.Events.BrowserContext.Request, "request"],
  145. [import_events.Events.BrowserContext.Response, "response"],
  146. [import_events.Events.BrowserContext.RequestFinished, "requestFinished"],
  147. [import_events.Events.BrowserContext.RequestFailed, "requestFailed"]
  148. ]));
  149. }
  150. static from(context) {
  151. return context._object;
  152. }
  153. static fromNullable(context) {
  154. return context ? BrowserContext.from(context) : null;
  155. }
  156. async _initializeHarFromOptions(recordHar) {
  157. if (!recordHar)
  158. return;
  159. const defaultContent = recordHar.path.endsWith(".zip") ? "attach" : "embed";
  160. await this._recordIntoHAR(recordHar.path, null, {
  161. url: recordHar.urlFilter,
  162. updateContent: recordHar.content ?? (recordHar.omitContent ? "omit" : defaultContent),
  163. updateMode: recordHar.mode ?? "full"
  164. });
  165. }
  166. _onPage(page) {
  167. this._pages.add(page);
  168. this.emit(import_events.Events.BrowserContext.Page, page);
  169. if (page._opener && !page._opener.isClosed())
  170. page._opener.emit(import_events.Events.Page.Popup, page);
  171. }
  172. _onRequest(request, page) {
  173. this.emit(import_events.Events.BrowserContext.Request, request);
  174. if (page)
  175. page.emit(import_events.Events.Page.Request, request);
  176. }
  177. _onResponse(response, page) {
  178. this.emit(import_events.Events.BrowserContext.Response, response);
  179. if (page)
  180. page.emit(import_events.Events.Page.Response, response);
  181. }
  182. _onRequestFailed(request, responseEndTiming, failureText, page) {
  183. request._failureText = failureText || null;
  184. request._setResponseEndTiming(responseEndTiming);
  185. this.emit(import_events.Events.BrowserContext.RequestFailed, request);
  186. if (page)
  187. page.emit(import_events.Events.Page.RequestFailed, request);
  188. }
  189. _onRequestFinished(params) {
  190. const { responseEndTiming } = params;
  191. const request = network.Request.from(params.request);
  192. const response = network.Response.fromNullable(params.response);
  193. const page = import_page.Page.fromNullable(params.page);
  194. request._setResponseEndTiming(responseEndTiming);
  195. this.emit(import_events.Events.BrowserContext.RequestFinished, request);
  196. if (page)
  197. page.emit(import_events.Events.Page.RequestFinished, request);
  198. if (response)
  199. response._finishedPromise.resolve(null);
  200. }
  201. async _onRoute(route) {
  202. route._context = this;
  203. const page = route.request()._safePage();
  204. const routeHandlers = this._routes.slice();
  205. for (const routeHandler of routeHandlers) {
  206. if (page?._closeWasCalled || this._closingStatus !== "none")
  207. return;
  208. if (!routeHandler.matches(route.request().url()))
  209. continue;
  210. const index = this._routes.indexOf(routeHandler);
  211. if (index === -1)
  212. continue;
  213. if (routeHandler.willExpire())
  214. this._routes.splice(index, 1);
  215. const handled = await routeHandler.handle(route);
  216. if (!this._routes.length)
  217. this._updateInterceptionPatterns({ internal: true }).catch(() => {
  218. });
  219. if (handled)
  220. return;
  221. }
  222. await route._innerContinue(
  223. true
  224. /* isFallback */
  225. ).catch(() => {
  226. });
  227. }
  228. async _onWebSocketRoute(webSocketRoute) {
  229. const routeHandler = this._webSocketRoutes.find((route) => route.matches(webSocketRoute.url()));
  230. if (routeHandler)
  231. await routeHandler.handle(webSocketRoute);
  232. else
  233. webSocketRoute.connectToServer();
  234. }
  235. async _onBinding(bindingCall) {
  236. const func = this._bindings.get(bindingCall._initializer.name);
  237. if (!func)
  238. return;
  239. await bindingCall.call(func);
  240. }
  241. _serviceWorkerScope(serviceWorker) {
  242. try {
  243. let url = new URL(".", serviceWorker.url()).href;
  244. if (!url.endsWith("/"))
  245. url += "/";
  246. return url;
  247. } catch {
  248. return null;
  249. }
  250. }
  251. setDefaultNavigationTimeout(timeout) {
  252. this._timeoutSettings.setDefaultNavigationTimeout(timeout);
  253. }
  254. setDefaultTimeout(timeout) {
  255. this._timeoutSettings.setDefaultTimeout(timeout);
  256. }
  257. browser() {
  258. return this._browser;
  259. }
  260. pages() {
  261. return [...this._pages];
  262. }
  263. async newPage() {
  264. if (this._ownerPage)
  265. throw new Error("Please use browser.newContext()");
  266. return import_page.Page.from((await this._channel.newPage()).page);
  267. }
  268. async cookies(urls) {
  269. if (!urls)
  270. urls = [];
  271. if (urls && typeof urls === "string")
  272. urls = [urls];
  273. return (await this._channel.cookies({ urls })).cookies;
  274. }
  275. async addCookies(cookies) {
  276. await this._channel.addCookies({ cookies });
  277. }
  278. async clearCookies(options = {}) {
  279. await this._channel.clearCookies({
  280. name: (0, import_rtti.isString)(options.name) ? options.name : void 0,
  281. nameRegexSource: (0, import_rtti.isRegExp)(options.name) ? options.name.source : void 0,
  282. nameRegexFlags: (0, import_rtti.isRegExp)(options.name) ? options.name.flags : void 0,
  283. domain: (0, import_rtti.isString)(options.domain) ? options.domain : void 0,
  284. domainRegexSource: (0, import_rtti.isRegExp)(options.domain) ? options.domain.source : void 0,
  285. domainRegexFlags: (0, import_rtti.isRegExp)(options.domain) ? options.domain.flags : void 0,
  286. path: (0, import_rtti.isString)(options.path) ? options.path : void 0,
  287. pathRegexSource: (0, import_rtti.isRegExp)(options.path) ? options.path.source : void 0,
  288. pathRegexFlags: (0, import_rtti.isRegExp)(options.path) ? options.path.flags : void 0
  289. });
  290. }
  291. async grantPermissions(permissions, options) {
  292. await this._channel.grantPermissions({ permissions, ...options });
  293. }
  294. async clearPermissions() {
  295. await this._channel.clearPermissions();
  296. }
  297. async setGeolocation(geolocation) {
  298. await this._channel.setGeolocation({ geolocation: geolocation || void 0 });
  299. }
  300. async setExtraHTTPHeaders(headers) {
  301. network.validateHeaders(headers);
  302. await this._channel.setExtraHTTPHeaders({ headers: (0, import_headers.headersObjectToArray)(headers) });
  303. }
  304. async setOffline(offline) {
  305. await this._channel.setOffline({ offline });
  306. }
  307. async setHTTPCredentials(httpCredentials) {
  308. await this._channel.setHTTPCredentials({ httpCredentials: httpCredentials || void 0 });
  309. }
  310. async addInitScript(script, arg) {
  311. const source = await (0, import_clientHelper.evaluationScript)(this._platform, script, arg);
  312. await this._channel.addInitScript({ source });
  313. }
  314. async exposeBinding(name, callback, options = {}) {
  315. await this._channel.exposeBinding({ name, needsHandle: options.handle });
  316. this._bindings.set(name, callback);
  317. }
  318. async exposeFunction(name, callback) {
  319. await this._channel.exposeBinding({ name });
  320. const binding = (source, ...args) => callback(...args);
  321. this._bindings.set(name, binding);
  322. }
  323. async route(url, handler, options = {}) {
  324. this._routes.unshift(new network.RouteHandler(this._platform, this._options.baseURL, url, handler, options.times));
  325. await this._updateInterceptionPatterns({ title: "Route requests" });
  326. }
  327. async routeWebSocket(url, handler) {
  328. this._webSocketRoutes.unshift(new network.WebSocketRouteHandler(this._options.baseURL, url, handler));
  329. await this._updateWebSocketInterceptionPatterns({ title: "Route WebSockets" });
  330. }
  331. async _recordIntoHAR(har, page, options = {}) {
  332. const { harId } = await this._channel.harStart({
  333. page: page?._channel,
  334. options: {
  335. zip: har.endsWith(".zip"),
  336. content: options.updateContent ?? "attach",
  337. urlGlob: (0, import_rtti.isString)(options.url) ? options.url : void 0,
  338. urlRegexSource: (0, import_rtti.isRegExp)(options.url) ? options.url.source : void 0,
  339. urlRegexFlags: (0, import_rtti.isRegExp)(options.url) ? options.url.flags : void 0,
  340. mode: options.updateMode ?? "minimal"
  341. }
  342. });
  343. this._harRecorders.set(harId, { path: har, content: options.updateContent ?? "attach" });
  344. }
  345. async routeFromHAR(har, options = {}) {
  346. const localUtils = this._connection.localUtils();
  347. if (!localUtils)
  348. throw new Error("Route from har is not supported in thin clients");
  349. if (options.update) {
  350. await this._recordIntoHAR(har, null, options);
  351. return;
  352. }
  353. const harRouter = await import_harRouter.HarRouter.create(localUtils, har, options.notFound || "abort", { urlMatch: options.url });
  354. this._harRouters.push(harRouter);
  355. await harRouter.addContextRoute(this);
  356. }
  357. _disposeHarRouters() {
  358. this._harRouters.forEach((router) => router.dispose());
  359. this._harRouters = [];
  360. }
  361. async unrouteAll(options) {
  362. await this._unrouteInternal(this._routes, [], options?.behavior);
  363. this._disposeHarRouters();
  364. }
  365. async unroute(url, handler) {
  366. const removed = [];
  367. const remaining = [];
  368. for (const route of this._routes) {
  369. if ((0, import_urlMatch.urlMatchesEqual)(route.url, url) && (!handler || route.handler === handler))
  370. removed.push(route);
  371. else
  372. remaining.push(route);
  373. }
  374. await this._unrouteInternal(removed, remaining, "default");
  375. }
  376. async _unrouteInternal(removed, remaining, behavior) {
  377. this._routes = remaining;
  378. if (behavior && behavior !== "default") {
  379. const promises = removed.map((routeHandler) => routeHandler.stop(behavior));
  380. await Promise.all(promises);
  381. }
  382. await this._updateInterceptionPatterns({ title: "Unroute requests" });
  383. }
  384. async _updateInterceptionPatterns(options) {
  385. const patterns = network.RouteHandler.prepareInterceptionPatterns(this._routes);
  386. await this._wrapApiCall(() => this._channel.setNetworkInterceptionPatterns({ patterns }), options);
  387. }
  388. async _updateWebSocketInterceptionPatterns(options) {
  389. const patterns = network.WebSocketRouteHandler.prepareInterceptionPatterns(this._webSocketRoutes);
  390. await this._wrapApiCall(() => this._channel.setWebSocketInterceptionPatterns({ patterns }), options);
  391. }
  392. _effectiveCloseReason() {
  393. return this._closeReason || this._browser?._closeReason;
  394. }
  395. async waitForEvent(event, optionsOrPredicate = {}) {
  396. return await this._wrapApiCall(async () => {
  397. const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === "function" ? {} : optionsOrPredicate);
  398. const predicate = typeof optionsOrPredicate === "function" ? optionsOrPredicate : optionsOrPredicate.predicate;
  399. const waiter = import_waiter.Waiter.createForEvent(this, event);
  400. waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`);
  401. if (event !== import_events.Events.BrowserContext.Close)
  402. waiter.rejectOnEvent(this, import_events.Events.BrowserContext.Close, () => new import_errors.TargetClosedError(this._effectiveCloseReason()));
  403. const result = await waiter.waitForEvent(this, event, predicate);
  404. waiter.dispose();
  405. return result;
  406. });
  407. }
  408. async storageState(options = {}) {
  409. const state = await this._channel.storageState({ indexedDB: options.indexedDB });
  410. if (options.path) {
  411. await (0, import_fileUtils.mkdirIfNeeded)(this._platform, options.path);
  412. await this._platform.fs().promises.writeFile(options.path, JSON.stringify(state, void 0, 2), "utf8");
  413. }
  414. return state;
  415. }
  416. backgroundPages() {
  417. return [];
  418. }
  419. serviceWorkers() {
  420. return [...this._serviceWorkers];
  421. }
  422. async newCDPSession(page) {
  423. if (!(page instanceof import_page.Page) && !(page instanceof import_frame.Frame))
  424. throw new Error("page: expected Page or Frame");
  425. const result = await this._channel.newCDPSession(page instanceof import_page.Page ? { page: page._channel } : { frame: page._channel });
  426. return import_cdpSession.CDPSession.from(result.session);
  427. }
  428. _onClose() {
  429. this._closingStatus = "closed";
  430. this._browser?._contexts.delete(this);
  431. this._browser?._browserType._contexts.delete(this);
  432. this._browser?._browserType._playwright.selectors._contextsForSelectors.delete(this);
  433. this._disposeHarRouters();
  434. this.tracing._resetStackCounter();
  435. this.emit(import_events.Events.BrowserContext.Close, this);
  436. }
  437. async [Symbol.asyncDispose]() {
  438. await this.close();
  439. }
  440. async close(options = {}) {
  441. if (this._closingStatus !== "none")
  442. return;
  443. this._closeReason = options.reason;
  444. this._closingStatus = "closing";
  445. await this.request.dispose(options);
  446. await this._instrumentation.runBeforeCloseBrowserContext(this);
  447. await this._wrapApiCall(async () => {
  448. for (const [harId, harParams] of this._harRecorders) {
  449. const har = await this._channel.harExport({ harId });
  450. const artifact = import_artifact.Artifact.from(har.artifact);
  451. const isCompressed = harParams.content === "attach" || harParams.path.endsWith(".zip");
  452. const needCompressed = harParams.path.endsWith(".zip");
  453. if (isCompressed && !needCompressed) {
  454. const localUtils = this._connection.localUtils();
  455. if (!localUtils)
  456. throw new Error("Uncompressed har is not supported in thin clients");
  457. await artifact.saveAs(harParams.path + ".tmp");
  458. await localUtils.harUnzip({ zipFile: harParams.path + ".tmp", harFile: harParams.path });
  459. } else {
  460. await artifact.saveAs(harParams.path);
  461. }
  462. await artifact.delete();
  463. }
  464. }, { internal: true });
  465. await this._channel.close(options);
  466. await this._closedPromise;
  467. }
  468. async _enableRecorder(params, eventSink) {
  469. if (eventSink)
  470. this._onRecorderEventSink = eventSink;
  471. await this._channel.enableRecorder(params);
  472. }
  473. async _disableRecorder() {
  474. this._onRecorderEventSink = void 0;
  475. await this._channel.disableRecorder();
  476. }
  477. }
  478. async function prepareStorageState(platform, storageState) {
  479. if (typeof storageState !== "string")
  480. return storageState;
  481. try {
  482. return JSON.parse(await platform.fs().promises.readFile(storageState, "utf8"));
  483. } catch (e) {
  484. (0, import_stackTrace.rewriteErrorMessage)(e, `Error reading storage state from ${storageState}:
  485. ` + e.message);
  486. throw e;
  487. }
  488. }
  489. async function prepareBrowserContextParams(platform, options) {
  490. if (options.videoSize && !options.videosPath)
  491. throw new Error(`"videoSize" option requires "videosPath" to be specified`);
  492. if (options.extraHTTPHeaders)
  493. network.validateHeaders(options.extraHTTPHeaders);
  494. const contextParams = {
  495. ...options,
  496. viewport: options.viewport === null ? void 0 : options.viewport,
  497. noDefaultViewport: options.viewport === null,
  498. extraHTTPHeaders: options.extraHTTPHeaders ? (0, import_headers.headersObjectToArray)(options.extraHTTPHeaders) : void 0,
  499. storageState: options.storageState ? await prepareStorageState(platform, options.storageState) : void 0,
  500. serviceWorkers: options.serviceWorkers,
  501. colorScheme: options.colorScheme === null ? "no-override" : options.colorScheme,
  502. reducedMotion: options.reducedMotion === null ? "no-override" : options.reducedMotion,
  503. forcedColors: options.forcedColors === null ? "no-override" : options.forcedColors,
  504. contrast: options.contrast === null ? "no-override" : options.contrast,
  505. acceptDownloads: toAcceptDownloadsProtocol(options.acceptDownloads),
  506. clientCertificates: await toClientCertificatesProtocol(platform, options.clientCertificates)
  507. };
  508. if (!contextParams.recordVideo && options.videosPath) {
  509. contextParams.recordVideo = {
  510. dir: options.videosPath,
  511. size: options.videoSize
  512. };
  513. }
  514. if (contextParams.recordVideo && contextParams.recordVideo.dir)
  515. contextParams.recordVideo.dir = platform.path().resolve(contextParams.recordVideo.dir);
  516. return contextParams;
  517. }
  518. function toAcceptDownloadsProtocol(acceptDownloads) {
  519. if (acceptDownloads === void 0)
  520. return void 0;
  521. if (acceptDownloads)
  522. return "accept";
  523. return "deny";
  524. }
  525. async function toClientCertificatesProtocol(platform, certs) {
  526. if (!certs)
  527. return void 0;
  528. const bufferizeContent = async (value, path) => {
  529. if (value)
  530. return value;
  531. if (path)
  532. return await platform.fs().promises.readFile(path);
  533. };
  534. return await Promise.all(certs.map(async (cert) => ({
  535. origin: cert.origin,
  536. cert: await bufferizeContent(cert.cert, cert.certPath),
  537. key: await bufferizeContent(cert.key, cert.keyPath),
  538. pfx: await bufferizeContent(cert.pfx, cert.pfxPath),
  539. passphrase: cert.passphrase
  540. })));
  541. }
  542. // Annotate the CommonJS export names for ESM import in node:
  543. 0 && (module.exports = {
  544. BrowserContext,
  545. prepareBrowserContextParams,
  546. toClientCertificatesProtocol
  547. });