browserContextDispatcher.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", {
  3. value: true
  4. });
  5. exports.BrowserContextDispatcher = void 0;
  6. var _browserContext = require("../browserContext");
  7. var _dispatcher = require("./dispatcher");
  8. var _pageDispatcher = require("./pageDispatcher");
  9. var _networkDispatchers = require("./networkDispatchers");
  10. var _crBrowser = require("../chromium/crBrowser");
  11. var _cdpSessionDispatcher = require("./cdpSessionDispatcher");
  12. var _recorder = require("../recorder");
  13. var _artifactDispatcher = require("./artifactDispatcher");
  14. var _tracingDispatcher = require("./tracingDispatcher");
  15. var fs = _interopRequireWildcard(require("fs"));
  16. var path = _interopRequireWildcard(require("path"));
  17. var _utils = require("../../utils");
  18. var _writableStreamDispatcher = require("./writableStreamDispatcher");
  19. var _dialogDispatcher = require("./dialogDispatcher");
  20. var _errors = require("../errors");
  21. var _elementHandlerDispatcher = require("./elementHandlerDispatcher");
  22. 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); }
  23. 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; }
  24. /**
  25. * Copyright (c) Microsoft Corporation.
  26. *
  27. * Licensed under the Apache License, Version 2.0 (the 'License");
  28. * you may not use this file except in compliance with the License.
  29. * You may obtain a copy of the License at
  30. *
  31. * http://www.apache.org/licenses/LICENSE-2.0
  32. *
  33. * Unless required by applicable law or agreed to in writing, software
  34. * distributed under the License is distributed on an "AS IS" BASIS,
  35. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  36. * See the License for the specific language governing permissions and
  37. * limitations under the License.
  38. */
  39. class BrowserContextDispatcher extends _dispatcher.Dispatcher {
  40. constructor(parentScope, context) {
  41. // We will reparent these to the context below.
  42. const requestContext = _networkDispatchers.APIRequestContextDispatcher.from(parentScope, context.fetchRequest);
  43. const tracing = _tracingDispatcher.TracingDispatcher.from(parentScope, context.tracing);
  44. super(parentScope, context, 'BrowserContext', {
  45. isChromium: context._browser.options.isChromium,
  46. isLocalBrowserOnServer: context._browser._isCollocatedWithServer,
  47. requestContext,
  48. tracing
  49. });
  50. this._type_EventTarget = true;
  51. this._type_BrowserContext = true;
  52. this._context = void 0;
  53. this._subscriptions = new Set();
  54. this.adopt(requestContext);
  55. this.adopt(tracing);
  56. this._context = context;
  57. // Note: when launching persistent context, dispatcher is created very late,
  58. // so we can already have pages, videos and everything else.
  59. const onVideo = artifact => {
  60. // Note: Video must outlive Page and BrowserContext, so that client can saveAs it
  61. // after closing the context. We use |scope| for it.
  62. const artifactDispatcher = _artifactDispatcher.ArtifactDispatcher.from(parentScope, artifact);
  63. this._dispatchEvent('video', {
  64. artifact: artifactDispatcher
  65. });
  66. };
  67. this.addObjectListener(_browserContext.BrowserContext.Events.VideoStarted, onVideo);
  68. for (const video of context._browser._idToVideo.values()) {
  69. if (video.context === context) onVideo(video.artifact);
  70. }
  71. for (const page of context.pages()) this._dispatchEvent('page', {
  72. page: _pageDispatcher.PageDispatcher.from(this, page)
  73. });
  74. this.addObjectListener(_browserContext.BrowserContext.Events.Page, page => {
  75. this._dispatchEvent('page', {
  76. page: _pageDispatcher.PageDispatcher.from(this, page)
  77. });
  78. });
  79. this.addObjectListener(_browserContext.BrowserContext.Events.Close, () => {
  80. this._dispatchEvent('close');
  81. this._dispose();
  82. });
  83. this.addObjectListener(_browserContext.BrowserContext.Events.PageError, (error, page) => {
  84. this._dispatchEvent('pageError', {
  85. error: (0, _errors.serializeError)(error),
  86. page: _pageDispatcher.PageDispatcher.from(this, page)
  87. });
  88. });
  89. this.addObjectListener(_browserContext.BrowserContext.Events.Console, message => {
  90. if (this._shouldDispatchEvent(message.page(), 'console')) {
  91. const pageDispatcher = _pageDispatcher.PageDispatcher.from(this, message.page());
  92. this._dispatchEvent('console', {
  93. page: pageDispatcher,
  94. type: message.type(),
  95. text: message.text(),
  96. args: message.args().map(a => _elementHandlerDispatcher.ElementHandleDispatcher.fromJSHandle(pageDispatcher, a)),
  97. location: message.location()
  98. });
  99. }
  100. });
  101. this.addObjectListener(_browserContext.BrowserContext.Events.Dialog, dialog => {
  102. if (this._shouldDispatchEvent(dialog.page(), 'dialog')) this._dispatchEvent('dialog', {
  103. dialog: new _dialogDispatcher.DialogDispatcher(this, dialog)
  104. });else dialog.close().catch(() => {});
  105. });
  106. if (context._browser.options.name === 'chromium') {
  107. for (const page of context.backgroundPages()) this._dispatchEvent('backgroundPage', {
  108. page: _pageDispatcher.PageDispatcher.from(this, page)
  109. });
  110. this.addObjectListener(_crBrowser.CRBrowserContext.CREvents.BackgroundPage, page => this._dispatchEvent('backgroundPage', {
  111. page: _pageDispatcher.PageDispatcher.from(this, page)
  112. }));
  113. for (const serviceWorker of context.serviceWorkers()) this._dispatchEvent('serviceWorker', {
  114. worker: new _pageDispatcher.WorkerDispatcher(this, serviceWorker)
  115. });
  116. this.addObjectListener(_crBrowser.CRBrowserContext.CREvents.ServiceWorker, serviceWorker => this._dispatchEvent('serviceWorker', {
  117. worker: new _pageDispatcher.WorkerDispatcher(this, serviceWorker)
  118. }));
  119. }
  120. this.addObjectListener(_browserContext.BrowserContext.Events.Request, request => {
  121. var _request$frame;
  122. // Create dispatcher, if:
  123. // - There are listeners to the requests.
  124. // - We are redirected from a reported request so that redirectedTo was updated on client.
  125. // - We are a navigation request and dispatcher will be reported as a part of the goto return value and newDocument param anyways.
  126. // By the time requestFinished is triggered to update the request, we should have a request on the client already.
  127. const redirectFromDispatcher = request.redirectedFrom() && (0, _dispatcher.existingDispatcher)(request.redirectedFrom());
  128. if (!redirectFromDispatcher && !this._shouldDispatchNetworkEvent(request, 'request') && !request.isNavigationRequest()) return;
  129. const requestDispatcher = _networkDispatchers.RequestDispatcher.from(this, request);
  130. this._dispatchEvent('request', {
  131. request: requestDispatcher,
  132. page: _pageDispatcher.PageDispatcher.fromNullable(this, (_request$frame = request.frame()) === null || _request$frame === void 0 ? void 0 : _request$frame._page.initializedOrUndefined())
  133. });
  134. });
  135. this.addObjectListener(_browserContext.BrowserContext.Events.Response, response => {
  136. var _response$frame;
  137. const requestDispatcher = (0, _dispatcher.existingDispatcher)(response.request());
  138. if (!requestDispatcher && !this._shouldDispatchNetworkEvent(response.request(), 'response')) return;
  139. this._dispatchEvent('response', {
  140. response: _networkDispatchers.ResponseDispatcher.from(this, response),
  141. page: _pageDispatcher.PageDispatcher.fromNullable(this, (_response$frame = response.frame()) === null || _response$frame === void 0 ? void 0 : _response$frame._page.initializedOrUndefined())
  142. });
  143. });
  144. this.addObjectListener(_browserContext.BrowserContext.Events.RequestFailed, request => {
  145. var _request$frame2;
  146. const requestDispatcher = (0, _dispatcher.existingDispatcher)(request);
  147. if (!requestDispatcher && !this._shouldDispatchNetworkEvent(request, 'requestFailed')) return;
  148. this._dispatchEvent('requestFailed', {
  149. request: _networkDispatchers.RequestDispatcher.from(this, request),
  150. failureText: request._failureText || undefined,
  151. responseEndTiming: request._responseEndTiming,
  152. page: _pageDispatcher.PageDispatcher.fromNullable(this, (_request$frame2 = request.frame()) === null || _request$frame2 === void 0 ? void 0 : _request$frame2._page.initializedOrUndefined())
  153. });
  154. });
  155. this.addObjectListener(_browserContext.BrowserContext.Events.RequestFinished, ({
  156. request,
  157. response
  158. }) => {
  159. var _request$frame3;
  160. const requestDispatcher = (0, _dispatcher.existingDispatcher)(request);
  161. if (!requestDispatcher && !this._shouldDispatchNetworkEvent(request, 'requestFinished')) return;
  162. this._dispatchEvent('requestFinished', {
  163. request: _networkDispatchers.RequestDispatcher.from(this, request),
  164. response: _networkDispatchers.ResponseDispatcher.fromNullable(this, response),
  165. responseEndTiming: request._responseEndTiming,
  166. page: _pageDispatcher.PageDispatcher.fromNullable(this, (_request$frame3 = request.frame()) === null || _request$frame3 === void 0 ? void 0 : _request$frame3._page.initializedOrUndefined())
  167. });
  168. });
  169. }
  170. _shouldDispatchNetworkEvent(request, event) {
  171. var _request$frame4, _request$frame4$_page;
  172. return this._shouldDispatchEvent((_request$frame4 = request.frame()) === null || _request$frame4 === void 0 ? void 0 : (_request$frame4$_page = _request$frame4._page) === null || _request$frame4$_page === void 0 ? void 0 : _request$frame4$_page.initializedOrUndefined(), event);
  173. }
  174. _shouldDispatchEvent(page, event) {
  175. if (this._subscriptions.has(event)) return true;
  176. const pageDispatcher = page ? (0, _dispatcher.existingDispatcher)(page) : undefined;
  177. if (pageDispatcher !== null && pageDispatcher !== void 0 && pageDispatcher._subscriptions.has(event)) return true;
  178. return false;
  179. }
  180. async createTempFile(params) {
  181. const dir = this._context._browser.options.artifactsDir;
  182. const tmpDir = path.join(dir, 'upload-' + (0, _utils.createGuid)());
  183. await fs.promises.mkdir(tmpDir);
  184. this._context._tempDirs.push(tmpDir);
  185. const file = fs.createWriteStream(path.join(tmpDir, params.name));
  186. return {
  187. writableStream: new _writableStreamDispatcher.WritableStreamDispatcher(this, file, params.lastModifiedMs)
  188. };
  189. }
  190. async setDefaultNavigationTimeoutNoReply(params) {
  191. this._context.setDefaultNavigationTimeout(params.timeout);
  192. }
  193. async setDefaultTimeoutNoReply(params) {
  194. this._context.setDefaultTimeout(params.timeout);
  195. }
  196. async exposeBinding(params) {
  197. await this._context.exposeBinding(params.name, !!params.needsHandle, (source, ...args) => {
  198. // When reusing the context, we might have some bindings called late enough,
  199. // after context and page dispatchers have been disposed.
  200. if (this._disposed) return;
  201. const pageDispatcher = _pageDispatcher.PageDispatcher.from(this, source.page);
  202. const binding = new _pageDispatcher.BindingCallDispatcher(pageDispatcher, params.name, !!params.needsHandle, source, args);
  203. this._dispatchEvent('bindingCall', {
  204. binding
  205. });
  206. return binding.promise();
  207. });
  208. }
  209. async newPage(params, metadata) {
  210. return {
  211. page: _pageDispatcher.PageDispatcher.from(this, await this._context.newPage(metadata))
  212. };
  213. }
  214. async cookies(params) {
  215. return {
  216. cookies: await this._context.cookies(params.urls)
  217. };
  218. }
  219. async addCookies(params) {
  220. await this._context.addCookies(params.cookies);
  221. }
  222. async clearCookies() {
  223. await this._context.clearCookies();
  224. }
  225. async grantPermissions(params) {
  226. await this._context.grantPermissions(params.permissions, params.origin);
  227. }
  228. async clearPermissions() {
  229. await this._context.clearPermissions();
  230. }
  231. async setGeolocation(params) {
  232. await this._context.setGeolocation(params.geolocation);
  233. }
  234. async setExtraHTTPHeaders(params) {
  235. await this._context.setExtraHTTPHeaders(params.headers);
  236. }
  237. async setOffline(params) {
  238. await this._context.setOffline(params.offline);
  239. }
  240. async setHTTPCredentials(params) {
  241. await this._context.setHTTPCredentials(params.httpCredentials);
  242. }
  243. async addInitScript(params) {
  244. await this._context.addInitScript(params.source);
  245. }
  246. async setNetworkInterceptionPatterns(params) {
  247. if (!params.patterns.length) {
  248. await this._context.setRequestInterceptor(undefined);
  249. return;
  250. }
  251. const urlMatchers = params.patterns.map(pattern => pattern.regexSource ? new RegExp(pattern.regexSource, pattern.regexFlags) : pattern.glob);
  252. await this._context.setRequestInterceptor((route, request) => {
  253. const matchesSome = urlMatchers.some(urlMatch => (0, _utils.urlMatches)(this._context._options.baseURL, request.url(), urlMatch));
  254. if (!matchesSome) return false;
  255. this._dispatchEvent('route', {
  256. route: _networkDispatchers.RouteDispatcher.from(_networkDispatchers.RequestDispatcher.from(this, request), route)
  257. });
  258. return true;
  259. });
  260. }
  261. async storageState(params, metadata) {
  262. return await this._context.storageState();
  263. }
  264. async close(params, metadata) {
  265. metadata.potentiallyClosesScope = true;
  266. await this._context.close(params);
  267. }
  268. async recorderSupplementEnable(params) {
  269. await _recorder.Recorder.show(this._context, params);
  270. }
  271. async pause(params, metadata) {
  272. // Debugger will take care of this.
  273. }
  274. async newCDPSession(params) {
  275. if (!this._object._browser.options.isChromium) throw new Error(`CDP session is only available in Chromium`);
  276. if (!params.page && !params.frame || params.page && params.frame) throw new Error(`CDP session must be initiated with either Page or Frame, not none or both`);
  277. const crBrowserContext = this._object;
  278. return {
  279. session: new _cdpSessionDispatcher.CDPSessionDispatcher(this, await crBrowserContext.newCDPSession((params.page ? params.page : params.frame)._object))
  280. };
  281. }
  282. async harStart(params) {
  283. const harId = await this._context._harStart(params.page ? params.page._object : null, params.options);
  284. return {
  285. harId
  286. };
  287. }
  288. async harExport(params) {
  289. const artifact = await this._context._harExport(params.harId);
  290. if (!artifact) throw new Error('No HAR artifact. Ensure record.harPath is set.');
  291. return {
  292. artifact: _artifactDispatcher.ArtifactDispatcher.from(this, artifact)
  293. };
  294. }
  295. async updateSubscription(params) {
  296. if (params.enabled) this._subscriptions.add(params.event);else this._subscriptions.delete(params.event);
  297. }
  298. _onDispose() {
  299. // Avoid protocol calls for the closed context.
  300. if (!this._context.isClosingOrClosed()) this._context.setRequestInterceptor(undefined).catch(() => {});
  301. }
  302. }
  303. exports.BrowserContextDispatcher = BrowserContextDispatcher;