browser(firefox): manage network activity per page (#1700)
a33663a362
Network events are now sent to corresponding page session. Previously they would be broadcast to all sessions.
This commit is contained in:
parent
20ff327827
commit
aff2ffacf8
|
|
@ -1 +1 @@
|
|||
1074
|
||||
1075
|
||||
|
|
|
|||
|
|
@ -1269,10 +1269,10 @@ index 0000000000000000000000000000000000000000..b8e6649fb91be6cd72b000426fb4d582
|
|||
+
|
||||
diff --git a/juggler/NetworkObserver.js b/juggler/NetworkObserver.js
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..052f893eb0e984914ac59f8cb24580449dc12a66
|
||||
index 0000000000000000000000000000000000000000..b8a98b058ea36f85fddfa21e992e2d0674c11e3d
|
||||
--- /dev/null
|
||||
+++ b/juggler/NetworkObserver.js
|
||||
@@ -0,0 +1,760 @@
|
||||
@@ -0,0 +1,789 @@
|
||||
+"use strict";
|
||||
+
|
||||
+const {EventEmitter} = ChromeUtils.import('resource://gre/modules/EventEmitter.jsm');
|
||||
|
|
@ -1305,6 +1305,105 @@ index 0000000000000000000000000000000000000000..052f893eb0e984914ac59f8cb2458044
|
|||
+const SINK_CONTRACT_ID = "@mozilla.org/network/monitor/channeleventsink;1";
|
||||
+const SINK_CATEGORY_NAME = "net-channel-event-sinks";
|
||||
+
|
||||
+const pageNetworkSymbol = Symbol('PageNetwork');
|
||||
+
|
||||
+class PageNetwork {
|
||||
+ static _forPageTarget(networkObserver, target) {
|
||||
+ let result = target[pageNetworkSymbol];
|
||||
+ if (!result) {
|
||||
+ result = new PageNetwork(networkObserver, target);
|
||||
+ target[pageNetworkSymbol] = result;
|
||||
+ }
|
||||
+ return result;
|
||||
+ }
|
||||
+
|
||||
+ constructor(networkObserver, target) {
|
||||
+ EventEmitter.decorate(this);
|
||||
+ this._networkObserver = networkObserver;
|
||||
+ this._target = target;
|
||||
+ this._sessionCount = 0;
|
||||
+ this._extraHTTPHeaders = null;
|
||||
+ this._responseStorage = null;
|
||||
+ this._requestInterceptionEnabled = false;
|
||||
+ this._requestIdToInterceptor = null;
|
||||
+ }
|
||||
+
|
||||
+ addSession() {
|
||||
+ if (this._sessionCount === 0) {
|
||||
+ this._responseStorage = new ResponseStorage(this._networkObserver, MAX_RESPONSE_STORAGE_SIZE, MAX_RESPONSE_STORAGE_SIZE / 10);
|
||||
+ }
|
||||
+ ++this._sessionCount;
|
||||
+ return () => this._stopTracking();
|
||||
+ }
|
||||
+
|
||||
+ _stopTracking() {
|
||||
+ --this._sessionCount;
|
||||
+ if (this._sessionCount === 0) {
|
||||
+ this._extraHTTPHeaders = null;
|
||||
+ this._responseStorage = null;
|
||||
+ this._requestInterceptionEnabled = false;
|
||||
+ this._requestIdToInterceptor = null;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ _isActive() {
|
||||
+ return this._sessionCount > 0;
|
||||
+ }
|
||||
+
|
||||
+ setExtraHTTPHeaders(headers) {
|
||||
+ this._extraHTTPHeaders = headers;
|
||||
+ }
|
||||
+
|
||||
+ enableRequestInterception() {
|
||||
+ this._requestInterceptionEnabled = true;
|
||||
+ }
|
||||
+
|
||||
+ disableRequestInterception() {
|
||||
+ this._requestInterceptionEnabled = false;
|
||||
+ const interceptors = this._requestIdToInterceptor;
|
||||
+ if (!interceptors)
|
||||
+ return;
|
||||
+ this._requestIdToInterceptor = null;
|
||||
+ for (const interceptor of interceptors.values())
|
||||
+ interceptor._resume();
|
||||
+ }
|
||||
+
|
||||
+ resumeInterceptedRequest(requestId, method, headers, postData) {
|
||||
+ this._takeInterceptor(requestId)._resume(method, headers, postData);
|
||||
+ }
|
||||
+
|
||||
+ fulfillInterceptedRequest(requestId, status, statusText, headers, base64body) {
|
||||
+ this._takeInterceptor(requestId)._fulfill(status, statusText, headers, base64body);
|
||||
+ }
|
||||
+
|
||||
+ abortInterceptedRequest(requestId, errorCode) {
|
||||
+ this._takeInterceptor(requestId)._abort(errorCode);
|
||||
+ }
|
||||
+
|
||||
+ getResponseBody(requestId) {
|
||||
+ if (!this._responseStorage)
|
||||
+ throw new Error('Responses are not tracked for the given browser');
|
||||
+ return this._responseStorage.getBase64EncodedResponse(requestId);
|
||||
+ }
|
||||
+
|
||||
+ _ensureInterceptors() {
|
||||
+ if (!this._requestIdToInterceptor)
|
||||
+ this._requestIdToInterceptor = new Map();
|
||||
+ return this._requestIdToInterceptor;
|
||||
+ }
|
||||
+
|
||||
+ _takeInterceptor(requestId) {
|
||||
+ const interceptors = this._requestIdToInterceptor;
|
||||
+ if (!interceptors)
|
||||
+ throw new Error(`Request interception is not enabled`);
|
||||
+ const interceptor = interceptors.get(requestId);
|
||||
+ if (!interceptor)
|
||||
+ throw new Error(`Cannot find request "${requestId}"`);
|
||||
+ interceptors.delete(requestId);
|
||||
+ return interceptor;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+class NetworkObserver {
|
||||
+ static instance() {
|
||||
+ return NetworkObserver._instance || null;
|
||||
|
|
@ -1315,7 +1414,6 @@ index 0000000000000000000000000000000000000000..052f893eb0e984914ac59f8cb2458044
|
|||
+ NetworkObserver._instance = this;
|
||||
+
|
||||
+ this._targetRegistry = targetRegistry;
|
||||
+ this._browserSessionCount = new Map();
|
||||
+ this._activityDistributor = Cc["@mozilla.org/network/http-activity-distributor;1"].getService(Ci.nsIHttpActivityDistributor);
|
||||
+ this._activityDistributor.addObserver(this);
|
||||
+
|
||||
|
|
@ -1342,11 +1440,6 @@ index 0000000000000000000000000000000000000000..052f893eb0e984914ac59f8cb2458044
|
|||
+ registrar.registerFactory(SINK_CLASS_ID, SINK_CLASS_DESCRIPTION, SINK_CONTRACT_ID, this._channelSinkFactory);
|
||||
+ Services.catMan.addCategoryEntry(SINK_CATEGORY_NAME, SINK_CONTRACT_ID, SINK_CONTRACT_ID, false, true);
|
||||
+
|
||||
+ this._browsersWithEnabledInterception = new Set();
|
||||
+ this._browserInterceptors = new Map(); // Browser => (requestId => interceptor).
|
||||
+ this._extraHTTPHeaders = new Map();
|
||||
+ this._browserResponseStorages = new Map();
|
||||
+
|
||||
+ this._eventListeners = [
|
||||
+ helper.addObserver(this._onRequest.bind(this), 'http-on-modify-request'),
|
||||
+ helper.addObserver(this._onResponse.bind(this, false /* fromCache */), 'http-on-examine-response'),
|
||||
|
|
@ -1355,57 +1448,6 @@ index 0000000000000000000000000000000000000000..052f893eb0e984914ac59f8cb2458044
|
|||
+ ];
|
||||
+ }
|
||||
+
|
||||
+ setExtraHTTPHeaders(browser, headers) {
|
||||
+ if (!headers)
|
||||
+ this._extraHTTPHeaders.delete(browser);
|
||||
+ else
|
||||
+ this._extraHTTPHeaders.set(browser, headers);
|
||||
+ }
|
||||
+
|
||||
+ enableRequestInterception(browser) {
|
||||
+ this._browsersWithEnabledInterception.add(browser);
|
||||
+ }
|
||||
+
|
||||
+ disableRequestInterception(browser) {
|
||||
+ this._browsersWithEnabledInterception.delete(browser);
|
||||
+ const interceptors = this._browserInterceptors.get(browser);
|
||||
+ if (!interceptors)
|
||||
+ return;
|
||||
+ this._browserInterceptors.delete(browser);
|
||||
+ for (const interceptor of interceptors.values())
|
||||
+ interceptor._resume();
|
||||
+ }
|
||||
+
|
||||
+ _takeInterceptor(browser, requestId) {
|
||||
+ const interceptors = this._browserInterceptors.get(browser);
|
||||
+ if (!interceptors)
|
||||
+ throw new Error(`Request interception is not enabled`);
|
||||
+ const interceptor = interceptors.get(requestId);
|
||||
+ if (!interceptor)
|
||||
+ throw new Error(`Cannot find request "${requestId}"`);
|
||||
+ interceptors.delete(requestId);
|
||||
+ return interceptor;
|
||||
+ }
|
||||
+
|
||||
+ resumeInterceptedRequest(browser, requestId, method, headers, postData) {
|
||||
+ this._takeInterceptor(browser, requestId)._resume(method, headers, postData);
|
||||
+ }
|
||||
+
|
||||
+ getResponseBody(browser, requestId) {
|
||||
+ const responseStorage = this._browserResponseStorages.get(browser);
|
||||
+ if (!responseStorage)
|
||||
+ throw new Error('Responses are not tracked for the given browser');
|
||||
+ return responseStorage.getBase64EncodedResponse(requestId);
|
||||
+ }
|
||||
+
|
||||
+ fulfillInterceptedRequest(browser, requestId, status, statusText, headers, base64body) {
|
||||
+ this._takeInterceptor(browser, requestId)._fulfill(status, statusText, headers, base64body);
|
||||
+ }
|
||||
+
|
||||
+ abortInterceptedRequest(browser, requestId, errorCode) {
|
||||
+ this._takeInterceptor(browser, requestId)._abort(errorCode);
|
||||
+ }
|
||||
+
|
||||
+ _requestAuthenticated(httpChannel) {
|
||||
+ this._pendingAuthentication.add(httpChannel.channelId + '');
|
||||
+ }
|
||||
|
|
@ -1425,8 +1467,8 @@ index 0000000000000000000000000000000000000000..052f893eb0e984914ac59f8cb2458044
|
|||
+ return;
|
||||
+ const oldHttpChannel = oldChannel.QueryInterface(Ci.nsIHttpChannel);
|
||||
+ const newHttpChannel = newChannel.QueryInterface(Ci.nsIHttpChannel);
|
||||
+ const browser = this._getBrowserForChannel(oldHttpChannel);
|
||||
+ if (!browser)
|
||||
+ const pageNetwork = this._pageNetworkForChannel(oldHttpChannel);
|
||||
+ if (!pageNetwork)
|
||||
+ return;
|
||||
+ const oldRequestId = this._requestId(oldHttpChannel);
|
||||
+ const newRequestId = this._requestId(newHttpChannel);
|
||||
|
|
@ -1462,8 +1504,8 @@ index 0000000000000000000000000000000000000000..052f893eb0e984914ac59f8cb2458044
|
|||
+ if (!(channel instanceof Ci.nsIHttpChannel))
|
||||
+ return;
|
||||
+ const httpChannel = channel.QueryInterface(Ci.nsIHttpChannel);
|
||||
+ const browser = this._getBrowserForChannel(httpChannel);
|
||||
+ if (!browser)
|
||||
+ const pageNetwork = this._pageNetworkForChannel(httpChannel);
|
||||
+ if (!pageNetwork)
|
||||
+ return;
|
||||
+ if (activitySubtype !== Ci.nsIHttpActivityObserver.ACTIVITY_SUBTYPE_TRANSACTION_CLOSE)
|
||||
+ return;
|
||||
|
|
@ -1471,14 +1513,24 @@ index 0000000000000000000000000000000000000000..052f893eb0e984914ac59f8cb2458044
|
|||
+ return;
|
||||
+ if (this._requestIdBeforeAuthentication(httpChannel))
|
||||
+ return;
|
||||
+ this._sendOnRequestFinished(httpChannel);
|
||||
+ this._sendOnRequestFinished(pageNetwork, httpChannel);
|
||||
+ }
|
||||
+
|
||||
+ _getBrowserForChannel(httpChannel) {
|
||||
+ pageNetworkForTarget(target) {
|
||||
+ return PageNetwork._forPageTarget(this, target);
|
||||
+ }
|
||||
+
|
||||
+ _pageNetworkForChannel(httpChannel) {
|
||||
+ let loadContext = helper.getLoadContext(httpChannel);
|
||||
+ if (!loadContext || !this._browserSessionCount.has(loadContext.topFrameElement))
|
||||
+ if (!loadContext)
|
||||
+ return;
|
||||
+ return loadContext.topFrameElement;
|
||||
+ const target = this._targetRegistry.targetForBrowser(loadContext.topFrameElement);
|
||||
+ if (!target)
|
||||
+ return;
|
||||
+ const pageNetwork = PageNetwork._forPageTarget(this, target);
|
||||
+ if (!pageNetwork._isActive())
|
||||
+ return;
|
||||
+ return pageNetwork;
|
||||
+ }
|
||||
+
|
||||
+ _isResumedChannel(httpChannel) {
|
||||
|
|
@ -1489,12 +1541,12 @@ index 0000000000000000000000000000000000000000..052f893eb0e984914ac59f8cb2458044
|
|||
+ if (!(channel instanceof Ci.nsIHttpChannel))
|
||||
+ return;
|
||||
+ const httpChannel = channel.QueryInterface(Ci.nsIHttpChannel);
|
||||
+ const browser = this._getBrowserForChannel(httpChannel);
|
||||
+ if (!browser)
|
||||
+ const pageNetwork = this._pageNetworkForChannel(httpChannel);
|
||||
+ if (!pageNetwork)
|
||||
+ return;
|
||||
+ if (this._isResumedChannel(httpChannel)) {
|
||||
+ // Ignore onRequest for resumed requests, but listen to their response.
|
||||
+ new ResponseBodyListener(this, browser, httpChannel);
|
||||
+ new ResponseBodyListener(this, pageNetwork, httpChannel);
|
||||
+ return;
|
||||
+ }
|
||||
+ // Convert pending auth bit into auth mapping.
|
||||
|
|
@ -1507,54 +1559,54 @@ index 0000000000000000000000000000000000000000..052f893eb0e984914ac59f8cb2458044
|
|||
+ if (bodyListener)
|
||||
+ bodyListener.dispose();
|
||||
+ }
|
||||
+ const browserContext = this._targetRegistry.browserContextForBrowser(browser);
|
||||
+ const browserContext = pageNetwork._target.browserContext();
|
||||
+ if (browserContext)
|
||||
+ this._appendExtraHTTPHeaders(httpChannel, browserContext.options.extraHTTPHeaders);
|
||||
+ this._appendExtraHTTPHeaders(httpChannel, this._extraHTTPHeaders.get(browser));
|
||||
+ this._appendExtraHTTPHeaders(httpChannel, pageNetwork._extraHTTPHeaders);
|
||||
+ const requestId = this._requestId(httpChannel);
|
||||
+ const isRedirect = this._redirectMap.has(requestId);
|
||||
+ const interceptionEnabled = this._isInterceptionEnabledForBrowser(browser);
|
||||
+ const interceptionEnabled = this._isInterceptionEnabledForPage(pageNetwork);
|
||||
+ if (!interceptionEnabled) {
|
||||
+ new NotificationCallbacks(this, browser, httpChannel, false);
|
||||
+ new NotificationCallbacks(this, pageNetwork, httpChannel, false);
|
||||
+ this._sendOnRequest(httpChannel, false);
|
||||
+ new ResponseBodyListener(this, browser, httpChannel);
|
||||
+ new ResponseBodyListener(this, pageNetwork, httpChannel);
|
||||
+ } else if (isRedirect) {
|
||||
+ // We pretend that redirect is interceptable in the protocol, although it's actually not
|
||||
+ // and therefore we do not instantiate the interceptor.
|
||||
+ // TODO: look into REDIRECT_MODE_MANUAL.
|
||||
+ const interceptors = this._ensureInterceptors(browser);
|
||||
+ const interceptors = pageNetwork._ensureInterceptors();
|
||||
+ interceptors.set(requestId, {
|
||||
+ _resume: () => {},
|
||||
+ _abort: () => {},
|
||||
+ _fulfill: () => {},
|
||||
+ });
|
||||
+ new NotificationCallbacks(this, browser, httpChannel, false);
|
||||
+ new NotificationCallbacks(this, pageNetwork, httpChannel, false);
|
||||
+ this._sendOnRequest(httpChannel, true);
|
||||
+ new ResponseBodyListener(this, browser, httpChannel);
|
||||
+ new ResponseBodyListener(this, pageNetwork, httpChannel);
|
||||
+ } else {
|
||||
+ const previousCallbacks = httpChannel.notificationCallbacks;
|
||||
+ if (previousCallbacks instanceof Ci.nsIInterfaceRequestor) {
|
||||
+ const interceptor = previousCallbacks.getInterface(Ci.nsINetworkInterceptController);
|
||||
+ // We assume that interceptor is a service worker if there is one.
|
||||
+ if (interceptor && interceptor.shouldPrepareForIntercept(httpChannel.URI, httpChannel)) {
|
||||
+ new NotificationCallbacks(this, browser, httpChannel, false);
|
||||
+ new NotificationCallbacks(this, pageNetwork, httpChannel, false);
|
||||
+ this._sendOnRequest(httpChannel, false);
|
||||
+ new ResponseBodyListener(this, browser, httpChannel);
|
||||
+ new ResponseBodyListener(this, pageNetwork, httpChannel);
|
||||
+ } else {
|
||||
+ // We'll issue onRequest once it's intercepted.
|
||||
+ new NotificationCallbacks(this, browser, httpChannel, true);
|
||||
+ new NotificationCallbacks(this, pageNetwork, httpChannel, true);
|
||||
+ }
|
||||
+ } else {
|
||||
+ // We'll issue onRequest once it's intercepted.
|
||||
+ new NotificationCallbacks(this, browser, httpChannel, true);
|
||||
+ new NotificationCallbacks(this, pageNetwork, httpChannel, true);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ _isInterceptionEnabledForBrowser(browser) {
|
||||
+ if (this._browsersWithEnabledInterception.has(browser))
|
||||
+ _isInterceptionEnabledForPage(pageNetwork) {
|
||||
+ if (pageNetwork._requestInterceptionEnabled)
|
||||
+ return true;
|
||||
+ const browserContext = this._targetRegistry.browserContextForBrowser(browser);
|
||||
+ const browserContext = pageNetwork._target.browserContext();
|
||||
+ if (browserContext && browserContext.options.requestInterceptionEnabled)
|
||||
+ return true;
|
||||
+ if (browserContext && browserContext.options.onlineOverride === 'offline')
|
||||
|
|
@ -1562,15 +1614,6 @@ index 0000000000000000000000000000000000000000..052f893eb0e984914ac59f8cb2458044
|
|||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ _ensureInterceptors(browser) {
|
||||
+ let interceptors = this._browserInterceptors.get(browser);
|
||||
+ if (!interceptors) {
|
||||
+ interceptors = new Map();
|
||||
+ this._browserInterceptors.set(browser, interceptors);
|
||||
+ }
|
||||
+ return interceptors;
|
||||
+ }
|
||||
+
|
||||
+ _appendExtraHTTPHeaders(httpChannel, headers) {
|
||||
+ if (!headers)
|
||||
+ return;
|
||||
|
|
@ -1579,35 +1622,34 @@ index 0000000000000000000000000000000000000000..052f893eb0e984914ac59f8cb2458044
|
|||
+ }
|
||||
+
|
||||
+ _onIntercepted(httpChannel, interceptor) {
|
||||
+ const browser = this._getBrowserForChannel(httpChannel);
|
||||
+ if (!browser) {
|
||||
+ const pageNetwork = this._pageNetworkForChannel(httpChannel);
|
||||
+ if (!pageNetwork) {
|
||||
+ interceptor._resume();
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ const browserContext = this._targetRegistry.browserContextForBrowser(browser);
|
||||
+ const browserContext = pageNetwork._target.browserContext();
|
||||
+ if (browserContext && browserContext.options.onlineOverride === 'offline') {
|
||||
+ interceptor._abort(Cr.NS_ERROR_OFFLINE);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ const interceptionEnabled = this._isInterceptionEnabledForBrowser(browser);
|
||||
+ const interceptionEnabled = this._isInterceptionEnabledForPage(pageNetwork);
|
||||
+ this._sendOnRequest(httpChannel, !!interceptionEnabled);
|
||||
+ if (interceptionEnabled)
|
||||
+ this._ensureInterceptors(browser).set(this._requestId(httpChannel), interceptor);
|
||||
+ pageNetwork._ensureInterceptors().set(this._requestId(httpChannel), interceptor);
|
||||
+ else
|
||||
+ interceptor._resume();
|
||||
+ }
|
||||
+
|
||||
+ _sendOnRequest(httpChannel, isIntercepted) {
|
||||
+ const browser = this._getBrowserForChannel(httpChannel);
|
||||
+ if (!browser)
|
||||
+ const pageNetwork = this._pageNetworkForChannel(httpChannel);
|
||||
+ if (!pageNetwork)
|
||||
+ return;
|
||||
+ const causeType = httpChannel.loadInfo ? httpChannel.loadInfo.externalContentPolicyType : Ci.nsIContentPolicy.TYPE_OTHER;
|
||||
+ const requestId = this._requestId(httpChannel);
|
||||
+ const redirectedFrom = this._redirectMap.get(requestId);
|
||||
+ this._redirectMap.delete(requestId);
|
||||
+ this.emit('request', httpChannel, {
|
||||
+ pageNetwork.emit(PageNetwork.Events.Request, httpChannel, {
|
||||
+ url: httpChannel.URI.spec,
|
||||
+ isIntercepted,
|
||||
+ requestId,
|
||||
|
|
@ -1620,15 +1662,15 @@ index 0000000000000000000000000000000000000000..052f893eb0e984914ac59f8cb2458044
|
|||
+ });
|
||||
+ }
|
||||
+
|
||||
+ _sendOnRequestFinished(httpChannel) {
|
||||
+ this.emit('requestfinished', httpChannel, {
|
||||
+ _sendOnRequestFinished(pageNetwork, httpChannel) {
|
||||
+ pageNetwork.emit(PageNetwork.Events.RequestFinished, httpChannel, {
|
||||
+ requestId: this._requestId(httpChannel),
|
||||
+ });
|
||||
+ this._cleanupChannelState(httpChannel);
|
||||
+ }
|
||||
+
|
||||
+ _sendOnRequestFailed(httpChannel, error) {
|
||||
+ this.emit('requestfailed', httpChannel, {
|
||||
+ _sendOnRequestFailed(pageNetwork, httpChannel, error) {
|
||||
+ pageNetwork.emit(PageNetwork.Events.RequestFailed, httpChannel, {
|
||||
+ requestId: this._requestId(httpChannel),
|
||||
+ errorCode: helper.getNetworkErrorStatusText(error),
|
||||
+ });
|
||||
|
|
@ -1642,8 +1684,8 @@ index 0000000000000000000000000000000000000000..052f893eb0e984914ac59f8cb2458044
|
|||
+ }
|
||||
+
|
||||
+ _onResponse(fromCache, httpChannel, topic) {
|
||||
+ const browser = this._getBrowserForChannel(httpChannel);
|
||||
+ if (!browser)
|
||||
+ const pageNetwork = this._pageNetworkForChannel(httpChannel);
|
||||
+ if (!pageNetwork)
|
||||
+ return;
|
||||
+ httpChannel.QueryInterface(Ci.nsIHttpChannelInternal);
|
||||
+ const headers = [];
|
||||
|
|
@ -1659,7 +1701,7 @@ index 0000000000000000000000000000000000000000..052f893eb0e984914ac59f8cb2458044
|
|||
+ } catch (e) {
|
||||
+ // remoteAddress is not defined for cached requests.
|
||||
+ }
|
||||
+ this.emit('response', httpChannel, {
|
||||
+ pageNetwork.emit(PageNetwork.Events.Response, httpChannel, {
|
||||
+ requestId: this._requestId(httpChannel),
|
||||
+ securityDetails: getSecurityDetails(httpChannel),
|
||||
+ fromCache,
|
||||
|
|
@ -1671,32 +1713,11 @@ index 0000000000000000000000000000000000000000..052f893eb0e984914ac59f8cb2458044
|
|||
+ });
|
||||
+ }
|
||||
+
|
||||
+ _onResponseFinished(browser, httpChannel, body) {
|
||||
+ const responseStorage = this._browserResponseStorages.get(browser);
|
||||
+ if (!responseStorage)
|
||||
+ _onResponseFinished(pageNetwork, httpChannel, body) {
|
||||
+ if (!pageNetwork._isActive())
|
||||
+ return;
|
||||
+ responseStorage.addResponseBody(httpChannel, body);
|
||||
+ this._sendOnRequestFinished(httpChannel);
|
||||
+ }
|
||||
+
|
||||
+ startTrackingBrowserNetwork(browser) {
|
||||
+ const value = this._browserSessionCount.get(browser) || 0;
|
||||
+ this._browserSessionCount.set(browser, value + 1);
|
||||
+ if (value === 0)
|
||||
+ this._browserResponseStorages.set(browser, new ResponseStorage(this, MAX_RESPONSE_STORAGE_SIZE, MAX_RESPONSE_STORAGE_SIZE / 10));
|
||||
+ return () => this.stopTrackingBrowserNetwork(browser);
|
||||
+ }
|
||||
+
|
||||
+ stopTrackingBrowserNetwork(browser) {
|
||||
+ const value = this._browserSessionCount.get(browser);
|
||||
+ if (value) {
|
||||
+ this._browserSessionCount.set(browser, value - 1);
|
||||
+ } else {
|
||||
+ this._browserSessionCount.delete(browser);
|
||||
+ this._browserResponseStorages.delete(browser);
|
||||
+ this._browsersWithEnabledInterception.delete(browser);
|
||||
+ this._browserInterceptors.delete(browser);
|
||||
+ }
|
||||
+ pageNetwork._responseStorage.addResponseBody(httpChannel, body);
|
||||
+ this._sendOnRequestFinished(pageNetwork, httpChannel);
|
||||
+ }
|
||||
+
|
||||
+ dispose() {
|
||||
|
|
@ -1833,9 +1854,9 @@ index 0000000000000000000000000000000000000000..052f893eb0e984914ac59f8cb2458044
|
|||
+}
|
||||
+
|
||||
+class ResponseBodyListener {
|
||||
+ constructor(networkObserver, browser, httpChannel) {
|
||||
+ constructor(networkObserver, pageNetwork, httpChannel) {
|
||||
+ this._networkObserver = networkObserver;
|
||||
+ this._browser = browser;
|
||||
+ this._pageNetwork = pageNetwork;
|
||||
+ this._httpChannel = httpChannel;
|
||||
+ this._chunks = [];
|
||||
+ this.QueryInterface = ChromeUtils.generateQI([Ci.nsIStreamListener]);
|
||||
|
|
@ -1874,7 +1895,7 @@ index 0000000000000000000000000000000000000000..052f893eb0e984914ac59f8cb2458044
|
|||
+
|
||||
+ const body = this._chunks.join('');
|
||||
+ delete this._chunks;
|
||||
+ this._networkObserver._onResponseFinished(this._browser, this._httpChannel, body);
|
||||
+ this._networkObserver._onResponseFinished(this._pageNetwork, this._httpChannel, body);
|
||||
+ this.dispose();
|
||||
+ }
|
||||
+
|
||||
|
|
@ -1885,9 +1906,9 @@ index 0000000000000000000000000000000000000000..052f893eb0e984914ac59f8cb2458044
|
|||
+}
|
||||
+
|
||||
+class NotificationCallbacks {
|
||||
+ constructor(networkObserver, browser, httpChannel, shouldIntercept) {
|
||||
+ constructor(networkObserver, pageNetwork, httpChannel, shouldIntercept) {
|
||||
+ this._networkObserver = networkObserver;
|
||||
+ this._browser = browser;
|
||||
+ this._pageNetwork = pageNetwork;
|
||||
+ this._shouldIntercept = shouldIntercept;
|
||||
+ this._httpChannel = httpChannel;
|
||||
+ this._previousCallbacks = httpChannel.notificationCallbacks;
|
||||
|
|
@ -1957,7 +1978,7 @@ index 0000000000000000000000000000000000000000..052f893eb0e984914ac59f8cb2458044
|
|||
+ promptAuth(aChannel, level, authInfo) {
|
||||
+ if (authInfo.flags & Ci.nsIAuthInformation.PREVIOUS_FAILED)
|
||||
+ return false;
|
||||
+ const browserContext = this._networkObserver._targetRegistry.browserContextForBrowser(this._browser);
|
||||
+ const browserContext = this._pageNetwork._target.browserContext();
|
||||
+ const credentials = browserContext ? browserContext.options.httpCredentials : undefined;
|
||||
+ if (!credentials)
|
||||
+ return false;
|
||||
|
|
@ -1996,7 +2017,7 @@ index 0000000000000000000000000000000000000000..052f893eb0e984914ac59f8cb2458044
|
|||
+ synthesized.data = body;
|
||||
+ this._intercepted.startSynthesizedResponse(synthesized, null, null, '', false);
|
||||
+ this._intercepted.finishSynthesizedResponse();
|
||||
+ this._networkObserver.emit('response', this._httpChannel, {
|
||||
+ this._pageNetwork.emit(PageNetwork.Events.Response, this._httpChannel, {
|
||||
+ requestId: this._networkObserver._requestId(this._httpChannel),
|
||||
+ securityDetails: null,
|
||||
+ fromCache: false,
|
||||
|
|
@ -2004,13 +2025,13 @@ index 0000000000000000000000000000000000000000..052f893eb0e984914ac59f8cb2458044
|
|||
+ status,
|
||||
+ statusText,
|
||||
+ });
|
||||
+ this._networkObserver._onResponseFinished(this._browser, this._httpChannel, body);
|
||||
+ this._networkObserver._onResponseFinished(this._pageNetwork, this._httpChannel, body);
|
||||
+ }
|
||||
+
|
||||
+ _abort(errorCode) {
|
||||
+ const error = errorMap[errorCode] || Cr.NS_ERROR_FAILURE;
|
||||
+ this._intercepted.cancelInterception(error);
|
||||
+ this._networkObserver._sendOnRequestFailed(this._httpChannel, error);
|
||||
+ this._networkObserver._sendOnRequestFailed(this._pageNetwork, this._httpChannel, error);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
|
|
@ -2031,8 +2052,16 @@ index 0000000000000000000000000000000000000000..052f893eb0e984914ac59f8cb2458044
|
|||
+ 'failed': Cr.NS_ERROR_FAILURE,
|
||||
+};
|
||||
+
|
||||
+var EXPORTED_SYMBOLS = ['NetworkObserver'];
|
||||
+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;
|
||||
diff --git a/juggler/SimpleChannel.js b/juggler/SimpleChannel.js
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..ba34976ad05e7f5f1a99777f76ac08b171af40b7
|
||||
|
|
@ -2171,10 +2200,10 @@ index 0000000000000000000000000000000000000000..ba34976ad05e7f5f1a99777f76ac08b1
|
|||
+this.SimpleChannel = SimpleChannel;
|
||||
diff --git a/juggler/TargetRegistry.js b/juggler/TargetRegistry.js
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..27cfe133cab5dc4b1218c0e5624b7eb2f8bc08e3
|
||||
index 0000000000000000000000000000000000000000..5ae2f349b65eb437aa646d14b8d5afd76380b3ad
|
||||
--- /dev/null
|
||||
+++ b/juggler/TargetRegistry.js
|
||||
@@ -0,0 +1,661 @@
|
||||
@@ -0,0 +1,660 @@
|
||||
+const {EventEmitter} = ChromeUtils.import('resource://gre/modules/EventEmitter.jsm');
|
||||
+const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
||||
+const {SimpleChannel} = ChromeUtils.import('chrome://juggler/content/SimpleChannel.js');
|
||||
|
|
@ -2477,11 +2506,6 @@ index 0000000000000000000000000000000000000000..27cfe133cab5dc4b1218c0e5624b7eb2
|
|||
+ return Array.from(this._browserToTarget.values());
|
||||
+ }
|
||||
+
|
||||
+ browserContextForBrowser(browser) {
|
||||
+ const target = this._browserToTarget.get(browser);
|
||||
+ return target ? target._browserContext : undefined;
|
||||
+ }
|
||||
+
|
||||
+ targetForBrowser(browser) {
|
||||
+ return this._browserToTarget.get(browser);
|
||||
+ }
|
||||
|
|
@ -2533,6 +2557,10 @@ index 0000000000000000000000000000000000000000..27cfe133cab5dc4b1218c0e5624b7eb2
|
|||
+ return this._linkedBrowser;
|
||||
+ }
|
||||
+
|
||||
+ browserContext() {
|
||||
+ return this._browserContext;
|
||||
+ }
|
||||
+
|
||||
+ setViewportSize(viewportSize) {
|
||||
+ return setViewportSizeForBrowser(viewportSize, this._linkedBrowser);
|
||||
+ }
|
||||
|
|
@ -5808,13 +5836,14 @@ index 0000000000000000000000000000000000000000..0b28a9568877d99967b2ad845df3eb59
|
|||
+
|
||||
diff --git a/juggler/protocol/NetworkHandler.js b/juggler/protocol/NetworkHandler.js
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..8003e1feb3f5b5faaff0a3699c024982d408dc01
|
||||
index 0000000000000000000000000000000000000000..10ce1e9eb24879426ca11a21ffeb89f3567ea078
|
||||
--- /dev/null
|
||||
+++ b/juggler/protocol/NetworkHandler.js
|
||||
@@ -0,0 +1,158 @@
|
||||
+"use strict";
|
||||
+
|
||||
+const {Helper} = ChromeUtils.import('chrome://juggler/content/Helper.js');
|
||||
+const {NetworkObserver, PageNetwork} = ChromeUtils.import('chrome://juggler/content/NetworkObserver.js');
|
||||
+
|
||||
+const Cc = Components.classes;
|
||||
+const Ci = Components.interfaces;
|
||||
|
|
@ -5826,10 +5855,9 @@ index 0000000000000000000000000000000000000000..8003e1feb3f5b5faaff0a3699c024982
|
|||
+ constructor(target, session, contentChannel) {
|
||||
+ this._session = session;
|
||||
+ this._contentPage = contentChannel.connect(session.sessionId() + 'page');
|
||||
+ this._networkObserver = ChromeUtils.import('chrome://juggler/content/NetworkObserver.js').NetworkObserver.instance();
|
||||
+ this._httpActivity = new Map();
|
||||
+ this._enabled = false;
|
||||
+ this._browser = target.linkedBrowser();
|
||||
+ this._pageNetwork = NetworkObserver.instance().pageNetworkForTarget(target);
|
||||
+ this._requestInterception = false;
|
||||
+ this._eventListeners = [];
|
||||
+ this._pendingRequstWillBeSentEvents = new Set();
|
||||
|
|
@ -5841,42 +5869,42 @@ index 0000000000000000000000000000000000000000..8003e1feb3f5b5faaff0a3699c024982
|
|||
+ return;
|
||||
+ this._enabled = true;
|
||||
+ this._eventListeners = [
|
||||
+ helper.on(this._networkObserver, 'request', this._onRequest.bind(this)),
|
||||
+ helper.on(this._networkObserver, 'response', this._onResponse.bind(this)),
|
||||
+ helper.on(this._networkObserver, 'requestfinished', this._onRequestFinished.bind(this)),
|
||||
+ helper.on(this._networkObserver, 'requestfailed', this._onRequestFailed.bind(this)),
|
||||
+ this._networkObserver.startTrackingBrowserNetwork(this._browser),
|
||||
+ helper.on(this._pageNetwork, PageNetwork.Events.Request, this._onRequest.bind(this)),
|
||||
+ helper.on(this._pageNetwork, PageNetwork.Events.Response, this._onResponse.bind(this)),
|
||||
+ helper.on(this._pageNetwork, PageNetwork.Events.RequestFinished, this._onRequestFinished.bind(this)),
|
||||
+ helper.on(this._pageNetwork, PageNetwork.Events.RequestFailed, this._onRequestFailed.bind(this)),
|
||||
+ this._pageNetwork.addSession(),
|
||||
+ ];
|
||||
+ }
|
||||
+
|
||||
+ async getResponseBody({requestId}) {
|
||||
+ return this._networkObserver.getResponseBody(this._browser, requestId);
|
||||
+ return this._pageNetwork.getResponseBody(requestId);
|
||||
+ }
|
||||
+
|
||||
+ async setExtraHTTPHeaders({headers}) {
|
||||
+ this._networkObserver.setExtraHTTPHeaders(this._browser, headers);
|
||||
+ this._pageNetwork.setExtraHTTPHeaders(headers);
|
||||
+ }
|
||||
+
|
||||
+ async setRequestInterception({enabled}) {
|
||||
+ if (enabled)
|
||||
+ this._networkObserver.enableRequestInterception(this._browser);
|
||||
+ this._pageNetwork.enableRequestInterception();
|
||||
+ else
|
||||
+ this._networkObserver.disableRequestInterception(this._browser);
|
||||
+ this._pageNetwork.disableRequestInterception();
|
||||
+ // Right after we enable/disable request interception we need to await all pending
|
||||
+ // requestWillBeSent events before successfully returning from the method.
|
||||
+ await Promise.all(Array.from(this._pendingRequstWillBeSentEvents));
|
||||
+ }
|
||||
+
|
||||
+ async resumeInterceptedRequest({requestId, method, headers, postData}) {
|
||||
+ this._networkObserver.resumeInterceptedRequest(this._browser, requestId, method, headers, postData);
|
||||
+ this._pageNetwork.resumeInterceptedRequest(requestId, method, headers, postData);
|
||||
+ }
|
||||
+
|
||||
+ async abortInterceptedRequest({requestId, errorCode}) {
|
||||
+ this._networkObserver.abortInterceptedRequest(this._browser, requestId, errorCode);
|
||||
+ this._pageNetwork.abortInterceptedRequest(requestId, errorCode);
|
||||
+ }
|
||||
+
|
||||
+ async fulfillInterceptedRequest({requestId, status, statusText, headers, base64body}) {
|
||||
+ this._networkObserver.fulfillInterceptedRequest(this._browser, requestId, status, statusText, headers, base64body);
|
||||
+ this._pageNetwork.fulfillInterceptedRequest(requestId, status, statusText, headers, base64body);
|
||||
+ }
|
||||
+
|
||||
+ dispose() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue