Bug 1326298 implement off-main-thread delivery with start/stop/error listeners, r?kmag draft
authorShane Caraveo <scaraveo@mozilla.com>
Tue, 18 Apr 2017 15:50:53 -0700
changeset 564722 4a76500cbe5bfa7c663d364b3c9e1c8bf7e155e1
parent 563757 2b6a66a98e253ba158f3960f1c68ad49b2ebcdb4
child 624815 dc324efe185e7bd36505246ba465ba0dbbe466c9
push id54678
push usermixedpuppy@gmail.com
push dateTue, 18 Apr 2017 22:55:29 +0000
reviewerskmag
bugs1326298
milestone55.0a1
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");
     }