browserContext.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.BrowserContext = void 0;
  6. exports.prepareBrowserContextParams = prepareBrowserContextParams;
  7. var _page = require("./page");
  8. var _frame = require("./frame");
  9. var network = _interopRequireWildcard(require("./network"));
  10. var _fs = _interopRequireDefault(require("fs"));
  11. var _path = _interopRequireDefault(require("path"));
  12. var _channelOwner = require("./channelOwner");
  13. var _clientHelper = require("./clientHelper");
  14. var _browser = require("./browser");
  15. var _worker = require("./worker");
  16. var _events = require("./events");
  17. var _timeoutSettings = require("../common/timeoutSettings");
  18. var _waiter = require("./waiter");
  19. var _utils = require("../utils");
  20. var _fileUtils = require("../utils/fileUtils");
  21. var _cdpSession = require("./cdpSession");
  22. var _tracing = require("./tracing");
  23. var _artifact = require("./artifact");
  24. var _fetch = require("./fetch");
  25. var _stackTrace = require("../utils/stackTrace");
  26. var _harRouter = require("./harRouter");
  27. var _consoleMessage = require("./consoleMessage");
  28. var _dialog = require("./dialog");
  29. var _webError = require("./webError");
  30. var _errors = require("./errors");
  31. let _Symbol$asyncDispose;
  32. /**
  33. * Copyright 2017 Google Inc. All rights reserved.
  34. * Modifications copyright (c) Microsoft Corporation.
  35. *
  36. * Licensed under the Apache License, Version 2.0 (the "License");
  37. * you may not use this file except in compliance with the License.
  38. * You may obtain a copy of the License at
  39. *
  40. * http://www.apache.org/licenses/LICENSE-2.0
  41. *
  42. * Unless required by applicable law or agreed to in writing, software
  43. * distributed under the License is distributed on an "AS IS" BASIS,
  44. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  45. * See the License for the specific language governing permissions and
  46. * limitations under the License.
  47. */
  48. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  49. 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); }
  50. 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; }
  51. _Symbol$asyncDispose = Symbol.asyncDispose;
  52. class BrowserContext extends _channelOwner.ChannelOwner {
  53. static from(context) {
  54. return context._object;
  55. }
  56. static fromNullable(context) {
  57. return context ? BrowserContext.from(context) : null;
  58. }
  59. constructor(parent, type, guid, initializer) {
  60. var _this$_browser, _this$_browser2;
  61. super(parent, type, guid, initializer);
  62. this._pages = new Set();
  63. this._routes = [];
  64. this._browser = null;
  65. this._browserType = void 0;
  66. this._bindings = new Map();
  67. this._timeoutSettings = new _timeoutSettings.TimeoutSettings();
  68. this._ownerPage = void 0;
  69. this._closedPromise = void 0;
  70. this._options = {};
  71. this.request = void 0;
  72. this.tracing = void 0;
  73. this._backgroundPages = new Set();
  74. this._serviceWorkers = new Set();
  75. this._isChromium = void 0;
  76. this._harRecorders = new Map();
  77. this._closeWasCalled = false;
  78. this._closeReason = void 0;
  79. this._harRouters = [];
  80. if (parent instanceof _browser.Browser) this._browser = parent;
  81. (_this$_browser = this._browser) === null || _this$_browser === void 0 ? void 0 : _this$_browser._contexts.add(this);
  82. this._isChromium = ((_this$_browser2 = this._browser) === null || _this$_browser2 === void 0 ? void 0 : _this$_browser2._name) === 'chromium';
  83. this.tracing = _tracing.Tracing.from(initializer.tracing);
  84. this.request = _fetch.APIRequestContext.from(initializer.requestContext);
  85. this._channel.on('bindingCall', ({
  86. binding
  87. }) => this._onBinding(_page.BindingCall.from(binding)));
  88. this._channel.on('close', () => this._onClose());
  89. this._channel.on('page', ({
  90. page
  91. }) => this._onPage(_page.Page.from(page)));
  92. this._channel.on('route', ({
  93. route
  94. }) => this._onRoute(network.Route.from(route)));
  95. this._channel.on('backgroundPage', ({
  96. page
  97. }) => {
  98. const backgroundPage = _page.Page.from(page);
  99. this._backgroundPages.add(backgroundPage);
  100. this.emit(_events.Events.BrowserContext.BackgroundPage, backgroundPage);
  101. });
  102. this._channel.on('serviceWorker', ({
  103. worker
  104. }) => {
  105. const serviceWorker = _worker.Worker.from(worker);
  106. serviceWorker._context = this;
  107. this._serviceWorkers.add(serviceWorker);
  108. this.emit(_events.Events.BrowserContext.ServiceWorker, serviceWorker);
  109. });
  110. this._channel.on('console', event => {
  111. const consoleMessage = new _consoleMessage.ConsoleMessage(event);
  112. this.emit(_events.Events.BrowserContext.Console, consoleMessage);
  113. const page = consoleMessage.page();
  114. if (page) page.emit(_events.Events.Page.Console, consoleMessage);
  115. });
  116. this._channel.on('pageError', ({
  117. error,
  118. page
  119. }) => {
  120. const pageObject = _page.Page.from(page);
  121. const parsedError = (0, _errors.parseError)(error);
  122. this.emit(_events.Events.BrowserContext.WebError, new _webError.WebError(pageObject, parsedError));
  123. if (pageObject) pageObject.emit(_events.Events.Page.PageError, parsedError);
  124. });
  125. this._channel.on('dialog', ({
  126. dialog
  127. }) => {
  128. const dialogObject = _dialog.Dialog.from(dialog);
  129. let hasListeners = this.emit(_events.Events.BrowserContext.Dialog, dialogObject);
  130. const page = dialogObject.page();
  131. if (page) hasListeners = page.emit(_events.Events.Page.Dialog, dialogObject) || hasListeners;
  132. if (!hasListeners) {
  133. // Although we do similar handling on the server side, we still need this logic
  134. // on the client side due to a possible race condition between two async calls:
  135. // a) removing "dialog" listener subscription (client->server)
  136. // b) actual "dialog" event (server->client)
  137. if (dialogObject.type() === 'beforeunload') dialog.accept({}).catch(() => {});else dialog.dismiss().catch(() => {});
  138. }
  139. });
  140. this._channel.on('request', ({
  141. request,
  142. page
  143. }) => this._onRequest(network.Request.from(request), _page.Page.fromNullable(page)));
  144. this._channel.on('requestFailed', ({
  145. request,
  146. failureText,
  147. responseEndTiming,
  148. page
  149. }) => this._onRequestFailed(network.Request.from(request), responseEndTiming, failureText, _page.Page.fromNullable(page)));
  150. this._channel.on('requestFinished', params => this._onRequestFinished(params));
  151. this._channel.on('response', ({
  152. response,
  153. page
  154. }) => this._onResponse(network.Response.from(response), _page.Page.fromNullable(page)));
  155. this._closedPromise = new Promise(f => this.once(_events.Events.BrowserContext.Close, f));
  156. this._setEventToSubscriptionMapping(new Map([[_events.Events.BrowserContext.Console, 'console'], [_events.Events.BrowserContext.Dialog, 'dialog'], [_events.Events.BrowserContext.Request, 'request'], [_events.Events.BrowserContext.Response, 'response'], [_events.Events.BrowserContext.RequestFinished, 'requestFinished'], [_events.Events.BrowserContext.RequestFailed, 'requestFailed']]));
  157. }
  158. _setOptions(contextOptions, browserOptions) {
  159. this._options = contextOptions;
  160. if (this._options.recordHar) this._harRecorders.set('', {
  161. path: this._options.recordHar.path,
  162. content: this._options.recordHar.content
  163. });
  164. this.tracing._tracesDir = browserOptions.tracesDir;
  165. }
  166. _onPage(page) {
  167. this._pages.add(page);
  168. this.emit(_events.Events.BrowserContext.Page, page);
  169. if (page._opener && !page._opener.isClosed()) page._opener.emit(_events.Events.Page.Popup, page);
  170. }
  171. _onRequest(request, page) {
  172. this.emit(_events.Events.BrowserContext.Request, request);
  173. if (page) page.emit(_events.Events.Page.Request, request);
  174. }
  175. _onResponse(response, page) {
  176. this.emit(_events.Events.BrowserContext.Response, response);
  177. if (page) page.emit(_events.Events.Page.Response, response);
  178. }
  179. _onRequestFailed(request, responseEndTiming, failureText, page) {
  180. request._failureText = failureText || null;
  181. request._setResponseEndTiming(responseEndTiming);
  182. this.emit(_events.Events.BrowserContext.RequestFailed, request);
  183. if (page) page.emit(_events.Events.Page.RequestFailed, request);
  184. }
  185. _onRequestFinished(params) {
  186. const {
  187. responseEndTiming
  188. } = params;
  189. const request = network.Request.from(params.request);
  190. const response = network.Response.fromNullable(params.response);
  191. const page = _page.Page.fromNullable(params.page);
  192. request._setResponseEndTiming(responseEndTiming);
  193. this.emit(_events.Events.BrowserContext.RequestFinished, request);
  194. if (page) page.emit(_events.Events.Page.RequestFinished, request);
  195. if (response) response._finishedPromise.resolve(null);
  196. }
  197. async _onRoute(route) {
  198. route._context = this;
  199. const page = route.request()._safePage();
  200. const routeHandlers = this._routes.slice();
  201. for (const routeHandler of routeHandlers) {
  202. // If the page or the context was closed we stall all requests right away.
  203. if (page !== null && page !== void 0 && page._closeWasCalled || this._closeWasCalled) return;
  204. if (!routeHandler.matches(route.request().url())) continue;
  205. const index = this._routes.indexOf(routeHandler);
  206. if (index === -1) continue;
  207. if (routeHandler.willExpire()) this._routes.splice(index, 1);
  208. const handled = await routeHandler.handle(route);
  209. if (!this._routes.length) this._wrapApiCall(() => this._updateInterceptionPatterns(), true).catch(() => {});
  210. if (handled) return;
  211. }
  212. // If the page is closed or unrouteAll() was called without waiting and interception disabled,
  213. // the method will throw an error - silence it.
  214. await route._innerContinue(true).catch(() => {});
  215. }
  216. async _onBinding(bindingCall) {
  217. const func = this._bindings.get(bindingCall._initializer.name);
  218. if (!func) return;
  219. await bindingCall.call(func);
  220. }
  221. setDefaultNavigationTimeout(timeout) {
  222. this._timeoutSettings.setDefaultNavigationTimeout(timeout);
  223. this._wrapApiCall(async () => {
  224. this._channel.setDefaultNavigationTimeoutNoReply({
  225. timeout
  226. }).catch(() => {});
  227. }, true);
  228. }
  229. setDefaultTimeout(timeout) {
  230. this._timeoutSettings.setDefaultTimeout(timeout);
  231. this._wrapApiCall(async () => {
  232. this._channel.setDefaultTimeoutNoReply({
  233. timeout
  234. }).catch(() => {});
  235. }, true);
  236. }
  237. browser() {
  238. return this._browser;
  239. }
  240. pages() {
  241. return [...this._pages];
  242. }
  243. async newPage() {
  244. if (this._ownerPage) throw new Error('Please use browser.newContext()');
  245. return _page.Page.from((await this._channel.newPage()).page);
  246. }
  247. async cookies(urls) {
  248. if (!urls) urls = [];
  249. if (urls && typeof urls === 'string') urls = [urls];
  250. return (await this._channel.cookies({
  251. urls: urls
  252. })).cookies;
  253. }
  254. async addCookies(cookies) {
  255. await this._channel.addCookies({
  256. cookies
  257. });
  258. }
  259. async clearCookies() {
  260. await this._channel.clearCookies();
  261. }
  262. async grantPermissions(permissions, options) {
  263. await this._channel.grantPermissions({
  264. permissions,
  265. ...options
  266. });
  267. }
  268. async clearPermissions() {
  269. await this._channel.clearPermissions();
  270. }
  271. async setGeolocation(geolocation) {
  272. await this._channel.setGeolocation({
  273. geolocation: geolocation || undefined
  274. });
  275. }
  276. async setExtraHTTPHeaders(headers) {
  277. network.validateHeaders(headers);
  278. await this._channel.setExtraHTTPHeaders({
  279. headers: (0, _utils.headersObjectToArray)(headers)
  280. });
  281. }
  282. async setOffline(offline) {
  283. await this._channel.setOffline({
  284. offline
  285. });
  286. }
  287. async setHTTPCredentials(httpCredentials) {
  288. await this._channel.setHTTPCredentials({
  289. httpCredentials: httpCredentials || undefined
  290. });
  291. }
  292. async addInitScript(script, arg) {
  293. const source = await (0, _clientHelper.evaluationScript)(script, arg);
  294. await this._channel.addInitScript({
  295. source
  296. });
  297. }
  298. async exposeBinding(name, callback, options = {}) {
  299. await this._channel.exposeBinding({
  300. name,
  301. needsHandle: options.handle
  302. });
  303. this._bindings.set(name, callback);
  304. }
  305. async exposeFunction(name, callback) {
  306. await this._channel.exposeBinding({
  307. name
  308. });
  309. const binding = (source, ...args) => callback(...args);
  310. this._bindings.set(name, binding);
  311. }
  312. async route(url, handler, options = {}) {
  313. this._routes.unshift(new network.RouteHandler(this._options.baseURL, url, handler, options.times));
  314. await this._updateInterceptionPatterns();
  315. }
  316. async _recordIntoHAR(har, page, options = {}) {
  317. var _options$updateConten, _options$updateMode, _options$updateConten2;
  318. const {
  319. harId
  320. } = await this._channel.harStart({
  321. page: page === null || page === void 0 ? void 0 : page._channel,
  322. options: prepareRecordHarOptions({
  323. path: har,
  324. content: (_options$updateConten = options.updateContent) !== null && _options$updateConten !== void 0 ? _options$updateConten : 'attach',
  325. mode: (_options$updateMode = options.updateMode) !== null && _options$updateMode !== void 0 ? _options$updateMode : 'minimal',
  326. urlFilter: options.url
  327. })
  328. });
  329. this._harRecorders.set(harId, {
  330. path: har,
  331. content: (_options$updateConten2 = options.updateContent) !== null && _options$updateConten2 !== void 0 ? _options$updateConten2 : 'attach'
  332. });
  333. }
  334. async routeFromHAR(har, options = {}) {
  335. if (options.update) {
  336. await this._recordIntoHAR(har, null, options);
  337. return;
  338. }
  339. const harRouter = await _harRouter.HarRouter.create(this._connection.localUtils(), har, options.notFound || 'abort', {
  340. urlMatch: options.url
  341. });
  342. this._harRouters.push(harRouter);
  343. await harRouter.addContextRoute(this);
  344. }
  345. _disposeHarRouters() {
  346. this._harRouters.forEach(router => router.dispose());
  347. this._harRouters = [];
  348. }
  349. async unrouteAll(options) {
  350. await this._unrouteInternal(this._routes, [], options === null || options === void 0 ? void 0 : options.behavior);
  351. this._disposeHarRouters();
  352. }
  353. async unroute(url, handler) {
  354. const removed = [];
  355. const remaining = [];
  356. for (const route of this._routes) {
  357. if ((0, _utils.urlMatchesEqual)(route.url, url) && (!handler || route.handler === handler)) removed.push(route);else remaining.push(route);
  358. }
  359. await this._unrouteInternal(removed, remaining, 'default');
  360. }
  361. async _unrouteInternal(removed, remaining, behavior) {
  362. this._routes = remaining;
  363. await this._updateInterceptionPatterns();
  364. if (!behavior || behavior === 'default') return;
  365. const promises = removed.map(routeHandler => routeHandler.stop(behavior));
  366. await Promise.all(promises);
  367. }
  368. async _updateInterceptionPatterns() {
  369. const patterns = network.RouteHandler.prepareInterceptionPatterns(this._routes);
  370. await this._channel.setNetworkInterceptionPatterns({
  371. patterns
  372. });
  373. }
  374. _effectiveCloseReason() {
  375. var _this$_browser3;
  376. return this._closeReason || ((_this$_browser3 = this._browser) === null || _this$_browser3 === void 0 ? void 0 : _this$_browser3._closeReason);
  377. }
  378. async waitForEvent(event, optionsOrPredicate = {}) {
  379. return await this._wrapApiCall(async () => {
  380. const timeout = this._timeoutSettings.timeout(typeof optionsOrPredicate === 'function' ? {} : optionsOrPredicate);
  381. const predicate = typeof optionsOrPredicate === 'function' ? optionsOrPredicate : optionsOrPredicate.predicate;
  382. const waiter = _waiter.Waiter.createForEvent(this, event);
  383. waiter.rejectOnTimeout(timeout, `Timeout ${timeout}ms exceeded while waiting for event "${event}"`);
  384. if (event !== _events.Events.BrowserContext.Close) waiter.rejectOnEvent(this, _events.Events.BrowserContext.Close, () => new _errors.TargetClosedError(this._effectiveCloseReason()));
  385. const result = await waiter.waitForEvent(this, event, predicate);
  386. waiter.dispose();
  387. return result;
  388. });
  389. }
  390. async storageState(options = {}) {
  391. const state = await this._channel.storageState();
  392. if (options.path) {
  393. await (0, _fileUtils.mkdirIfNeeded)(options.path);
  394. await _fs.default.promises.writeFile(options.path, JSON.stringify(state, undefined, 2), 'utf8');
  395. }
  396. return state;
  397. }
  398. backgroundPages() {
  399. return [...this._backgroundPages];
  400. }
  401. serviceWorkers() {
  402. return [...this._serviceWorkers];
  403. }
  404. async newCDPSession(page) {
  405. // channelOwner.ts's validation messages don't handle the pseudo-union type, so we're explicit here
  406. if (!(page instanceof _page.Page) && !(page instanceof _frame.Frame)) throw new Error('page: expected Page or Frame');
  407. const result = await this._channel.newCDPSession(page instanceof _page.Page ? {
  408. page: page._channel
  409. } : {
  410. frame: page._channel
  411. });
  412. return _cdpSession.CDPSession.from(result.session);
  413. }
  414. _onClose() {
  415. var _this$_browserType, _this$_browserType$_c;
  416. if (this._browser) this._browser._contexts.delete(this);
  417. (_this$_browserType = this._browserType) === null || _this$_browserType === void 0 ? void 0 : (_this$_browserType$_c = _this$_browserType._contexts) === null || _this$_browserType$_c === void 0 ? void 0 : _this$_browserType$_c.delete(this);
  418. this._disposeHarRouters();
  419. this.emit(_events.Events.BrowserContext.Close, this);
  420. }
  421. async [_Symbol$asyncDispose]() {
  422. await this.close();
  423. }
  424. async close(options = {}) {
  425. if (this._closeWasCalled) return;
  426. this._closeReason = options.reason;
  427. this._closeWasCalled = true;
  428. await this._wrapApiCall(async () => {
  429. var _this$_browserType2;
  430. await ((_this$_browserType2 = this._browserType) === null || _this$_browserType2 === void 0 ? void 0 : _this$_browserType2._willCloseContext(this));
  431. for (const [harId, harParams] of this._harRecorders) {
  432. const har = await this._channel.harExport({
  433. harId
  434. });
  435. const artifact = _artifact.Artifact.from(har.artifact);
  436. // Server side will compress artifact if content is attach or if file is .zip.
  437. const isCompressed = harParams.content === 'attach' || harParams.path.endsWith('.zip');
  438. const needCompressed = harParams.path.endsWith('.zip');
  439. if (isCompressed && !needCompressed) {
  440. await artifact.saveAs(harParams.path + '.tmp');
  441. await this._connection.localUtils()._channel.harUnzip({
  442. zipFile: harParams.path + '.tmp',
  443. harFile: harParams.path
  444. });
  445. } else {
  446. await artifact.saveAs(harParams.path);
  447. }
  448. await artifact.delete();
  449. }
  450. }, true);
  451. await this._channel.close(options);
  452. await this._closedPromise;
  453. }
  454. async _enableRecorder(params) {
  455. await this._channel.recorderSupplementEnable(params);
  456. }
  457. }
  458. exports.BrowserContext = BrowserContext;
  459. async function prepareStorageState(options) {
  460. if (typeof options.storageState !== 'string') return options.storageState;
  461. try {
  462. return JSON.parse(await _fs.default.promises.readFile(options.storageState, 'utf8'));
  463. } catch (e) {
  464. (0, _stackTrace.rewriteErrorMessage)(e, `Error reading storage state from ${options.storageState}:\n` + e.message);
  465. throw e;
  466. }
  467. }
  468. function prepareRecordHarOptions(options) {
  469. if (!options) return;
  470. return {
  471. path: options.path,
  472. content: options.content || (options.omitContent ? 'omit' : undefined),
  473. urlGlob: (0, _utils.isString)(options.urlFilter) ? options.urlFilter : undefined,
  474. urlRegexSource: (0, _utils.isRegExp)(options.urlFilter) ? options.urlFilter.source : undefined,
  475. urlRegexFlags: (0, _utils.isRegExp)(options.urlFilter) ? options.urlFilter.flags : undefined,
  476. mode: options.mode
  477. };
  478. }
  479. async function prepareBrowserContextParams(options) {
  480. if (options.videoSize && !options.videosPath) throw new Error(`"videoSize" option requires "videosPath" to be specified`);
  481. if (options.extraHTTPHeaders) network.validateHeaders(options.extraHTTPHeaders);
  482. const contextParams = {
  483. ...options,
  484. viewport: options.viewport === null ? undefined : options.viewport,
  485. noDefaultViewport: options.viewport === null,
  486. extraHTTPHeaders: options.extraHTTPHeaders ? (0, _utils.headersObjectToArray)(options.extraHTTPHeaders) : undefined,
  487. storageState: await prepareStorageState(options),
  488. serviceWorkers: options.serviceWorkers,
  489. recordHar: prepareRecordHarOptions(options.recordHar),
  490. colorScheme: options.colorScheme === null ? 'no-override' : options.colorScheme,
  491. reducedMotion: options.reducedMotion === null ? 'no-override' : options.reducedMotion,
  492. forcedColors: options.forcedColors === null ? 'no-override' : options.forcedColors,
  493. acceptDownloads: toAcceptDownloadsProtocol(options.acceptDownloads)
  494. };
  495. if (!contextParams.recordVideo && options.videosPath) {
  496. contextParams.recordVideo = {
  497. dir: options.videosPath,
  498. size: options.videoSize
  499. };
  500. }
  501. if (contextParams.recordVideo && contextParams.recordVideo.dir) contextParams.recordVideo.dir = _path.default.resolve(process.cwd(), contextParams.recordVideo.dir);
  502. return contextParams;
  503. }
  504. function toAcceptDownloadsProtocol(acceptDownloads) {
  505. if (acceptDownloads === undefined) return undefined;
  506. if (acceptDownloads === true) return 'accept';
  507. return 'deny';
  508. }