Bug 1326298 implement off-main-thread delivery with start/stop/error listeners, r=kmag
authorShane Caraveo <scaraveo@mozilla.com>
Tue, 18 Apr 2017 15:50:53 -0700
changeset 353696 2e58d1f1ac271b6d618f792835fd7c9e8e109006
parent 353695 0077229d75004cad0a516d480e361bdff7de1503
child 353697 d7af70f65c2ce11c6dda8aed276b9d5de45f38a5
push id41017
push usermixedpuppy@gmail.com
push dateWed, 19 Apr 2017 00:46:48 +0000
treeherderautoland@2e58d1f1ac27 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskmag
bugs1326298
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1326298 implement off-main-thread delivery with start/stop/error listeners, r=kmag MozReview-Commit-ID: Ke4NsthrbP2
browser/installer/package-manifest.in
mobile/android/installer/package-manifest.in
toolkit/components/build/nsToolkitCompsModule.cpp
toolkit/components/extensions/moz.build
toolkit/components/extensions/test/mochitest/file_WebRequest_page3.html
toolkit/components/extensions/test/mochitest/test_ext_webrequest_basic.html
toolkit/components/extensions/webrequest/moz.build
toolkit/components/extensions/webrequest/nsIWebRequestListener.idl
toolkit/components/extensions/webrequest/nsWebRequestListener.cpp
toolkit/components/extensions/webrequest/nsWebRequestListener.h
toolkit/modules/addons/WebRequest.jsm
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -325,16 +325,17 @@
 @RESPATH@/components/txmgr.xpt
 @RESPATH@/components/uconv.xpt
 @RESPATH@/components/unicharutil.xpt
 @RESPATH@/components/update.xpt
 @RESPATH@/components/uriloader.xpt
 @RESPATH@/components/urlformatter.xpt
 @RESPATH@/components/webBrowser_core.xpt
 @RESPATH@/components/webbrowserpersist.xpt
+@RESPATH@/components/webextensions.xpt
 @RESPATH@/components/widget.xpt
 #ifdef XP_MACOSX
 @RESPATH@/components/widget_cocoa.xpt
 #endif
 @RESPATH@/components/windowcreator.xpt
 @RESPATH@/components/windowds.xpt
 @RESPATH@/components/windowwatcher.xpt
 @RESPATH@/components/xpcom_base.xpt
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -236,16 +236,17 @@
 @BINPATH@/components/txmgr.xpt
 @BINPATH@/components/uconv.xpt
 @BINPATH@/components/unicharutil.xpt
 @BINPATH@/components/update.xpt
 @BINPATH@/components/uriloader.xpt
 @BINPATH@/components/urlformatter.xpt
 @BINPATH@/components/webBrowser_core.xpt
 @BINPATH@/components/webbrowserpersist.xpt
+@BINPATH@/components/webextensions.xpt
 @BINPATH@/components/widget.xpt
 @BINPATH@/components/widget_android.xpt
 @BINPATH@/components/windowcreator.xpt
 @BINPATH@/components/windowds.xpt
 @BINPATH@/components/windowwatcher.xpt
 @BINPATH@/components/xpcom_base.xpt
 @BINPATH@/components/xpcom_system.xpt
 @BINPATH@/components/xpcom_components.xpt
--- a/toolkit/components/build/nsToolkitCompsModule.cpp
+++ b/toolkit/components/build/nsToolkitCompsModule.cpp
@@ -39,16 +39,18 @@
 #include "mozilla/AddonPathService.h"
 
 #if defined(XP_WIN)
 #include "NativeFileWatcherWin.h"
 #else
 #include "NativeFileWatcherNotSupported.h"
 #endif // (XP_WIN)
 
+#include "nsWebRequestListener.h"
+
 #if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID)
 #define MOZ_HAS_TERMINATOR
 #endif
 
 #if defined(MOZ_HAS_TERMINATOR)
 #include "nsTerminator.h"
 #endif
 
@@ -120,16 +122,18 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsUpdateP
 #endif
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(FinalizationWitnessService, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(NativeOSFileInternalsService)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(NativeFileWatcherService, Init)
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(AddonContentPolicy)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(AddonPathService, AddonPathService::GetInstance)
 
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsWebRequestListener)
+
 NS_DEFINE_NAMED_CID(NS_TOOLKIT_APPSTARTUP_CID);
 #if defined(MOZ_HAS_PERFSTATS)
 NS_DEFINE_NAMED_CID(NS_TOOLKIT_PERFORMANCESTATSSERVICE_CID);
 #endif // defined (MOZ_HAS_PERFSTATS)
 
 #if defined(MOZ_HAS_TERMINATOR)
 NS_DEFINE_NAMED_CID(NS_TOOLKIT_TERMINATOR_CID);
 #endif
@@ -153,16 +157,17 @@ NS_DEFINE_NAMED_CID(NS_BROWSERSTATUSFILT
 #if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID)
 NS_DEFINE_NAMED_CID(NS_UPDATEPROCESSOR_CID);
 #endif
 NS_DEFINE_NAMED_CID(FINALIZATIONWITNESSSERVICE_CID);
 NS_DEFINE_NAMED_CID(NATIVE_OSFILE_INTERNALS_SERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_ADDONCONTENTPOLICY_CID);
 NS_DEFINE_NAMED_CID(NS_ADDON_PATH_SERVICE_CID);
 NS_DEFINE_NAMED_CID(NATIVE_FILEWATCHER_SERVICE_CID);
+NS_DEFINE_NAMED_CID(NS_WEBREQUESTLISTENER_CID);
 
 static const Module::CIDEntry kToolkitCIDs[] = {
   { &kNS_TOOLKIT_APPSTARTUP_CID, false, nullptr, nsAppStartupConstructor },
 #if defined(MOZ_HAS_TERMINATOR)
   { &kNS_TOOLKIT_TERMINATOR_CID, false, nullptr, nsTerminatorConstructor },
 #endif
 #if defined(MOZ_HAS_PERFSTATS)
   { &kNS_TOOLKIT_PERFORMANCESTATSSERVICE_CID, false, nullptr, nsPerformanceStatsServiceConstructor },
@@ -187,16 +192,17 @@ static const Module::CIDEntry kToolkitCI
 #if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID)
   { &kNS_UPDATEPROCESSOR_CID, false, nullptr, nsUpdateProcessorConstructor },
 #endif
   { &kFINALIZATIONWITNESSSERVICE_CID, false, nullptr, FinalizationWitnessServiceConstructor },
   { &kNATIVE_OSFILE_INTERNALS_SERVICE_CID, false, nullptr, NativeOSFileInternalsServiceConstructor },
   { &kNS_ADDONCONTENTPOLICY_CID, false, nullptr, AddonContentPolicyConstructor },
   { &kNS_ADDON_PATH_SERVICE_CID, false, nullptr, AddonPathServiceConstructor },
   { &kNATIVE_FILEWATCHER_SERVICE_CID, false, nullptr, NativeFileWatcherServiceConstructor },
+  { &kNS_WEBREQUESTLISTENER_CID, false, nullptr, nsWebRequestListenerConstructor },
   { nullptr }
 };
 
 static const Module::ContractIDEntry kToolkitContracts[] = {
   { NS_APPSTARTUP_CONTRACTID, &kNS_TOOLKIT_APPSTARTUP_CID },
 #if defined(MOZ_HAS_TERMINATOR)
   { NS_TOOLKIT_TERMINATOR_CONTRACTID, &kNS_TOOLKIT_TERMINATOR_CID },
 #endif
@@ -223,16 +229,17 @@ static const Module::ContractIDEntry kTo
 #if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID)
   { NS_UPDATEPROCESSOR_CONTRACTID, &kNS_UPDATEPROCESSOR_CID },
 #endif
   { FINALIZATIONWITNESSSERVICE_CONTRACTID, &kFINALIZATIONWITNESSSERVICE_CID },
   { NATIVE_OSFILE_INTERNALS_SERVICE_CONTRACTID, &kNATIVE_OSFILE_INTERNALS_SERVICE_CID },
   { NS_ADDONCONTENTPOLICY_CONTRACTID, &kNS_ADDONCONTENTPOLICY_CID },
   { NS_ADDONPATHSERVICE_CONTRACTID, &kNS_ADDON_PATH_SERVICE_CID },
   { NATIVE_FILEWATCHER_SERVICE_CONTRACTID, &kNATIVE_FILEWATCHER_SERVICE_CID },
+  { NS_WEBREQUESTLISTENER_CONTRACTID, &kNS_WEBREQUESTLISTENER_CID },
   { nullptr }
 };
 
 static const mozilla::Module::CategoryEntry kToolkitCategories[] = {
   { "content-policy", NS_ADDONCONTENTPOLICY_CONTRACTID, NS_ADDONCONTENTPOLICY_CONTRACTID },
   { nullptr }
 };
 
--- a/toolkit/components/extensions/moz.build
+++ b/toolkit/components/extensions/moz.build
@@ -34,17 +34,20 @@ EXTRA_COMPONENTS += [
     'extensions-toolkit.manifest',
 ]
 
 TESTING_JS_MODULES += [
     'ExtensionTestCommon.jsm',
     'ExtensionXPCShellUtils.jsm',
 ]
 
-DIRS += ['schemas']
+DIRS += [
+    'schemas',
+    'webrequest',
+]
 
 JAR_MANIFESTS += ['jar.mn']
 
 BROWSER_CHROME_MANIFESTS += [
     'test/browser/browser.ini',
 ]
 
 MOCHITEST_MANIFESTS += [
--- a/toolkit/components/extensions/test/mochitest/file_WebRequest_page3.html
+++ b/toolkit/components/extensions/test/mochitest/file_WebRequest_page3.html
@@ -1,11 +1,13 @@
 <!DOCTYPE HTML>
 
 <html>
 <head>
 <meta charset="utf-8">
 <script>
 "use strict";
-window.close();
+window.onload = () => {
+  window.close();
+};
 </script>
 </head>
 </html>
--- a/toolkit/components/extensions/test/mochitest/test_ext_webrequest_basic.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_webrequest_basic.html
@@ -234,17 +234,17 @@ add_task(function* test_webRequest_heade
 add_task(function* test_webRequest_tabId() {
   let expect = {
     "file_WebRequest_page3.html": {
       type: "main_frame",
     },
   };
   extension.sendMessage("set-expected", {expect, origin: location.href});
   yield extension.awaitMessage("continue");
-  let a = addLink("file_WebRequest_page3.html?trigger=a");
+  let a = addLink(`file_WebRequest_page3.html?trigger=a&nocache=${Math.random()}`);
   a.click();
   yield extension.awaitMessage("done");
 });
 
 add_task(function* test_webRequest_tabId_browser() {
   async function background(url) {
     let tabId;
     browser.test.onMessage.addListener(async (msg, expected) => {
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/webrequest/moz.build
@@ -0,0 +1,21 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+XPIDL_SOURCES += [
+    'nsIWebRequestListener.idl',
+]
+
+XPIDL_MODULE = 'webextensions'
+
+EXPORTS += [
+    'nsWebRequestListener.h',
+]
+
+UNIFIED_SOURCES += [
+    'nsWebRequestListener.cpp',
+]
+
+FINAL_LIBRARY = 'xul'
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/webrequest/nsIWebRequestListener.idl
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#include "nsISupports.idl"
+#include "nsIStreamListener.idl"
+#include "nsITraceableChannel.idl"
+
+/* nsIWebRequestListener is a nsIThreadRetargetableStreamListener that handles
+ * forwarding of nsIRequestObserver for JS consumers. nsIWebRequestListener
+ * is not cycle collected, JS consumers should not keep a reference to this.
+ */
+
+[scriptable, uuid(699a50bb-1f18-2844-b9ea-9f216f62cb18)]
+interface nsIWebRequestListener : nsISupports
+{
+  void init(in nsIStreamListener aStreamListener,
+            in nsITraceableChannel aTraceableChannel);
+};
+
+%{C++
+/* ebea9901-e135-b546-82e2-052666992dbb */
+#define NS_WEBREQUESTLISTENER_CID                   \
+ {0xebea9901, 0xe135, 0xb546,                       \
+ {0x82, 0xe2, 0x05, 0x26, 0x66, 0x99, 0x2d, 0xbb} }
+#define NS_WEBREQUESTLISTENER_CONTRACTID "@mozilla.org/webextensions/webRequestListener;1"
+%}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/webrequest/nsWebRequestListener.cpp
@@ -0,0 +1,74 @@
+/* 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/. */
+
+#include "mozilla/ModuleUtils.h"
+#include "nsWebRequestListener.h"
+
+#ifdef DEBUG
+#include "MainThreadUtils.h"
+#endif
+
+using namespace mozilla;
+
+NS_IMPL_ISUPPORTS(nsWebRequestListener,
+                  nsIWebRequestListener,
+                  nsIStreamListener,
+                  nsIRequestObserver,
+                  nsIThreadRetargetableStreamListener)
+
+NS_IMETHODIMP
+nsWebRequestListener::Init(nsIStreamListener *aStreamListener, nsITraceableChannel *aTraceableChannel)
+{
+  MOZ_ASSERT(aStreamListener, "Should have aStreamListener");
+  MOZ_ASSERT(aTraceableChannel, "Should have aTraceableChannel");
+  mTargetStreamListener = aStreamListener;
+  return aTraceableChannel->SetNewListener(this, getter_AddRefs(mOrigStreamListener));
+}
+
+NS_IMETHODIMP
+nsWebRequestListener::OnStartRequest(nsIRequest *request, nsISupports * aCtxt)
+{
+  MOZ_ASSERT(mTargetStreamListener, "Should have mTargetStreamListener");
+  MOZ_ASSERT(mOrigStreamListener, "Should have mOrigStreamListener");
+
+  nsresult rv = mTargetStreamListener->OnStartRequest(request, aCtxt);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return mOrigStreamListener->OnStartRequest(request, aCtxt);
+}
+
+NS_IMETHODIMP
+nsWebRequestListener::OnStopRequest(nsIRequest *request, nsISupports *aCtxt,
+                                           nsresult aStatus)
+{
+  MOZ_ASSERT(mOrigStreamListener, "Should have mOrigStreamListener");
+  MOZ_ASSERT(mTargetStreamListener, "Should have mTargetStreamListener");
+
+  nsresult rv = mOrigStreamListener->OnStopRequest(request, aCtxt, aStatus);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return mTargetStreamListener->OnStopRequest(request, aCtxt, aStatus);
+}
+
+NS_IMETHODIMP
+nsWebRequestListener::OnDataAvailable(nsIRequest *request, nsISupports * aCtxt,
+                                             nsIInputStream * inStr,
+                                             uint64_t sourceOffset, uint32_t count)
+{
+  MOZ_ASSERT(mOrigStreamListener, "Should have mOrigStreamListener");
+  return mOrigStreamListener->OnDataAvailable(request, aCtxt, inStr, sourceOffset, count);
+}
+
+NS_IMETHODIMP
+nsWebRequestListener::CheckListenerChain()
+{
+    MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread!");
+    nsresult rv;
+    nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener =
+        do_QueryInterface(mOrigStreamListener, &rv);
+    if (retargetableListener) {
+        return retargetableListener->CheckListenerChain();
+    }
+    return rv;
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/webrequest/nsWebRequestListener.h
@@ -0,0 +1,36 @@
+/* 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/. */
+
+#ifndef nsWebRequestListener_h__
+#define nsWebRequestListener_h__
+
+#include "nsCOMPtr.h"
+#include "nsIWebRequestListener.h"
+#include "nsIRequestObserver.h"
+#include "nsIStreamListener.h"
+#include "nsITraceableChannel.h"
+#include "nsIThreadRetargetableStreamListener.h"
+#include "mozilla/Attributes.h"
+
+class nsWebRequestListener final : public nsIWebRequestListener
+                                 , public nsIStreamListener
+                                 , public nsIThreadRetargetableStreamListener
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIWEBREQUESTLISTENER
+  NS_DECL_NSIREQUESTOBSERVER
+  NS_DECL_NSISTREAMLISTENER
+  NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
+
+  nsWebRequestListener() {}
+
+private:
+  ~nsWebRequestListener() {}
+  nsCOMPtr<nsIStreamListener> mOrigStreamListener;
+  nsCOMPtr<nsIStreamListener> mTargetStreamListener;
+};
+
+#endif // nsWebRequestListener_h__
+
--- a/toolkit/modules/addons/WebRequest.jsm
+++ b/toolkit/modules/addons/WebRequest.jsm
@@ -25,16 +25,19 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource://gre/modules/ExtensionUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "WebRequestCommon",
                                   "resource://gre/modules/WebRequestCommon.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "WebRequestUpload",
                                   "resource://gre/modules/WebRequestUpload.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "ExtensionError", () => ExtensionUtils.ExtensionError);
 
+let WebRequestListener = Components.Constructor("@mozilla.org/webextensions/webRequestListener;1",
+                                                "nsIWebRequestListener", "init");
+
 function attachToChannel(channel, key, data) {
   if (channel instanceof Ci.nsIWritablePropertyBag2) {
     let wrapper = {wrappedJSObject: data};
     channel.setPropertyAsInterface(key, wrapper);
   }
   return data;
 }
 
@@ -321,43 +324,33 @@ var ContentPolicyManager = {
 
     this.policyData.delete(id);
     this.idMap.delete(callback);
     this.policies.delete(id);
   },
 };
 ContentPolicyManager.init();
 
-function StartStopListener(manager, loadContext) {
+function StartStopListener(manager, channel, loadContext) {
   this.manager = manager;
   this.loadContext = loadContext;
-  this.orig = null;
+  new WebRequestListener(this, channel);
 }
 
 StartStopListener.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIRequestObserver,
                                          Ci.nsIStreamListener]),
 
   onStartRequest: function(request, context) {
     this.manager.onStartRequest(request, this.loadContext);
-    this.orig.onStartRequest(request, context);
   },
 
   onStopRequest(request, context, statusCode) {
-    try {
-      this.orig.onStopRequest(request, context, statusCode);
-    } catch (e) {
-      Cu.reportError(e);
-    }
     this.manager.onStopRequest(request, this.loadContext);
   },
-
-  onDataAvailable(...args) {
-    return this.orig.onDataAvailable(...args);
-  },
 };
 
 var ChannelEventSink = {
   _classDescription: "WebRequest channel event sink",
   _classID: Components.ID("115062f8-92f1-11e5-8b7f-080027b0f7ec"),
   _contractID: "@mozilla.org/webrequest/channel-event-sink;1",
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIChannelEventSink,
@@ -995,19 +988,17 @@ HttpObserverManager = {
     let channelData = getData(channel);
     if (this.needTracing) {
       // Check whether we've already added a listener to this channel,
       // so we don't wind up chaining multiple listeners.
       if (!channelData.hasListener && channel instanceof Ci.nsITraceableChannel) {
         let responseStatus = channel.responseStatus;
         // skip redirections, https://bugzilla.mozilla.org/show_bug.cgi?id=728901#c8
         if (responseStatus < 300 || responseStatus >= 400) {
-          let listener = new StartStopListener(this, loadContext);
-          let orig = channel.setNewListener(listener);
-          listener.orig = orig;
+          new StartStopListener(this, channel, loadContext);
           channelData.hasListener = true;
         }
       }
     }
 
     if (this.listeners.headersReceived.size) {
       this.runChannelListener(channel, loadContext, "headersReceived");
     }