Bug 1187335 - P1 - Add a way to report error to all clients for ServiceWorker and SharedWorker. r=bkelly.
authorTom Tung <ttung@mozilla.com>
Mon, 23 May 2016 14:56:46 +0800
changeset 354512 8510e1c0f42a29d15b442c9c44c700add93e955c
parent 354511 1c1d78ab773ca5bce52238fc2155c0eabc626e0c
child 354513 89c1c3294bee9930d4421f0f033f9d9ed0a69be3
push id6570
push userraliiev@mozilla.com
push dateMon, 14 Nov 2016 12:26:13 +0000
treeherdermozilla-beta@f455459b2ae5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbkelly
bugs1187335
milestone51.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 1187335 - P1 - Add a way to report error to all clients for ServiceWorker and SharedWorker. r=bkelly.
devtools/shared/webconsole/test/test_console_serviceworker.html
dom/console/ConsoleReportCollector.cpp
dom/console/ConsoleReportCollector.h
dom/console/nsIConsoleReportCollector.h
dom/workers/ServiceWorkerManager.cpp
dom/workers/ServiceWorkerManager.h
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerPrivate.h
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/HttpBaseChannel.h
netwerk/protocol/http/InterceptedChannel.cpp
--- a/devtools/shared/webconsole/test/test_console_serviceworker.html
+++ b/devtools/shared/webconsole/test/test_console_serviceworker.html
@@ -43,16 +43,21 @@ let expectedConsoleCalls = [
       filename: /helper_serviceworker/,
       arguments: ['fetch event: ' + SCOPE_FRAME_URL],
     },
     {
       level: "log",
       filename: /helper_serviceworker/,
       arguments: ['fetch event: ' + SCOPE_FRAME_URL2],
     },
+    {
+      level: "log",
+      filename: /helper_serviceworker/,
+      arguments: ['message event: ' + MESSAGE],
+    },
 ];
 let consoleCalls = [];
 
 let startTest = Task.async(function*() {
   removeEventListener("load", startTest);
 
   yield new Promise(resolve => {
     SpecialPowers.pushPrefEnv({"set": [
@@ -110,19 +115,20 @@ let onAttach = Task.async(function*(stat
     yield forceReloadFrame(currentFrame);
     ok(!currentFrame.contentWindow.navigator.serviceWorker.controller,
        'current frame should not be controlled after force refresh');
     is(currentFrame.contentWindow.location.toString(), SCOPE_FRAME_URL,
        'current frame should still have in-scope location URL even though it got 404');
 
     // Now postMessage() the service worker to trigger its message event
     // handler.  This will generate 1 or 2 to console.log() statements
-    // depending on if the worker thread needs to spin up again.  In either
-    // case, though, we should not get any console calls because we don't
-    // have a controlled or registering document.
+    // depending on if the worker thread needs to spin up again.  Although we
+    // don't have a controlled or registering document in both cases, we still
+    // could get console calls since we only flush reports when the channel is
+    // finally destroyed.
     info("Completed force refresh.  Messaging service worker.");
     yield messageServiceWorker(currentFrame.contentWindow, SCOPE, MESSAGE);
 
     info("Done messaging service worker.  Unregistering service worker.");
     yield unregisterServiceWorker(currentFrame.contentWindow);
 
     info('Service worker unregistered.  Checking console calls.');
     state.dbgClient.removeListener("consoleAPICall", onConsoleAPICall);
--- a/dom/console/ConsoleReportCollector.cpp
+++ b/dom/console/ConsoleReportCollector.cpp
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/ConsoleReportCollector.h"
 
+#include "nsIConsoleService.h"
+#include "nsIScriptError.h"
 #include "nsNetUtil.h"
 
 namespace mozilla {
 
 NS_IMPL_ISUPPORTS(ConsoleReportCollector, nsIConsoleReportCollector)
 
 ConsoleReportCollector::ConsoleReportCollector()
   : mMutex("mozilla::ConsoleReportCollector")
@@ -32,36 +34,44 @@ ConsoleReportCollector::AddConsoleReport
 
   mPendingReports.AppendElement(PendingReport(aErrorFlags, aCategory,
                                               aPropertiesFile, aSourceFileURI,
                                               aLineNumber, aColumnNumber,
                                               aMessageName, aStringParams));
 }
 
 void
-ConsoleReportCollector::FlushConsoleReports(nsIDocument* aDocument)
+ConsoleReportCollector::FlushConsoleReports(nsIDocument* aDocument,
+                                            ReportAction aAction)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsTArray<PendingReport> reports;
 
   {
     MutexAutoLock lock(mMutex);
-    mPendingReports.SwapElements(reports);
+    if (aAction == ReportAction::Forget) {
+      mPendingReports.SwapElements(reports);
+    } else {
+      reports = mPendingReports;
+    }
   }
 
   for (uint32_t i = 0; i < reports.Length(); ++i) {
     PendingReport& report = reports[i];
 
     // It would be nice if we did not have to do this since ReportToConsole()
     // just turns around and converts it back to a spec.
     nsCOMPtr<nsIURI> uri;
-    nsresult rv = NS_NewURI(getter_AddRefs(uri), report.mSourceFileURI);
-    if (NS_FAILED(rv)) {
-      continue;
+    if (!report.mSourceFileURI.IsEmpty()) {
+      nsresult rv = NS_NewURI(getter_AddRefs(uri), report.mSourceFileURI);
+      MOZ_ALWAYS_SUCCEEDS(rv);
+      if (NS_FAILED(rv)) {
+        continue;
+      }
     }
 
     // Convert back from nsTArray<nsString> to the char16_t** format required
     // by our l10n libraries and ReportToConsole. (bug 1219762)
     UniquePtr<const char16_t*[]> params;
     uint32_t paramsLength = report.mStringParams.Length();
     if (paramsLength > 0) {
       params = MakeUnique<const char16_t*[]>(paramsLength);
@@ -95,13 +105,86 @@ ConsoleReportCollector::FlushConsoleRepo
     PendingReport& report = reports[i];
     aCollector->AddConsoleReport(report.mErrorFlags, report.mCategory,
                                  report.mPropertiesFile, report.mSourceFileURI,
                                  report.mLineNumber, report.mColumnNumber,
                                  report.mMessageName, report.mStringParams);
   }
 }
 
+void
+ConsoleReportCollector::FlushReportsByWindowId(uint64_t aWindowId,
+                                               ReportAction aAction)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsTArray<PendingReport> reports;
+
+  {
+    MutexAutoLock lock(mMutex);
+    if (aAction == ReportAction::Forget) {
+      mPendingReports.SwapElements(reports);
+    } else {
+      reports = mPendingReports;
+    }
+  }
+
+  nsCOMPtr<nsIConsoleService> consoleService =
+    do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+  if (!consoleService) {
+    NS_WARNING("GetConsoleService failed");
+    return;
+  }
+
+  nsresult rv;
+  for (uint32_t i = 0; i < reports.Length(); ++i) {
+    PendingReport& report = reports[i];
+
+    nsXPIDLString errorText;
+    if (!report.mStringParams.IsEmpty()) {
+      rv = nsContentUtils::FormatLocalizedString(report.mPropertiesFile,
+                                                 report.mMessageName.get(),
+                                                 report.mStringParams,
+                                                 errorText);
+    } else {
+      rv = nsContentUtils::GetLocalizedString(report.mPropertiesFile,
+                                              report.mMessageName.get(),
+                                              errorText);
+    }
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      continue;
+    }
+
+    nsCOMPtr<nsIScriptError> errorObject =
+    do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      continue;
+    }
+
+    rv = errorObject->InitWithWindowID(errorText,
+                                       NS_ConvertUTF8toUTF16(report.mSourceFileURI),
+                                       EmptyString(),
+                                       report.mLineNumber,
+                                       report.mColumnNumber,
+                                       report.mErrorFlags,
+                                       report.mCategory,
+                                       aWindowId);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      continue;
+    }
+
+    consoleService->LogMessage(errorObject);
+  }
+}
+
+void
+ConsoleReportCollector::ClearConsoleReports()
+{
+  MutexAutoLock lock(mMutex);
+
+  mPendingReports.Clear();
+}
+
 ConsoleReportCollector::~ConsoleReportCollector()
 {
 }
 
 } // namespace mozilla
--- a/dom/console/ConsoleReportCollector.h
+++ b/dom/console/ConsoleReportCollector.h
@@ -22,21 +22,29 @@ public:
   AddConsoleReport(uint32_t aErrorFlags, const nsACString& aCategory,
                    nsContentUtils::PropertiesFile aPropertiesFile,
                    const nsACString& aSourceFileURI,
                    uint32_t aLineNumber, uint32_t aColumnNumber,
                    const nsACString& aMessageName,
                    const nsTArray<nsString>& aStringParams) override;
 
   void
-  FlushConsoleReports(nsIDocument* aDocument) override;
+  FlushConsoleReports(nsIDocument* aDocument,
+                      ReportAction aAction = ReportAction::Forget) override;
 
   void
   FlushConsoleReports(nsIConsoleReportCollector* aCollector) override;
 
+  void
+  FlushReportsByWindowId(uint64_t aWindowId,
+                         ReportAction aAction = ReportAction::Forget) override;
+
+  void
+  ClearConsoleReports() override;
+
 private:
   ~ConsoleReportCollector();
 
   struct PendingReport
   {
     PendingReport(uint32_t aErrorFlags, const nsACString& aCategory,
                nsContentUtils::PropertiesFile aPropertiesFile,
                const nsACString& aSourceFileURI, uint32_t aLineNumber,
--- a/dom/console/nsIConsoleReportCollector.h
+++ b/dom/console/nsIConsoleReportCollector.h
@@ -61,29 +61,55 @@ public:
   {
     nsTArray<nsString> params;
     mozilla::dom::StringArrayAppender::Append(params, sizeof...(Params),
                                               mozilla::Forward<Params>(aParams)...);
     AddConsoleReport(aErrorFlags, aCategory, aPropertiesFile, aSourceFileURI,
                      aLineNumber, aColumnNumber, aMessageName, params);
   }
 
+  // An enum calss to indicate whether should free the pending reports or not.
+  // Forget        Free the pending reports.
+  // Save          Keep the pending reports.
+  enum class ReportAction {
+    Forget,
+    Save
+  };
+
   // Flush all pending reports to the console.  Main thread only.
   //
   // aDocument      An optional document representing where to flush the
   //                reports.  If provided, then the corresponding window's
   //                web console will get the reports.  Otherwise the reports
   //                go to the browser console.
+  // aAction        An action to determine whether to reserve the pending
+  //                reports. Defalut action is to forget the report.
   virtual void
-  FlushConsoleReports(nsIDocument* aDocument) = 0;
+  FlushConsoleReports(nsIDocument* aDocument,
+                      ReportAction aAction = ReportAction::Forget) = 0;
 
   // Flush all pending reports to another collector.  May be called from any
   // thread.
   //
   // aCollector     A required collector object that will effectively take
   //                ownership of our currently console reports.
   virtual void
   FlushConsoleReports(nsIConsoleReportCollector* aCollector) = 0;
+
+  // Flush all pending reports to the console accroding to window ID. Main
+  // thread only.
+  //
+  // aWindowId      A window ID representing where to flush the reports and it's
+  //                typically the inner window ID.
+  //
+  // aAction        An action to decide whether free the pending reports or not.
+  virtual void
+  FlushReportsByWindowId(uint64_t aWindowId,
+                         ReportAction aAction = ReportAction::Forget) = 0;
+
+  // Clear all pending reports.
+  virtual void
+  ClearConsoleReports() = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIConsoleReportCollector, NS_NSICONSOLEREPORTCOLLECTOR_IID)
 
 #endif // nsIConsoleReportCollector_h
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -1537,16 +1537,108 @@ ServiceWorkerManager::LocalizeAndReportT
                             aFilename, aLine, aLineNumber, aColumnNumber,
                             aFlags);
   } else {
     NS_WARNING("Failed to format and therefore report localized error.");
   }
 }
 
 void
+ServiceWorkerManager::FlushReportsToAllClients(const nsACString& aScope,
+                                               nsIConsoleReportCollector* aReporter)
+{
+  AutoTArray<uint64_t, 16> windows;
+
+  // Report errors to every controlled document.
+  for (auto iter = mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
+    ServiceWorkerRegistrationInfo* reg = iter.UserData();
+    MOZ_ASSERT(reg);
+    if (!reg->mScope.Equals(aScope)) {
+      continue;
+    }
+
+    nsCOMPtr<nsIDocument> doc = do_QueryInterface(iter.Key());
+    if (!doc || !doc->IsCurrentActiveDocument() || !doc->GetWindow()) {
+      continue;
+    }
+
+    windows.AppendElement(doc->InnerWindowID());
+
+    aReporter->FlushConsoleReports(doc,
+                                   nsIConsoleReportCollector::ReportAction::Save);
+  }
+
+  // Report to any documents that have called .register() for this scope.  They
+  // may not be controlled, but will still want to see error reports.
+  WeakDocumentList* regList = mRegisteringDocuments.Get(aScope);
+  if (regList) {
+    for (int32_t i = regList->Length() - 1; i >= 0; --i) {
+      nsCOMPtr<nsIDocument> doc = do_QueryReferent(regList->ElementAt(i));
+      if (!doc) {
+        regList->RemoveElementAt(i);
+        continue;
+      }
+
+      if (!doc->IsCurrentActiveDocument()) {
+        continue;
+      }
+
+      uint64_t innerWindowId = doc->InnerWindowID();
+      if (windows.Contains(innerWindowId)) {
+        continue;
+      }
+
+      windows.AppendElement(innerWindowId);
+
+      aReporter->FlushConsoleReports(doc,
+                                     nsIConsoleReportCollector::ReportAction::Save);
+    }
+
+    if (regList->IsEmpty()) {
+      regList = nullptr;
+      nsAutoPtr<WeakDocumentList> doomed;
+      mRegisteringDocuments.RemoveAndForget(aScope, doomed);
+    }
+  }
+
+  nsresult rv;
+  InterceptionList* intList = mNavigationInterceptions.Get(aScope);
+  if (intList) {
+    for (uint32_t i = 0; i < intList->Length(); ++i) {
+      nsCOMPtr<nsIInterceptedChannel> channel = intList->ElementAt(i);
+
+      nsCOMPtr<nsIChannel> inner;
+      rv = channel->GetChannel(getter_AddRefs(inner));
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        continue;
+      }
+
+      uint64_t innerWindowId = nsContentUtils::GetInnerWindowID(inner);
+      if (innerWindowId == 0 || windows.Contains(innerWindowId)) {
+        continue;
+      }
+
+      windows.AppendElement(innerWindowId);
+
+      aReporter->FlushReportsByWindowId(innerWindowId,
+                                        nsIConsoleReportCollector::ReportAction::Save);
+    }
+  }
+
+  // If there are no documents to report to, at least report something to the
+  // browser console.
+  if (windows.IsEmpty()) {
+    aReporter->FlushConsoleReports((nsIDocument*)nullptr);
+    return;
+  }
+
+  aReporter->ClearConsoleReports();
+}
+
+void
 ServiceWorkerManager::HandleError(JSContext* aCx,
                                   nsIPrincipal* aPrincipal,
                                   const nsCString& aScope,
                                   const nsString& aWorkerURL,
                                   const nsString& aMessage,
                                   const nsString& aFilename,
                                   const nsString& aLine,
                                   uint32_t aLineNumber,
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -8,16 +8,17 @@
 #define mozilla_dom_workers_serviceworkermanager_h
 
 #include "nsIServiceWorkerManager.h"
 #include "nsCOMPtr.h"
 
 #include "ipc/IPCMessageUtils.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/AutoRestore.h"
+#include "mozilla/ConsoleReportCollector.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TypedEnumBits.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/WeakPtr.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ServiceWorkerCommon.h"
@@ -28,16 +29,17 @@
 #include "nsClassHashtable.h"
 #include "nsDataHashtable.h"
 #include "nsIIPCBackgroundChildCreateCallback.h"
 #include "nsRefPtrHashtable.h"
 #include "nsTArrayForwardDeclare.h"
 #include "nsTObserverArray.h"
 
 class mozIApplicationClearPrivateDataParams;
+class nsIConsoleReportCollector;
 
 namespace mozilla {
 
 class PrincipalOriginAttributes;
 
 namespace dom {
 
 class ServiceWorkerRegistrationListener;
@@ -217,16 +219,20 @@ public:
                                 const char* aStringKey,
                                 const nsTArray<nsString>& aParamArray,
                                 uint32_t aFlags = 0x0,
                                 const nsString& aFilename = EmptyString(),
                                 const nsString& aLine = EmptyString(),
                                 uint32_t aLineNumber = 0,
                                 uint32_t aColumnNumber = 0);
 
+  void
+  FlushReportsToAllClients(const nsACString& aScope,
+                           nsIConsoleReportCollector* aReporter);
+
   // Always consumes the error by reporting to consoles of all controlled
   // documents.
   void
   HandleError(JSContext* aCx,
               nsIPrincipal* aPrincipal,
               const nsCString& aScope,
               const nsString& aWorkerURL,
               const nsString& aMessage,
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -3437,16 +3437,61 @@ WorkerPrivateParent<Derived>::UpdateOver
 {
   AssertIsOnMainThread();
 
   // The load group should have been overriden at init time.
   mLoadInfo.mInterfaceRequestor->MaybeAddTabChild(aBaseLoadGroup);
 }
 
 template <class Derived>
+void
+WorkerPrivateParent<Derived>::FlushReportsToSharedWorkers(
+                                           nsIConsoleReportCollector* aReporter)
+{
+  AssertIsOnMainThread();
+
+  AutoTArray<RefPtr<SharedWorker>, 10> sharedWorkers;
+  AutoTArray<WindowAction, 10> windowActions;
+  GetAllSharedWorkers(sharedWorkers);
+
+  // First find out all the shared workers' window.
+  for (size_t index = 0; index < sharedWorkers.Length(); index++) {
+    RefPtr<SharedWorker>& sharedWorker = sharedWorkers[index];
+
+    // May be null.
+    nsPIDOMWindowInner* window = sharedWorker->GetOwner();
+
+    // Add the owning window to our list so that we will flush the reports later.
+    if (window && !windowActions.Contains(window)) {
+      windowActions.AppendElement(WindowAction(window));
+    }
+  }
+
+  bool reportErrorToBrowserConsole = true;
+
+  // Flush the reports.
+  for (uint32_t index = 0; index < windowActions.Length(); index++) {
+    WindowAction& windowAction = windowActions[index];
+
+    aReporter->FlushConsoleReports(windowAction.mWindow->GetExtantDoc(),
+                                   nsIConsoleReportCollector::ReportAction::Save);
+    reportErrorToBrowserConsole = false;
+  }
+
+  // Finally report to broswer console if there is no any window or shared
+  // worker.
+  if (reportErrorToBrowserConsole) {
+    aReporter->FlushConsoleReports((nsIDocument*)nullptr);
+    return;
+  }
+
+  aReporter->ClearConsoleReports();
+}
+
+template <class Derived>
 NS_IMPL_ADDREF_INHERITED(WorkerPrivateParent<Derived>, DOMEventTargetHelper)
 
 template <class Derived>
 NS_IMPL_RELEASE_INHERITED(WorkerPrivateParent<Derived>, DOMEventTargetHelper)
 
 template <class Derived>
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(WorkerPrivateParent<Derived>)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -13,16 +13,17 @@
 #include "nsIContentSecurityPolicy.h"
 #include "nsILoadGroup.h"
 #include "nsIWorkerDebugger.h"
 #include "nsPIDOMWindow.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/CondVar.h"
+#include "mozilla/ConsoleReportCollector.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/Move.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "nsAutoPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDataHashtable.h"
 #include "nsHashKeys.h"
@@ -35,16 +36,17 @@
 #include "Queue.h"
 #include "WorkerHolder.h"
 
 #ifdef XP_WIN
 #undef PostMessage
 #endif
 
 class nsIChannel;
+class nsIConsoleReportCollector;
 class nsIDocument;
 class nsIEventTarget;
 class nsIPrincipal;
 class nsIScriptContext;
 class nsIScriptTimeoutHandler;
 class nsISerializable;
 class nsIThread;
 class nsIThreadInternal;
@@ -806,16 +808,19 @@ public:
   UpdateOverridenLoadGroup(nsILoadGroup* aBaseLoadGroup);
 
   already_AddRefed<nsIRunnable>
   StealLoadFailedAsyncRunnable()
   {
     return mLoadInfo.mLoadFailedAsyncRunnable.forget();
   }
 
+  void
+  FlushReportsToSharedWorkers(nsIConsoleReportCollector* aReporter);
+
   IMPL_EVENT_HANDLER(message)
   IMPL_EVENT_HANDLER(error)
 
 #ifdef DEBUG
   void
   AssertIsOnParentThread() const;
 
   void
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -2562,27 +2562,41 @@ HttpBaseChannel::AddConsoleReport(uint32
 {
   mReportCollector->AddConsoleReport(aErrorFlags, aCategory, aPropertiesFile,
                                      aSourceFileURI, aLineNumber,
                                      aColumnNumber, aMessageName,
                                      aStringParams);
 }
 
 void
-HttpBaseChannel::FlushConsoleReports(nsIDocument* aDocument)
+HttpBaseChannel::FlushConsoleReports(nsIDocument* aDocument,
+                                     ReportAction aAction)
 {
-  mReportCollector->FlushConsoleReports(aDocument);
+  mReportCollector->FlushConsoleReports(aDocument, aAction);
 }
 
 void
 HttpBaseChannel::FlushConsoleReports(nsIConsoleReportCollector* aCollector)
 {
   mReportCollector->FlushConsoleReports(aCollector);
 }
 
+void
+HttpBaseChannel::FlushReportsByWindowId(uint64_t aWindowId,
+                                        ReportAction aAction)
+{
+  mReportCollector->FlushReportsByWindowId(aWindowId, aAction);
+}
+
+void
+HttpBaseChannel::ClearConsoleReports()
+{
+  mReportCollector->ClearConsoleReports();
+}
+
 nsIPrincipal *
 HttpBaseChannel::GetURIPrincipal()
 {
   if (mPrincipal) {
       return mPrincipal;
   }
 
   nsIScriptSecurityManager *securityManager =
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -258,21 +258,29 @@ public:
   AddConsoleReport(uint32_t aErrorFlags, const nsACString& aCategory,
                    nsContentUtils::PropertiesFile aPropertiesFile,
                    const nsACString& aSourceFileURI,
                    uint32_t aLineNumber, uint32_t aColumnNumber,
                    const nsACString& aMessageName,
                    const nsTArray<nsString>& aStringParams) override;
 
   void
-  FlushConsoleReports(nsIDocument* aDocument) override;
+  FlushConsoleReports(nsIDocument* aDocument,
+                      ReportAction aAction = ReportAction::Forget) override;
 
   void
   FlushConsoleReports(nsIConsoleReportCollector* aCollector) override;
 
+  void
+  FlushReportsByWindowId(uint64_t aWindowId,
+                         ReportAction aAction = ReportAction::Forget) override;
+
+  void
+  ClearConsoleReports() override;
+
   class nsContentEncodings : public nsIUTF8StringEnumerator
     {
     public:
         NS_DECL_ISUPPORTS
         NS_DECL_NSIUTF8STRINGENUMERATOR
 
         nsContentEncodings(nsIHttpChannel* aChannel, const char* aEncodingHeader);
 
--- a/netwerk/protocol/http/InterceptedChannel.cpp
+++ b/netwerk/protocol/http/InterceptedChannel.cpp
@@ -122,16 +122,18 @@ InterceptedChannelBase::GetConsoleReport
 }
 
 NS_IMETHODIMP
 InterceptedChannelBase::SetReleaseHandle(nsISupports* aHandle)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!mReleaseHandle);
   MOZ_ASSERT(aHandle);
+
+  // We need to keep it and mChannel alive until destructor clear it up.
   mReleaseHandle = aHandle;
   return NS_OK;
 }
 
 /* static */
 already_AddRefed<nsIURI>
 InterceptedChannelBase::SecureUpgradeChannelURI(nsIChannel* aChannel)
 {
@@ -195,19 +197,16 @@ InterceptedChannelChrome::ResetIntercept
   nsCOMPtr<nsIURI> uri;
   mChannel->GetURI(getter_AddRefs(uri));
 
   nsresult rv = mChannel->StartRedirectChannelToURI(uri, nsIChannelEventSink::REDIRECT_INTERNAL);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mResponseBody->Close();
   mResponseBody = nullptr;
-
-  mReleaseHandle = nullptr;
-  mChannel = nullptr;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 InterceptedChannelChrome::SynthesizeStatus(uint16_t aStatus, const nsACString& aReason)
 {
   if (!mSynthesizedCacheEntry) {
     return NS_ERROR_NOT_AVAILABLE;
@@ -295,18 +294,16 @@ InterceptedChannelChrome::FinishSynthesi
 
     mSynthesizedCacheEntry = nullptr;
 
     if (!mChannel->AwaitingCacheCallbacks()) {
       rv = mChannel->ContinueConnect();
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
-  mReleaseHandle = nullptr;
-  mChannel = nullptr;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 InterceptedChannelChrome::Cancel(nsresult aStatus)
 {
   MOZ_ASSERT(NS_FAILED(aStatus));
 
@@ -315,17 +312,16 @@ InterceptedChannelChrome::Cancel(nsresul
   }
 
   mReportCollector->FlushConsoleReports(mChannel);
 
   // we need to use AsyncAbort instead of Cancel since there's no active pump
   // to cancel which will provide OnStart/OnStopRequest to the channel.
   nsresult rv = mChannel->AsyncAbort(aStatus);
   NS_ENSURE_SUCCESS(rv, rv);
-  mReleaseHandle = nullptr;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 InterceptedChannelChrome::SetChannelInfo(dom::ChannelInfo* aChannelInfo)
 {
   if (!mChannel) {
     return NS_ERROR_FAILURE;
@@ -390,18 +386,16 @@ InterceptedChannelContent::ResetIntercep
 
   mReportCollector->FlushConsoleReports(mChannel);
 
   mResponseBody->Close();
   mResponseBody = nullptr;
   mSynthesizedInput = nullptr;
 
   mChannel->ResetInterception();
-  mReleaseHandle = nullptr;
-  mChannel = nullptr;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 InterceptedChannelContent::SynthesizeStatus(uint16_t aStatus, const nsACString& aReason)
 {
   if (!mResponseBody) {
     return NS_ERROR_NOT_AVAILABLE;
@@ -458,18 +452,16 @@ InterceptedChannelContent::FinishSynthes
     mChannel->BeginNonIPCRedirect(responseURI, *mSynthesizedResponseHead.ptr());
   } else {
     mChannel->OverrideWithSynthesizedResponse(mSynthesizedResponseHead.ref(),
                                               mSynthesizedInput,
                                               mStreamListener);
   }
 
   mResponseBody = nullptr;
-  mReleaseHandle = nullptr;
-  mChannel = nullptr;
   mStreamListener = nullptr;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 InterceptedChannelContent::Cancel(nsresult aStatus)
 {
   MOZ_ASSERT(NS_FAILED(aStatus));
@@ -479,18 +471,16 @@ InterceptedChannelContent::Cancel(nsresu
   }
 
   mReportCollector->FlushConsoleReports(mChannel);
 
   // we need to use AsyncAbort instead of Cancel since there's no active pump
   // to cancel which will provide OnStart/OnStopRequest to the channel.
   nsresult rv = mChannel->AsyncAbort(aStatus);
   NS_ENSURE_SUCCESS(rv, rv);
-  mReleaseHandle = nullptr;
-  mChannel = nullptr;
   mStreamListener = nullptr;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 InterceptedChannelContent::SetChannelInfo(dom::ChannelInfo* aChannelInfo)
 {
   if (!mChannel) {