| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  | /* This Source Code Form is subject to the terms of the Mozilla Public | 
					
						
							|  |  |  |  * License, v. 2.0. If a copy of the MPL was not distributed with this | 
					
						
							|  |  |  |  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | "use strict"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js'); | 
					
						
							|  |  |  | const {NetUtil} = ChromeUtils.import('resource://gre/modules/NetUtil.jsm'); | 
					
						
							| 
									
										
										
										
											2022-07-05 08:20:01 -07:00
										 |  |  | const { ChannelEventSinkFactory } = ChromeUtils.import("chrome://remote/content/cdp/observers/ChannelEventSink.jsm"); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const Cc = Components.classes; | 
					
						
							|  |  |  | const Ci = Components.interfaces; | 
					
						
							|  |  |  | const Cu = Components.utils; | 
					
						
							|  |  |  | const Cr = Components.results; | 
					
						
							|  |  |  | const Cm = Components.manager; | 
					
						
							|  |  |  | const CC = Components.Constructor; | 
					
						
							|  |  |  | const helper = new Helper(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-04 08:52:43 -07:00
										 |  |  | const UINT32_MAX = Math.pow(2, 32)-1; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  | const BinaryInputStream = CC('@mozilla.org/binaryinputstream;1', 'nsIBinaryInputStream', 'setInputStream'); | 
					
						
							|  |  |  | const BinaryOutputStream = CC('@mozilla.org/binaryoutputstream;1', 'nsIBinaryOutputStream', 'setOutputStream'); | 
					
						
							|  |  |  | const StorageStream = CC('@mozilla.org/storagestream;1', 'nsIStorageStream', 'init'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Cap response storage with 100Mb per tracked tab.
 | 
					
						
							|  |  |  | const MAX_RESPONSE_STORAGE_SIZE = 100 * 1024 * 1024; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const pageNetworkSymbol = Symbol('PageNetwork'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class PageNetwork { | 
					
						
							| 
									
										
										
										
											2021-08-10 14:43:21 -07:00
										 |  |  |   static forPageTarget(target) { | 
					
						
							| 
									
										
										
										
											2023-08-10 14:41:33 -07:00
										 |  |  |     if (!target) | 
					
						
							|  |  |  |       return undefined; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     let result = target[pageNetworkSymbol]; | 
					
						
							|  |  |  |     if (!result) { | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |       result = new PageNetwork(target); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |       target[pageNetworkSymbol] = result; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return result; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |   constructor(target) { | 
					
						
							| 
									
										
										
										
											2023-01-23 11:29:48 -08:00
										 |  |  |     helper.decorateAsEventEmitter(this); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     this._target = target; | 
					
						
							|  |  |  |     this._extraHTTPHeaders = null; | 
					
						
							| 
									
										
										
										
											2020-10-06 01:53:25 -07:00
										 |  |  |     this._responseStorage = new ResponseStorage(MAX_RESPONSE_STORAGE_SIZE, MAX_RESPONSE_STORAGE_SIZE / 10); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     this._requestInterceptionEnabled = false; | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     // This is requestId => NetworkRequest map, only contains requests that are
 | 
					
						
							|  |  |  |     // awaiting interception action (abort, resume, fulfill) over the protocol.
 | 
					
						
							|  |  |  |     this._interceptedRequests = new Map(); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   setExtraHTTPHeaders(headers) { | 
					
						
							|  |  |  |     this._extraHTTPHeaders = headers; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-08 09:26:11 -07:00
										 |  |  |   combinedExtraHTTPHeaders() { | 
					
						
							|  |  |  |     return [ | 
					
						
							|  |  |  |       ...(this._target.browserContext().extraHTTPHeaders || []), | 
					
						
							|  |  |  |       ...(this._extraHTTPHeaders || []), | 
					
						
							|  |  |  |     ]; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   enableRequestInterception() { | 
					
						
							|  |  |  |     this._requestInterceptionEnabled = true; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   disableRequestInterception() { | 
					
						
							|  |  |  |     this._requestInterceptionEnabled = false; | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     for (const intercepted of this._interceptedRequests.values()) | 
					
						
							|  |  |  |       intercepted.resume(); | 
					
						
							|  |  |  |     this._interceptedRequests.clear(); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-04 12:29:36 -07:00
										 |  |  |   resumeInterceptedRequest(requestId, url, method, headers, postData) { | 
					
						
							| 
									
										
										
										
											2020-11-13 14:56:27 -08:00
										 |  |  |     this._takeIntercepted(requestId).resume(url, method, headers, postData); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   fulfillInterceptedRequest(requestId, status, statusText, headers, base64body) { | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     this._takeIntercepted(requestId).fulfill(status, statusText, headers, base64body); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   abortInterceptedRequest(requestId, errorCode) { | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     this._takeIntercepted(requestId).abort(errorCode); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   getResponseBody(requestId) { | 
					
						
							|  |  |  |     if (!this._responseStorage) | 
					
						
							|  |  |  |       throw new Error('Responses are not tracked for the given browser'); | 
					
						
							|  |  |  |     return this._responseStorage.getBase64EncodedResponse(requestId); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |   _takeIntercepted(requestId) { | 
					
						
							|  |  |  |     const intercepted = this._interceptedRequests.get(requestId); | 
					
						
							|  |  |  |     if (!intercepted) | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |       throw new Error(`Cannot find request "${requestId}"`); | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     this._interceptedRequests.delete(requestId); | 
					
						
							|  |  |  |     return intercepted; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  | class NetworkRequest { | 
					
						
							|  |  |  |   constructor(networkObserver, httpChannel, redirectedFrom) { | 
					
						
							|  |  |  |     this._networkObserver = networkObserver; | 
					
						
							|  |  |  |     this.httpChannel = httpChannel; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-06 00:15:24 -07:00
										 |  |  |     const loadInfo = this.httpChannel.loadInfo; | 
					
						
							| 
									
										
										
										
											2023-08-10 14:41:33 -07:00
										 |  |  |     const browsingContext = loadInfo?.frameBrowsingContext || loadInfo?.workerAssociatedBrowsingContext || loadInfo?.browsingContext; | 
					
						
							| 
									
										
										
										
											2020-10-06 00:15:24 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     this._frameId = helper.browsingContextToFrameId(browsingContext); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     this.requestId = httpChannel.channelId + ''; | 
					
						
							| 
									
										
										
										
											2023-09-11 18:16:33 -07:00
										 |  |  |     this.navigationId = httpChannel.isMainDocumentChannel && loadInfo ? helper.toProtocolNavigationId(loadInfo.jugglerLoadIdentifier) : undefined; | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     this._redirectedIndex = 0; | 
					
						
							| 
									
										
										
										
											2021-06-30 12:59:27 -07:00
										 |  |  |     if (redirectedFrom) { | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |       this.redirectedFromId = redirectedFrom.requestId; | 
					
						
							|  |  |  |       this._redirectedIndex = redirectedFrom._redirectedIndex + 1; | 
					
						
							|  |  |  |       this.requestId = this.requestId + '-redirect' + this._redirectedIndex; | 
					
						
							|  |  |  |       this.navigationId = redirectedFrom.navigationId; | 
					
						
							|  |  |  |       // Finish previous request now. Since we inherit the listener, we could in theory
 | 
					
						
							|  |  |  |       // use onStopRequest, but that will only happen after the last redirect has finished.
 | 
					
						
							|  |  |  |       redirectedFrom._sendOnRequestFinished(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-12-10 13:11:58 -08:00
										 |  |  |     // In case of proxy auth, we get two requests with the same channel:
 | 
					
						
							|  |  |  |     // - one is pre-auth
 | 
					
						
							|  |  |  |     // - second is with auth header.
 | 
					
						
							|  |  |  |     //
 | 
					
						
							|  |  |  |     // In this case, we create this NetworkRequest object with a `redirectedFrom`
 | 
					
						
							|  |  |  |     // object, and they both share the same httpChannel.
 | 
					
						
							|  |  |  |     //
 | 
					
						
							|  |  |  |     // Since we want to maintain _channelToRequest map without clashes,
 | 
					
						
							|  |  |  |     // we must call `_sendOnRequestFinished` **before** we update it with a new object
 | 
					
						
							|  |  |  |     // here.
 | 
					
						
							|  |  |  |     if (this._networkObserver._channelToRequest.has(this.httpChannel)) | 
					
						
							|  |  |  |       throw new Error(`Internal Error: invariant is broken for _channelToRequest map`); | 
					
						
							|  |  |  |     this._networkObserver._channelToRequest.set(this.httpChannel, this); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-10 14:41:33 -07:00
										 |  |  |     if (redirectedFrom) { | 
					
						
							|  |  |  |       this._pageNetwork = redirectedFrom._pageNetwork; | 
					
						
							|  |  |  |     } else if (browsingContext) { | 
					
						
							|  |  |  |       const target = this._networkObserver._targetRegistry.targetForBrowserId(browsingContext.browserId); | 
					
						
							|  |  |  |       this._pageNetwork = PageNetwork.forPageTarget(target); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     this._expectingInterception = false; | 
					
						
							|  |  |  |     this._expectingResumedRequest = undefined;  // { method, headers, postData }
 | 
					
						
							|  |  |  |     this._sentOnResponse = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-08 09:26:11 -07:00
										 |  |  |     if (this._pageNetwork) | 
					
						
							|  |  |  |       appendExtraHTTPHeaders(httpChannel, this._pageNetwork.combinedExtraHTTPHeaders()); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     this._responseBodyChunks = []; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     httpChannel.QueryInterface(Ci.nsITraceableChannel); | 
					
						
							|  |  |  |     this._originalListener = httpChannel.setNewListener(this); | 
					
						
							| 
									
										
										
										
											2021-11-04 12:29:36 -07:00
										 |  |  |     if (redirectedFrom) { | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |       // Listener is inherited for regular redirects, so we'd like to avoid
 | 
					
						
							|  |  |  |       // calling into previous NetworkRequest.
 | 
					
						
							|  |  |  |       this._originalListener = redirectedFrom._originalListener; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     this._previousCallbacks = httpChannel.notificationCallbacks; | 
					
						
							|  |  |  |     httpChannel.notificationCallbacks = this; | 
					
						
							| 
									
										
										
										
											2020-06-04 08:52:43 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     this.QueryInterface = ChromeUtils.generateQI([ | 
					
						
							|  |  |  |       Ci.nsIAuthPrompt2, | 
					
						
							|  |  |  |       Ci.nsIAuthPromptProvider, | 
					
						
							|  |  |  |       Ci.nsIInterfaceRequestor, | 
					
						
							|  |  |  |       Ci.nsINetworkInterceptController, | 
					
						
							|  |  |  |       Ci.nsIStreamListener, | 
					
						
							|  |  |  |     ]); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     if (this.redirectedFromId) { | 
					
						
							|  |  |  |       // Redirects are not interceptable.
 | 
					
						
							|  |  |  |       this._sendOnRequest(false); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |   // Public interception API.
 | 
					
						
							| 
									
										
										
										
											2020-11-13 14:56:27 -08:00
										 |  |  |   resume(url, method, headers, postData) { | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     this._expectingResumedRequest = { method, headers, postData }; | 
					
						
							| 
									
										
										
										
											2020-11-13 14:56:27 -08:00
										 |  |  |     const newUri = url ? Services.io.newURI(url) : null; | 
					
						
							|  |  |  |     this._interceptedChannel.resetInterceptionWithURI(newUri); | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     this._interceptedChannel = undefined; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |   // Public interception API.
 | 
					
						
							|  |  |  |   abort(errorCode) { | 
					
						
							|  |  |  |     const error = errorMap[errorCode] || Cr.NS_ERROR_FAILURE; | 
					
						
							|  |  |  |     this._interceptedChannel.cancelInterception(error); | 
					
						
							|  |  |  |     this._interceptedChannel = undefined; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |   // Public interception API.
 | 
					
						
							|  |  |  |   fulfill(status, statusText, headers, base64body) { | 
					
						
							|  |  |  |     this._interceptedChannel.synthesizeStatus(status, statusText); | 
					
						
							| 
									
										
										
										
											2021-02-15 22:49:57 -08:00
										 |  |  |     for (const header of headers) { | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |       this._interceptedChannel.synthesizeHeader(header.name, header.value); | 
					
						
							| 
									
										
										
										
											2021-02-15 22:49:57 -08:00
										 |  |  |       if (header.name.toLowerCase() === 'set-cookie') { | 
					
						
							|  |  |  |         Services.cookies.QueryInterface(Ci.nsICookieService); | 
					
						
							|  |  |  |         Services.cookies.setCookieStringFromHttp(this.httpChannel.URI, header.value, this.httpChannel); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     const synthesized = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream); | 
					
						
							| 
									
										
										
										
											2021-11-04 12:29:36 -07:00
										 |  |  |     synthesized.data = base64body ? atob(base64body) : ''; | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     this._interceptedChannel.startSynthesizedResponse(synthesized, null, null, '', false); | 
					
						
							|  |  |  |     this._interceptedChannel.finishSynthesizedResponse(); | 
					
						
							|  |  |  |     this._interceptedChannel = undefined; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |   // Instrumentation called by NetworkObserver.
 | 
					
						
							|  |  |  |   _onInternalRedirect(newChannel) { | 
					
						
							|  |  |  |     // Intercepted requests produce "internal redirects" - this is both for our own
 | 
					
						
							|  |  |  |     // interception and service workers.
 | 
					
						
							| 
									
										
										
										
											2023-01-23 11:29:48 -08:00
										 |  |  |     // An internal redirect does not necessarily have the same channelId,
 | 
					
						
							|  |  |  |     // but inherits notificationCallbacks and the listener,
 | 
					
						
							|  |  |  |     // and should be used instead of an old channel.
 | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     this._networkObserver._channelToRequest.delete(this.httpChannel); | 
					
						
							|  |  |  |     this.httpChannel = newChannel; | 
					
						
							|  |  |  |     this._networkObserver._channelToRequest.set(this.httpChannel, this); | 
					
						
							| 
									
										
										
										
											2020-07-28 11:32:44 -07:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-28 11:32:44 -07:00
										 |  |  |   // Instrumentation called by NetworkObserver.
 | 
					
						
							|  |  |  |   _onInternalRedirectReady() { | 
					
						
							|  |  |  |     // Resumed request is first internally redirected to a new request,
 | 
					
						
							|  |  |  |     // and then the new request is ready to be updated.
 | 
					
						
							|  |  |  |     if (!this._expectingResumedRequest) | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     const { method, headers, postData } = this._expectingResumedRequest; | 
					
						
							|  |  |  |     this._expectingResumedRequest = undefined; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (headers) { | 
					
						
							|  |  |  |       for (const header of requestHeaders(this.httpChannel)) | 
					
						
							|  |  |  |         this.httpChannel.setRequestHeader(header.name, '', false /* merge */); | 
					
						
							|  |  |  |       for (const header of headers) | 
					
						
							|  |  |  |         this.httpChannel.setRequestHeader(header.name, header.value, false /* merge */); | 
					
						
							| 
									
										
										
										
											2022-01-08 09:26:11 -07:00
										 |  |  |     } else if (this._pageNetwork) { | 
					
						
							|  |  |  |       appendExtraHTTPHeaders(this.httpChannel, this._pageNetwork.combinedExtraHTTPHeaders()); | 
					
						
							| 
									
										
										
										
											2020-07-28 11:32:44 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |     if (method) | 
					
						
							|  |  |  |       this.httpChannel.requestMethod = method; | 
					
						
							| 
									
										
										
										
											2022-01-20 16:19:29 -08:00
										 |  |  |     if (postData !== undefined) | 
					
						
							| 
									
										
										
										
											2021-07-09 05:41:53 -07:00
										 |  |  |       setPostData(this.httpChannel, postData, headers); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |   // nsIInterfaceRequestor
 | 
					
						
							|  |  |  |   getInterface(iid) { | 
					
						
							|  |  |  |     if (iid.equals(Ci.nsIAuthPrompt2) || iid.equals(Ci.nsIAuthPromptProvider) || iid.equals(Ci.nsINetworkInterceptController)) | 
					
						
							|  |  |  |       return this; | 
					
						
							|  |  |  |     if (iid.equals(Ci.nsIAuthPrompt))  // Block nsIAuthPrompt - we want nsIAuthPrompt2 to be used instead.
 | 
					
						
							|  |  |  |       throw Cr.NS_ERROR_NO_INTERFACE; | 
					
						
							|  |  |  |     if (this._previousCallbacks) | 
					
						
							|  |  |  |       return this._previousCallbacks.getInterface(iid); | 
					
						
							|  |  |  |     throw Cr.NS_ERROR_NO_INTERFACE; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // nsIAuthPromptProvider
 | 
					
						
							|  |  |  |   getAuthPrompt(aPromptReason, iid) { | 
					
						
							|  |  |  |     return this; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // nsIAuthPrompt2
 | 
					
						
							|  |  |  |   asyncPromptAuth(aChannel, aCallback, aContext, level, authInfo) { | 
					
						
							|  |  |  |     let canceled = false; | 
					
						
							|  |  |  |     Promise.resolve().then(() => { | 
					
						
							|  |  |  |       if (canceled) | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       const hasAuth = this.promptAuth(aChannel, level, authInfo); | 
					
						
							|  |  |  |       if (hasAuth) | 
					
						
							|  |  |  |         aCallback.onAuthAvailable(aContext, authInfo); | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         aCallback.onAuthCancelled(aContext, true); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |       QueryInterface: ChromeUtils.generateQI([Ci.nsICancelable]), | 
					
						
							|  |  |  |       cancel: () => { | 
					
						
							|  |  |  |         aCallback.onAuthCancelled(aContext, false); | 
					
						
							|  |  |  |         canceled = true; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // nsIAuthPrompt2
 | 
					
						
							|  |  |  |   promptAuth(aChannel, level, authInfo) { | 
					
						
							|  |  |  |     if (authInfo.flags & Ci.nsIAuthInformation.PREVIOUS_FAILED) | 
					
						
							|  |  |  |       return false; | 
					
						
							| 
									
										
										
										
											2021-08-10 14:43:21 -07:00
										 |  |  |     const pageNetwork = this._pageNetwork; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     if (!pageNetwork) | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |       return false; | 
					
						
							| 
									
										
										
										
											2020-08-07 15:38:06 -07:00
										 |  |  |     let credentials = null; | 
					
						
							|  |  |  |     if (authInfo.flags & Ci.nsIAuthInformation.AUTH_PROXY) { | 
					
						
							|  |  |  |       const proxy = this._networkObserver._targetRegistry.getProxyInfo(aChannel); | 
					
						
							|  |  |  |       credentials = proxy ? {username: proxy.username, password: proxy.password} : null; | 
					
						
							|  |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2020-08-19 15:39:46 -07:00
										 |  |  |       credentials = pageNetwork._target.browserContext().httpCredentials; | 
					
						
							| 
									
										
										
										
											2020-08-07 15:38:06 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     if (!credentials) | 
					
						
							|  |  |  |       return false; | 
					
						
							| 
									
										
										
										
											2023-04-24 21:28:08 +00:00
										 |  |  |     const origin = aChannel.URI.scheme + '://' + aChannel.URI.hostPort; | 
					
						
							|  |  |  |     if (credentials.origin && origin.toLowerCase() !== credentials.origin.toLowerCase()) | 
					
						
							|  |  |  |       return false; | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     authInfo.username = credentials.username; | 
					
						
							|  |  |  |     authInfo.password = credentials.password; | 
					
						
							|  |  |  |     // This will produce a new request with respective auth header set.
 | 
					
						
							|  |  |  |     // It will have the same id as ours. We expect it to arrive as new request and
 | 
					
						
							|  |  |  |     // will treat it as our own redirect.
 | 
					
						
							|  |  |  |     this._networkObserver._expectRedirect(this.httpChannel.channelId + '', this); | 
					
						
							|  |  |  |     return true; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |   // nsINetworkInterceptController
 | 
					
						
							|  |  |  |   shouldPrepareForIntercept(aURI, channel) { | 
					
						
							|  |  |  |     const interceptController = this._fallThroughInterceptController(); | 
					
						
							|  |  |  |     if (interceptController && interceptController.shouldPrepareForIntercept(aURI, channel)) { | 
					
						
							|  |  |  |       // We assume that interceptController is a service worker if there is one,
 | 
					
						
							|  |  |  |       // and yield interception to it. We are not going to intercept ourselves,
 | 
					
						
							|  |  |  |       // so we send onRequest now.
 | 
					
						
							|  |  |  |       this._sendOnRequest(false); | 
					
						
							|  |  |  |       return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (channel !== this.httpChannel) { | 
					
						
							|  |  |  |       // Not our channel? Just in case this happens, don't do anything.
 | 
					
						
							|  |  |  |       return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-22 15:57:05 -07:00
										 |  |  |     // We do not want to intercept any redirects, because we are not able
 | 
					
						
							|  |  |  |     // to intercept subresource redirects, and it's unreliable for main requests.
 | 
					
						
							|  |  |  |     // We do not sendOnRequest here, because redirects do that in constructor.
 | 
					
						
							|  |  |  |     if (this.redirectedFromId) | 
					
						
							|  |  |  |       return false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     const shouldIntercept = this._shouldIntercept(); | 
					
						
							|  |  |  |     if (!shouldIntercept) { | 
					
						
							|  |  |  |       // We are not intercepting - ready to issue onRequest.
 | 
					
						
							|  |  |  |       this._sendOnRequest(false); | 
					
						
							|  |  |  |       return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     this._expectingInterception = true; | 
					
						
							|  |  |  |     return true; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |   // nsINetworkInterceptController
 | 
					
						
							|  |  |  |   channelIntercepted(intercepted) { | 
					
						
							|  |  |  |     if (!this._expectingInterception) { | 
					
						
							|  |  |  |       // We are not intercepting, fall-through.
 | 
					
						
							|  |  |  |       const interceptController = this._fallThroughInterceptController(); | 
					
						
							|  |  |  |       if (interceptController) | 
					
						
							|  |  |  |         interceptController.channelIntercepted(intercepted); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |       return; | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     this._expectingInterception = false; | 
					
						
							|  |  |  |     this._interceptedChannel = intercepted.QueryInterface(Ci.nsIInterceptedChannel); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-10 14:43:21 -07:00
										 |  |  |     const pageNetwork = this._pageNetwork; | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     if (!pageNetwork) { | 
					
						
							|  |  |  |       // Just in case we disabled instrumentation while intercepting, resume and forget.
 | 
					
						
							|  |  |  |       this.resume(); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |       return; | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const browserContext = pageNetwork._target.browserContext(); | 
					
						
							| 
									
										
										
										
											2023-01-23 11:29:48 -08:00
										 |  |  |     if (browserContext.crossProcessCookie.settings.onlineOverride === 'offline') { | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |       // Implement offline.
 | 
					
						
							|  |  |  |       this.abort(Cr.NS_ERROR_OFFLINE); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |       return; | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Ok, so now we have intercepted the request, let's issue onRequest.
 | 
					
						
							|  |  |  |     // If interception has been disabled while we were intercepting, resume and forget.
 | 
					
						
							|  |  |  |     const interceptionEnabled = this._shouldIntercept(); | 
					
						
							|  |  |  |     this._sendOnRequest(!!interceptionEnabled); | 
					
						
							|  |  |  |     if (interceptionEnabled) | 
					
						
							|  |  |  |       pageNetwork._interceptedRequests.set(this.requestId, this); | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |       this.resume(); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |   // nsIStreamListener
 | 
					
						
							|  |  |  |   onDataAvailable(aRequest, aInputStream, aOffset, aCount) { | 
					
						
							| 
									
										
										
										
											2021-10-15 19:37:00 -07:00
										 |  |  |     // Turns out webcompat shims might redirect to
 | 
					
						
							|  |  |  |     // SimpleChannel, so we get requests from a different channel.
 | 
					
						
							|  |  |  |     // See https://github.com/microsoft/playwright/issues/9418#issuecomment-944836244
 | 
					
						
							|  |  |  |     if (aRequest !== this.httpChannel) | 
					
						
							|  |  |  |       return; | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     // For requests with internal redirect (e.g. intercepted by Service Worker),
 | 
					
						
							|  |  |  |     // we do not get onResponse normally, but we do get nsIStreamListener notifications.
 | 
					
						
							|  |  |  |     this._sendOnResponse(false); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const iStream = new BinaryInputStream(aInputStream); | 
					
						
							|  |  |  |     const sStream = new StorageStream(8192, aCount, null); | 
					
						
							|  |  |  |     const oStream = new BinaryOutputStream(sStream.getOutputStream(0)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Copy received data as they come.
 | 
					
						
							|  |  |  |     const data = iStream.readBytes(aCount); | 
					
						
							|  |  |  |     this._responseBodyChunks.push(data); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     oStream.writeBytes(data, aCount); | 
					
						
							|  |  |  |     try { | 
					
						
							|  |  |  |       this._originalListener.onDataAvailable(aRequest, sStream.newInputStream(0), aOffset, aCount); | 
					
						
							|  |  |  |     } catch (e) { | 
					
						
							|  |  |  |       // Be ready to original listener exceptions.
 | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |   // nsIStreamListener
 | 
					
						
							|  |  |  |   onStartRequest(aRequest) { | 
					
						
							| 
									
										
										
										
											2021-10-15 19:37:00 -07:00
										 |  |  |     // Turns out webcompat shims might redirect to
 | 
					
						
							|  |  |  |     // SimpleChannel, so we get requests from a different channel.
 | 
					
						
							|  |  |  |     // See https://github.com/microsoft/playwright/issues/9418#issuecomment-944836244
 | 
					
						
							|  |  |  |     if (aRequest !== this.httpChannel) | 
					
						
							|  |  |  |       return; | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     try { | 
					
						
							|  |  |  |       this._originalListener.onStartRequest(aRequest); | 
					
						
							|  |  |  |     } catch (e) { | 
					
						
							|  |  |  |       // Be ready to original listener exceptions.
 | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // nsIStreamListener
 | 
					
						
							|  |  |  |   onStopRequest(aRequest, aStatusCode) { | 
					
						
							| 
									
										
										
										
											2021-10-15 19:37:00 -07:00
										 |  |  |     // Turns out webcompat shims might redirect to
 | 
					
						
							|  |  |  |     // SimpleChannel, so we get requests from a different channel.
 | 
					
						
							|  |  |  |     // See https://github.com/microsoft/playwright/issues/9418#issuecomment-944836244
 | 
					
						
							|  |  |  |     if (aRequest !== this.httpChannel) | 
					
						
							|  |  |  |       return; | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     try { | 
					
						
							|  |  |  |       this._originalListener.onStopRequest(aRequest, aStatusCode); | 
					
						
							|  |  |  |     } catch (e) { | 
					
						
							|  |  |  |       // Be ready to original listener exceptions.
 | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (aStatusCode === 0) { | 
					
						
							|  |  |  |       // For requests with internal redirect (e.g. intercepted by Service Worker),
 | 
					
						
							|  |  |  |       // we do not get onResponse normally, but we do get nsIRequestObserver notifications.
 | 
					
						
							|  |  |  |       this._sendOnResponse(false); | 
					
						
							|  |  |  |       const body = this._responseBodyChunks.join(''); | 
					
						
							| 
									
										
										
										
											2021-08-10 14:43:21 -07:00
										 |  |  |       const pageNetwork = this._pageNetwork; | 
					
						
							| 
									
										
										
										
											2021-11-04 12:29:36 -07:00
										 |  |  |       if (pageNetwork) | 
					
						
							|  |  |  |         pageNetwork._responseStorage.addResponseBody(this, body); | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |       this._sendOnRequestFinished(); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |       this._sendOnRequestFailed(aStatusCode); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     delete this._responseBodyChunks; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |   _shouldIntercept() { | 
					
						
							| 
									
										
										
										
											2021-08-10 14:43:21 -07:00
										 |  |  |     const pageNetwork = this._pageNetwork; | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     if (!pageNetwork) | 
					
						
							|  |  |  |       return false; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     if (pageNetwork._requestInterceptionEnabled) | 
					
						
							|  |  |  |       return true; | 
					
						
							|  |  |  |     const browserContext = pageNetwork._target.browserContext(); | 
					
						
							| 
									
										
										
										
											2020-08-19 15:39:46 -07:00
										 |  |  |     if (browserContext.requestInterceptionEnabled) | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |       return true; | 
					
						
							| 
									
										
										
										
											2023-01-23 11:29:48 -08:00
										 |  |  |     if (browserContext.crossProcessCookie.settings.onlineOverride === 'offline') | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |       return true; | 
					
						
							|  |  |  |     return false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |   _fallThroughInterceptController() { | 
					
						
							| 
									
										
										
										
											2023-04-24 21:28:08 +00:00
										 |  |  |     try { | 
					
						
							|  |  |  |       return this._previousCallbacks?.getInterface(Ci.nsINetworkInterceptController); | 
					
						
							|  |  |  |     } catch (e) { | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |       return undefined; | 
					
						
							| 
									
										
										
										
											2023-04-24 21:28:08 +00:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |   _sendOnRequest(isIntercepted) { | 
					
						
							|  |  |  |     // Note: we call _sendOnRequest either after we intercepted the request,
 | 
					
						
							|  |  |  |     // or at the first moment we know that we are not going to intercept.
 | 
					
						
							| 
									
										
										
										
											2021-08-10 14:43:21 -07:00
										 |  |  |     const pageNetwork = this._pageNetwork; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     if (!pageNetwork) | 
					
						
							|  |  |  |       return; | 
					
						
							| 
									
										
										
										
											2020-09-29 11:22:00 -07:00
										 |  |  |     const loadInfo = this.httpChannel.loadInfo; | 
					
						
							|  |  |  |     const causeType = loadInfo?.externalContentPolicyType || Ci.nsIContentPolicy.TYPE_OTHER; | 
					
						
							|  |  |  |     const internalCauseType = loadInfo?.internalContentPolicyType || Ci.nsIContentPolicy.TYPE_OTHER; | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     pageNetwork.emit(PageNetwork.Events.Request, { | 
					
						
							|  |  |  |       url: this.httpChannel.URI.spec, | 
					
						
							| 
									
										
										
										
											2020-10-06 00:15:24 -07:00
										 |  |  |       frameId: this._frameId, | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |       isIntercepted, | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |       requestId: this.requestId, | 
					
						
							|  |  |  |       redirectedFrom: this.redirectedFromId, | 
					
						
							|  |  |  |       postData: readRequestPostData(this.httpChannel), | 
					
						
							|  |  |  |       headers: requestHeaders(this.httpChannel), | 
					
						
							|  |  |  |       method: this.httpChannel.requestMethod, | 
					
						
							|  |  |  |       navigationId: this.navigationId, | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |       cause: causeTypeToString(causeType), | 
					
						
							|  |  |  |       internalCause: causeTypeToString(internalCauseType), | 
					
						
							| 
									
										
										
										
											2020-10-06 00:15:24 -07:00
										 |  |  |     }, this._frameId); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-30 12:59:27 -07:00
										 |  |  |   _sendOnResponse(fromCache, opt_statusCode, opt_statusText) { | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     if (this._sentOnResponse) { | 
					
						
							|  |  |  |       // We can come here twice because of internal redirects, e.g. service workers.
 | 
					
						
							| 
									
										
										
										
											2020-06-16 17:19:01 -07:00
										 |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     this._sentOnResponse = true; | 
					
						
							| 
									
										
										
										
											2021-08-10 14:43:21 -07:00
										 |  |  |     const pageNetwork = this._pageNetwork; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     if (!pageNetwork) | 
					
						
							|  |  |  |       return; | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-04 12:29:36 -07:00
										 |  |  |     this.httpChannel.QueryInterface(Ci.nsIHttpChannelInternal); | 
					
						
							|  |  |  |     this.httpChannel.QueryInterface(Ci.nsITimedChannel); | 
					
						
							| 
									
										
										
										
											2020-10-21 13:55:30 -07:00
										 |  |  |     const timing = { | 
					
						
							| 
									
										
										
										
											2021-11-04 12:29:36 -07:00
										 |  |  |       startTime: this.httpChannel.channelCreationTime, | 
					
						
							|  |  |  |       domainLookupStart: this.httpChannel.domainLookupStartTime, | 
					
						
							|  |  |  |       domainLookupEnd: this.httpChannel.domainLookupEndTime, | 
					
						
							|  |  |  |       connectStart: this.httpChannel.connectStartTime, | 
					
						
							|  |  |  |       secureConnectionStart: this.httpChannel.secureConnectionStartTime, | 
					
						
							|  |  |  |       connectEnd: this.httpChannel.connectEndTime, | 
					
						
							|  |  |  |       requestStart: this.httpChannel.requestStartTime, | 
					
						
							|  |  |  |       responseStart: this.httpChannel.responseStartTime, | 
					
						
							| 
									
										
										
										
											2020-10-21 13:55:30 -07:00
										 |  |  |     }; | 
					
						
							| 
									
										
										
										
											2020-09-09 09:16:03 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-04 12:29:36 -07:00
										 |  |  |     const { status, statusText, headers } = responseHead(this.httpChannel, opt_statusCode, opt_statusText); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     let remoteIPAddress = undefined; | 
					
						
							|  |  |  |     let remotePort = undefined; | 
					
						
							|  |  |  |     try { | 
					
						
							| 
									
										
										
										
											2021-11-04 12:29:36 -07:00
										 |  |  |       remoteIPAddress = this.httpChannel.remoteAddress; | 
					
						
							|  |  |  |       remotePort = this.httpChannel.remotePort; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     } catch (e) { | 
					
						
							|  |  |  |       // remoteAddress is not defined for cached requests.
 | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-02 17:16:19 -07:00
										 |  |  |     const fromServiceWorker = this._networkObserver._channelIdsFulfilledByServiceWorker.has(this.requestId); | 
					
						
							|  |  |  |     this._networkObserver._channelIdsFulfilledByServiceWorker.delete(this.requestId); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     pageNetwork.emit(PageNetwork.Events.Response, { | 
					
						
							|  |  |  |       requestId: this.requestId, | 
					
						
							| 
									
										
										
										
											2021-11-04 12:29:36 -07:00
										 |  |  |       securityDetails: getSecurityDetails(this.httpChannel), | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |       fromCache, | 
					
						
							|  |  |  |       headers, | 
					
						
							|  |  |  |       remoteIPAddress, | 
					
						
							|  |  |  |       remotePort, | 
					
						
							| 
									
										
										
										
											2020-09-09 09:16:03 -07:00
										 |  |  |       status, | 
					
						
							|  |  |  |       statusText, | 
					
						
							| 
									
										
										
										
											2020-10-21 13:55:30 -07:00
										 |  |  |       timing, | 
					
						
							| 
									
										
										
										
											2022-06-02 17:16:19 -07:00
										 |  |  |       fromServiceWorker, | 
					
						
							| 
									
										
										
										
											2020-10-06 00:15:24 -07:00
										 |  |  |     }, this._frameId); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |   _sendOnRequestFailed(error) { | 
					
						
							| 
									
										
										
										
											2021-08-10 14:43:21 -07:00
										 |  |  |     const pageNetwork = this._pageNetwork; | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     if (pageNetwork) { | 
					
						
							|  |  |  |       pageNetwork.emit(PageNetwork.Events.RequestFailed, { | 
					
						
							|  |  |  |         requestId: this.requestId, | 
					
						
							|  |  |  |         errorCode: helper.getNetworkErrorStatusText(error), | 
					
						
							| 
									
										
										
										
											2020-10-06 00:15:24 -07:00
										 |  |  |       }, this._frameId); | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |     this._networkObserver._channelToRequest.delete(this.httpChannel); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _sendOnRequestFinished() { | 
					
						
							| 
									
										
										
										
											2021-08-10 14:43:21 -07:00
										 |  |  |     const pageNetwork = this._pageNetwork; | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     if (pageNetwork) { | 
					
						
							| 
									
										
										
										
											2021-12-28 13:23:53 -07:00
										 |  |  |       let protocolVersion = undefined; | 
					
						
							|  |  |  |       try { | 
					
						
							|  |  |  |         protocolVersion = this.httpChannel.protocolVersion; | 
					
						
							|  |  |  |       } catch (e) { | 
					
						
							|  |  |  |         // protocolVersion is unavailable in certain cases.
 | 
					
						
							|  |  |  |       }; | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |       pageNetwork.emit(PageNetwork.Events.RequestFinished, { | 
					
						
							|  |  |  |         requestId: this.requestId, | 
					
						
							| 
									
										
										
										
											2021-11-04 12:29:36 -07:00
										 |  |  |         responseEndTime: this.httpChannel.responseEndTime, | 
					
						
							|  |  |  |         transferSize: this.httpChannel.transferSize, | 
					
						
							|  |  |  |         encodedBodySize: this.httpChannel.encodedBodySize, | 
					
						
							| 
									
										
										
										
											2021-12-28 13:23:53 -07:00
										 |  |  |         protocolVersion, | 
					
						
							| 
									
										
										
										
											2020-10-06 00:15:24 -07:00
										 |  |  |       }, this._frameId); | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-11-04 12:29:36 -07:00
										 |  |  |     this._networkObserver._channelToRequest.delete(this.httpChannel); | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class NetworkObserver { | 
					
						
							|  |  |  |   static instance() { | 
					
						
							|  |  |  |     return NetworkObserver._instance || null; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   constructor(targetRegistry) { | 
					
						
							| 
									
										
										
										
											2023-01-23 11:29:48 -08:00
										 |  |  |     helper.decorateAsEventEmitter(this); | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     NetworkObserver._instance = this; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     this._targetRegistry = targetRegistry; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     this._channelToRequest = new Map();  // http channel -> network request
 | 
					
						
							|  |  |  |     this._expectedRedirect = new Map();  // expected redirect channel id (string) -> network request
 | 
					
						
							| 
									
										
										
										
											2022-06-02 17:16:19 -07:00
										 |  |  |     this._channelIdsFulfilledByServiceWorker = new Set();  // http channel ids that were fulfilled by service worker
 | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     const protocolProxyService = Cc['@mozilla.org/network/protocol-proxy-service;1'].getService(); | 
					
						
							|  |  |  |     this._channelProxyFilter = { | 
					
						
							|  |  |  |       QueryInterface: ChromeUtils.generateQI([Ci.nsIProtocolProxyChannelFilter]), | 
					
						
							|  |  |  |       applyFilter: (channel, defaultProxyInfo, proxyFilter) => { | 
					
						
							| 
									
										
										
										
											2020-08-07 15:38:06 -07:00
										 |  |  |         const proxy = this._targetRegistry.getProxyInfo(channel); | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |         if (!proxy) { | 
					
						
							|  |  |  |           proxyFilter.onProxyFilterResult(defaultProxyInfo); | 
					
						
							|  |  |  |           return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         proxyFilter.onProxyFilterResult(protocolProxyService.newProxyInfo( | 
					
						
							|  |  |  |             proxy.type, | 
					
						
							|  |  |  |             proxy.host, | 
					
						
							|  |  |  |             proxy.port, | 
					
						
							|  |  |  |             '', /* aProxyAuthorizationHeader */ | 
					
						
							|  |  |  |             '', /* aConnectionIsolationKey */ | 
					
						
							| 
									
										
										
										
											2021-09-06 16:34:28 +02:00
										 |  |  |             Ci.nsIProxyInfo.TRANSPARENT_PROXY_RESOLVES_HOST, /* aFlags */ | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |             UINT32_MAX, /* aFailoverTimeout */ | 
					
						
							|  |  |  |             null, /* failover proxy */ | 
					
						
							|  |  |  |         )); | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |     }; | 
					
						
							|  |  |  |     protocolProxyService.registerChannelFilter(this._channelProxyFilter, 0 /* position */); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Register self as ChannelEventSink to track redirects.
 | 
					
						
							| 
									
										
										
										
											2022-07-05 08:20:01 -07:00
										 |  |  |     ChannelEventSinkFactory.getService().registerCollector({ | 
					
						
							|  |  |  |       _onChannelRedirect: this._onRedirect.bind(this), | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     this._eventListeners = [ | 
					
						
							|  |  |  |       helper.addObserver(this._onRequest.bind(this), 'http-on-modify-request'), | 
					
						
							|  |  |  |       helper.addObserver(this._onResponse.bind(this, false /* fromCache */), 'http-on-examine-response'), | 
					
						
							|  |  |  |       helper.addObserver(this._onResponse.bind(this, true /* fromCache */), 'http-on-examine-cached-response'), | 
					
						
							|  |  |  |       helper.addObserver(this._onResponse.bind(this, true /* fromCache */), 'http-on-examine-merged-response'), | 
					
						
							| 
									
										
										
										
											2022-06-02 17:16:19 -07:00
										 |  |  |       helper.addObserver(this._onServiceWorkerResponse.bind(this), 'service-worker-synthesized-response'), | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     ]; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _expectRedirect(channelId, previous) { | 
					
						
							|  |  |  |     this._expectedRedirect.set(channelId, previous); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _onRedirect(oldChannel, newChannel, flags) { | 
					
						
							|  |  |  |     if (!(oldChannel instanceof Ci.nsIHttpChannel) || !(newChannel instanceof Ci.nsIHttpChannel)) | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     const oldHttpChannel = oldChannel.QueryInterface(Ci.nsIHttpChannel); | 
					
						
							|  |  |  |     const newHttpChannel = newChannel.QueryInterface(Ci.nsIHttpChannel); | 
					
						
							| 
									
										
										
										
											2021-06-30 12:59:27 -07:00
										 |  |  |     const request = this._channelToRequest.get(oldHttpChannel); | 
					
						
							|  |  |  |     if (flags & Ci.nsIChannelEventSink.REDIRECT_INTERNAL) { | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |       if (request) | 
					
						
							|  |  |  |         request._onInternalRedirect(newHttpChannel); | 
					
						
							| 
									
										
										
										
											2021-06-30 12:59:27 -07:00
										 |  |  |     } else if (flags & Ci.nsIChannelEventSink.REDIRECT_STS_UPGRADE) { | 
					
						
							|  |  |  |       if (request) { | 
					
						
							|  |  |  |         // This is an internal HSTS upgrade. The original http request is canceled, and a new
 | 
					
						
							|  |  |  |         // equivalent https request is sent. We forge 307 redirect to follow Chromium here:
 | 
					
						
							|  |  |  |         // https://source.chromium.org/chromium/chromium/src/+/main:net/url_request/url_request_http_job.cc;l=211
 | 
					
						
							|  |  |  |         request._sendOnResponse(false, 307, 'Temporary Redirect'); | 
					
						
							|  |  |  |         this._expectRedirect(newHttpChannel.channelId + '', request); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       if (request) | 
					
						
							|  |  |  |         this._expectRedirect(newHttpChannel.channelId + '', request); | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _onRequest(channel, topic) { | 
					
						
							|  |  |  |     if (!(channel instanceof Ci.nsIHttpChannel)) | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |       return; | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     const httpChannel = channel.QueryInterface(Ci.nsIHttpChannel); | 
					
						
							|  |  |  |     const channelId = httpChannel.channelId + ''; | 
					
						
							|  |  |  |     const redirectedFrom = this._expectedRedirect.get(channelId); | 
					
						
							|  |  |  |     if (redirectedFrom) { | 
					
						
							|  |  |  |       this._expectedRedirect.delete(channelId); | 
					
						
							|  |  |  |       new NetworkRequest(this, httpChannel, redirectedFrom); | 
					
						
							|  |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2020-07-28 11:32:44 -07:00
										 |  |  |       const redirectedRequest = this._channelToRequest.get(httpChannel); | 
					
						
							|  |  |  |       if (redirectedRequest) | 
					
						
							|  |  |  |         redirectedRequest._onInternalRedirectReady(); | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         new NetworkRequest(this, httpChannel); | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   _onResponse(fromCache, httpChannel, topic) { | 
					
						
							|  |  |  |     const request = this._channelToRequest.get(httpChannel); | 
					
						
							|  |  |  |     if (request) | 
					
						
							| 
									
										
										
										
											2021-08-10 14:43:21 -07:00
										 |  |  |       request._sendOnResponse(fromCache); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-02 17:16:19 -07:00
										 |  |  |   _onServiceWorkerResponse(channel, topic) { | 
					
						
							|  |  |  |     if (!(channel instanceof Ci.nsIHttpChannel)) | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     const httpChannel = channel.QueryInterface(Ci.nsIHttpChannel); | 
					
						
							|  |  |  |     const channelId = httpChannel.channelId + ''; | 
					
						
							|  |  |  |     this._channelIdsFulfilledByServiceWorker.add(channelId); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   dispose() { | 
					
						
							|  |  |  |     this._activityDistributor.removeObserver(this); | 
					
						
							| 
									
										
										
										
											2022-07-05 08:20:01 -07:00
										 |  |  |     ChannelEventSinkFactory.unregister(); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     helper.removeListeners(this._eventListeners); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const protocolVersionNames = { | 
					
						
							|  |  |  |   [Ci.nsITransportSecurityInfo.TLS_VERSION_1]: 'TLS 1', | 
					
						
							|  |  |  |   [Ci.nsITransportSecurityInfo.TLS_VERSION_1_1]: 'TLS 1.1', | 
					
						
							|  |  |  |   [Ci.nsITransportSecurityInfo.TLS_VERSION_1_2]: 'TLS 1.2', | 
					
						
							|  |  |  |   [Ci.nsITransportSecurityInfo.TLS_VERSION_1_3]: 'TLS 1.3', | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function getSecurityDetails(httpChannel) { | 
					
						
							|  |  |  |   const securityInfo = httpChannel.securityInfo; | 
					
						
							|  |  |  |   if (!securityInfo) | 
					
						
							|  |  |  |     return null; | 
					
						
							|  |  |  |   securityInfo.QueryInterface(Ci.nsITransportSecurityInfo); | 
					
						
							|  |  |  |   if (!securityInfo.serverCert) | 
					
						
							|  |  |  |     return null; | 
					
						
							|  |  |  |   return { | 
					
						
							|  |  |  |     protocol: protocolVersionNames[securityInfo.protocolVersion] || '<unknown>', | 
					
						
							|  |  |  |     subjectName: securityInfo.serverCert.commonName, | 
					
						
							|  |  |  |     issuer: securityInfo.serverCert.issuerCommonName, | 
					
						
							|  |  |  |     // Convert to seconds.
 | 
					
						
							|  |  |  |     validFrom: securityInfo.serverCert.validity.notBefore / 1000 / 1000, | 
					
						
							|  |  |  |     validTo: securityInfo.serverCert.validity.notAfter / 1000 / 1000, | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function readRequestPostData(httpChannel) { | 
					
						
							|  |  |  |   if (!(httpChannel instanceof Ci.nsIUploadChannel)) | 
					
						
							|  |  |  |     return undefined; | 
					
						
							| 
									
										
										
										
											2021-05-12 16:27:53 -07:00
										 |  |  |   let iStream = httpChannel.uploadStream; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   if (!iStream) | 
					
						
							|  |  |  |     return undefined; | 
					
						
							|  |  |  |   const isSeekableStream = iStream instanceof Ci.nsISeekableStream; | 
					
						
							| 
									
										
										
										
											2023-09-11 18:16:33 -07:00
										 |  |  |   const isTellableStream = iStream instanceof Ci.nsITellableStream; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-12 16:27:53 -07:00
										 |  |  |   // For some reason, we cannot rewind back big streams,
 | 
					
						
							|  |  |  |   // so instead we should clone them.
 | 
					
						
							|  |  |  |   const isCloneable = iStream instanceof Ci.nsICloneableInputStream; | 
					
						
							|  |  |  |   if (isCloneable) | 
					
						
							|  |  |  |     iStream = iStream.clone(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   let prevOffset; | 
					
						
							| 
									
										
										
										
											2023-09-11 18:16:33 -07:00
										 |  |  |   // Surprisingly, stream might implement `nsITellableStream` without
 | 
					
						
							|  |  |  |   // implementing the `tell` method.
 | 
					
						
							|  |  |  |   if (isSeekableStream && isTellableStream && iStream.tell) { | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     prevOffset = iStream.tell(); | 
					
						
							|  |  |  |     iStream.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Read data from the stream.
 | 
					
						
							| 
									
										
										
										
											2022-03-23 08:58:29 -07:00
										 |  |  |   let result = undefined; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   try { | 
					
						
							| 
									
										
										
										
											2022-03-23 08:58:29 -07:00
										 |  |  |     const maxLen = iStream.available(); | 
					
						
							|  |  |  |     // Cap at 10Mb.
 | 
					
						
							|  |  |  |     if (maxLen <= 10 * 1024 * 1024) { | 
					
						
							|  |  |  |       const buffer = NetUtil.readInputStreamToString(iStream, maxLen); | 
					
						
							|  |  |  |       result = btoa(buffer); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |   } catch (err) { | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // Seek locks the file, so seek to the beginning only if necko hasn't
 | 
					
						
							|  |  |  |   // read it yet, since necko doesn't seek to 0 before reading (at lest
 | 
					
						
							|  |  |  |   // not till 459384 is fixed).
 | 
					
						
							| 
									
										
										
										
											2021-05-12 16:27:53 -07:00
										 |  |  |   if (isSeekableStream && prevOffset == 0 && !isCloneable) | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     iStream.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0); | 
					
						
							| 
									
										
										
										
											2020-07-21 09:55:46 -07:00
										 |  |  |   return result; | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function requestHeaders(httpChannel) { | 
					
						
							|  |  |  |   const headers = []; | 
					
						
							|  |  |  |   httpChannel.visitRequestHeaders({ | 
					
						
							|  |  |  |     visitHeader: (name, value) => headers.push({name, value}), | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   return headers; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function causeTypeToString(causeType) { | 
					
						
							|  |  |  |   for (let key in Ci.nsIContentPolicy) { | 
					
						
							|  |  |  |     if (Ci.nsIContentPolicy[key] === causeType) | 
					
						
							|  |  |  |       return key; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return 'TYPE_OTHER'; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  | function appendExtraHTTPHeaders(httpChannel, headers) { | 
					
						
							|  |  |  |   if (!headers) | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   for (const header of headers) | 
					
						
							|  |  |  |     httpChannel.setRequestHeader(header.name, header.value, false /* merge */); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  | class ResponseStorage { | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |   constructor(maxTotalSize, maxResponseSize) { | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     this._totalSize = 0; | 
					
						
							|  |  |  |     this._maxResponseSize = maxResponseSize; | 
					
						
							|  |  |  |     this._maxTotalSize = maxTotalSize; | 
					
						
							|  |  |  |     this._responses = new Map(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-04 12:29:36 -07:00
										 |  |  |   addResponseBody(request, body) { | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     if (body.length > this._maxResponseSize) { | 
					
						
							| 
									
										
										
										
											2020-11-16 13:16:20 -08:00
										 |  |  |       this._responses.set(request.requestId, { | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |         evicted: true, | 
					
						
							|  |  |  |         body: '', | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     let encodings = []; | 
					
						
							| 
									
										
										
										
											2021-11-04 12:29:36 -07:00
										 |  |  |     if ((request.httpChannel instanceof Ci.nsIEncodedChannel) && request.httpChannel.contentEncodings && !request.httpChannel.applyConversion) { | 
					
						
							|  |  |  |       const encodingHeader = request.httpChannel.getResponseHeader("Content-Encoding"); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |       encodings = encodingHeader.split(/\s*\t*,\s*\t*/); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-06-20 20:45:01 -07:00
										 |  |  |     this._responses.set(request.requestId, {body, encodings}); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     this._totalSize += body.length; | 
					
						
							|  |  |  |     if (this._totalSize > this._maxTotalSize) { | 
					
						
							|  |  |  |       for (let [requestId, response] of this._responses) { | 
					
						
							|  |  |  |         this._totalSize -= response.body.length; | 
					
						
							|  |  |  |         response.body = ''; | 
					
						
							|  |  |  |         response.evicted = true; | 
					
						
							|  |  |  |         if (this._totalSize < this._maxTotalSize) | 
					
						
							|  |  |  |           break; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   getBase64EncodedResponse(requestId) { | 
					
						
							|  |  |  |     const response = this._responses.get(requestId); | 
					
						
							|  |  |  |     if (!response) | 
					
						
							|  |  |  |       throw new Error(`Request "${requestId}" is not found`); | 
					
						
							|  |  |  |     if (response.evicted) | 
					
						
							|  |  |  |       return {base64body: '', evicted: true}; | 
					
						
							|  |  |  |     let result = response.body; | 
					
						
							|  |  |  |     if (response.encodings && response.encodings.length) { | 
					
						
							|  |  |  |       for (const encoding of response.encodings) | 
					
						
							| 
									
										
										
										
											2020-06-22 16:01:16 -07:00
										 |  |  |         result = convertString(result, encoding, 'uncompressed'); | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |     return {base64body: btoa(result)}; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-09 05:41:53 -07:00
										 |  |  | function responseHead(httpChannel, opt_statusCode, opt_statusText) { | 
					
						
							|  |  |  |   const headers = []; | 
					
						
							|  |  |  |   let status = opt_statusCode || 0; | 
					
						
							|  |  |  |   let statusText = opt_statusText || ''; | 
					
						
							|  |  |  |   try { | 
					
						
							|  |  |  |     status = httpChannel.responseStatus; | 
					
						
							|  |  |  |     statusText = httpChannel.responseStatusText; | 
					
						
							|  |  |  |     httpChannel.visitResponseHeaders({ | 
					
						
							|  |  |  |       visitHeader: (name, value) => headers.push({name, value}), | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } catch (e) { | 
					
						
							|  |  |  |     // Response headers, status and/or statusText are not available
 | 
					
						
							|  |  |  |     // when redirect did not actually hit the network.
 | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   return { status, statusText, headers }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function setPostData(httpChannel, postData, headers) { | 
					
						
							|  |  |  |   if (!(httpChannel instanceof Ci.nsIUploadChannel2)) | 
					
						
							|  |  |  |     return; | 
					
						
							|  |  |  |   const synthesized = Cc["@mozilla.org/io/string-input-stream;1"].createInstance(Ci.nsIStringInputStream); | 
					
						
							|  |  |  |   const body = atob(postData); | 
					
						
							|  |  |  |   synthesized.setData(body, body.length); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-23 08:51:09 -07:00
										 |  |  |   const overriddenHeader = (lowerCaseName) => { | 
					
						
							| 
									
										
										
										
											2021-07-09 05:41:53 -07:00
										 |  |  |     if (headers) { | 
					
						
							|  |  |  |       for (const header of headers) { | 
					
						
							|  |  |  |         if (header.name.toLowerCase() === lowerCaseName) { | 
					
						
							|  |  |  |           return header.value; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-08-23 08:51:09 -07:00
										 |  |  |     return undefined; | 
					
						
							| 
									
										
										
										
											2021-07-09 05:41:53 -07:00
										 |  |  |   } | 
					
						
							|  |  |  |   // Clear content-length, so that upload stream resets it.
 | 
					
						
							| 
									
										
										
										
											2022-08-02 13:51:05 -07:00
										 |  |  |   httpChannel.setRequestHeader('content-length', '', false /* merge */); | 
					
						
							| 
									
										
										
										
											2022-08-23 08:51:09 -07:00
										 |  |  |   let contentType = overriddenHeader('content-type'); | 
					
						
							|  |  |  |   if (contentType === undefined) { | 
					
						
							|  |  |  |     try { | 
					
						
							|  |  |  |       contentType = httpChannel.getRequestHeader('content-type'); | 
					
						
							|  |  |  |     } catch (e) { | 
					
						
							|  |  |  |       if (e.result == Cr.NS_ERROR_NOT_AVAILABLE) | 
					
						
							|  |  |  |         contentType =  'application/octet-stream'; | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         throw e; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   httpChannel.explicitSetUploadStream(synthesized, contentType, -1, httpChannel.requestMethod, false); | 
					
						
							| 
									
										
										
										
											2021-07-09 05:41:53 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-22 16:01:16 -07:00
										 |  |  | function convertString(s, source, dest) { | 
					
						
							|  |  |  |   const is = Cc["@mozilla.org/io/string-input-stream;1"].createInstance( | 
					
						
							|  |  |  |     Ci.nsIStringInputStream | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  |   is.setData(s, s.length); | 
					
						
							|  |  |  |   const listener = Cc["@mozilla.org/network/stream-loader;1"].createInstance( | 
					
						
							|  |  |  |     Ci.nsIStreamLoader | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  |   let result = []; | 
					
						
							|  |  |  |   listener.init({ | 
					
						
							|  |  |  |     onStreamComplete: function onStreamComplete( | 
					
						
							|  |  |  |       loader, | 
					
						
							|  |  |  |       context, | 
					
						
							|  |  |  |       status, | 
					
						
							|  |  |  |       length, | 
					
						
							|  |  |  |       data | 
					
						
							|  |  |  |     ) { | 
					
						
							|  |  |  |       const array = Array.from(data); | 
					
						
							|  |  |  |       const kChunk = 100000; | 
					
						
							|  |  |  |       for (let i = 0; i < length; i += kChunk) { | 
					
						
							|  |  |  |         const len = Math.min(kChunk, length - i); | 
					
						
							|  |  |  |         const chunk = String.fromCharCode.apply(this, array.slice(i, i + len)); | 
					
						
							|  |  |  |         result.push(chunk); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }, | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   const converter = Cc["@mozilla.org/streamConverters;1"].getService( | 
					
						
							|  |  |  |     Ci.nsIStreamConverterService | 
					
						
							|  |  |  |   ).asyncConvertData( | 
					
						
							|  |  |  |     source, | 
					
						
							|  |  |  |     dest, | 
					
						
							|  |  |  |     listener, | 
					
						
							|  |  |  |     null | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  |   converter.onStartRequest(null, null); | 
					
						
							|  |  |  |   converter.onDataAvailable(null, is, 0, s.length); | 
					
						
							|  |  |  |   converter.onStopRequest(null, null, null); | 
					
						
							|  |  |  |   return result.join(''); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-02 16:51:13 -07:00
										 |  |  | const errorMap = { | 
					
						
							|  |  |  |   'aborted': Cr.NS_ERROR_ABORT, | 
					
						
							|  |  |  |   'accessdenied': Cr.NS_ERROR_PORT_ACCESS_NOT_ALLOWED, | 
					
						
							|  |  |  |   'addressunreachable': Cr.NS_ERROR_UNKNOWN_HOST, | 
					
						
							|  |  |  |   'blockedbyclient': Cr.NS_ERROR_FAILURE, | 
					
						
							|  |  |  |   'blockedbyresponse': Cr.NS_ERROR_FAILURE, | 
					
						
							|  |  |  |   'connectionaborted': Cr.NS_ERROR_NET_INTERRUPT, | 
					
						
							|  |  |  |   'connectionclosed': Cr.NS_ERROR_FAILURE, | 
					
						
							|  |  |  |   'connectionfailed': Cr.NS_ERROR_FAILURE, | 
					
						
							|  |  |  |   'connectionrefused': Cr.NS_ERROR_CONNECTION_REFUSED, | 
					
						
							|  |  |  |   'connectionreset': Cr.NS_ERROR_NET_RESET, | 
					
						
							|  |  |  |   'internetdisconnected': Cr.NS_ERROR_OFFLINE, | 
					
						
							|  |  |  |   'namenotresolved': Cr.NS_ERROR_UNKNOWN_HOST, | 
					
						
							|  |  |  |   'timedout': Cr.NS_ERROR_NET_TIMEOUT, | 
					
						
							|  |  |  |   'failed': Cr.NS_ERROR_FAILURE, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | PageNetwork.Events = { | 
					
						
							|  |  |  |   Request: Symbol('PageNetwork.Events.Request'), | 
					
						
							|  |  |  |   Response: Symbol('PageNetwork.Events.Response'), | 
					
						
							|  |  |  |   RequestFinished: Symbol('PageNetwork.Events.RequestFinished'), | 
					
						
							|  |  |  |   RequestFailed: Symbol('PageNetwork.Events.RequestFailed'), | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var EXPORTED_SYMBOLS = ['NetworkObserver', 'PageNetwork']; | 
					
						
							|  |  |  | this.NetworkObserver = NetworkObserver; | 
					
						
							|  |  |  | this.PageNetwork = PageNetwork; |