Bug 1492036 - Reporting API - part 4 - Reporting, r=smaug
authorAndrea Marchesini <amarchesini@mozilla.com>
Wed, 14 Nov 2018 20:02:33 +0100
changeset 446483 c200472121fd75104ec9deb50d03d57655f9e668
parent 446482 a86e9cde89bbbbb7b6d755a9daacf2e03dc5947e
child 446484 3e311bf1af407110e07215872044d8451347ff8f
push id35042
push useraiakab@mozilla.com
push dateThu, 15 Nov 2018 09:54:38 +0000
treeherdermozilla-central@dca9c72df68b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1492036
milestone65.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 1492036 - Reporting API - part 4 - Reporting, r=smaug
dom/base/nsGlobalWindowInner.cpp
dom/base/nsPIDOMWindow.h
dom/bindings/BindingUtils.cpp
dom/reporting/Report.cpp
dom/reporting/Report.h
dom/reporting/ReportingObserver.cpp
dom/reporting/ReportingObserver.h
dom/reporting/ReportingUtils.cpp
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -232,16 +232,18 @@
 #include "mozilla/dom/WindowBinding.h"
 #include "nsITabChild.h"
 #include "mozilla/dom/MediaQueryList.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/NavigatorBinding.h"
 #include "mozilla/dom/ImageBitmap.h"
 #include "mozilla/dom/ImageBitmapBinding.h"
 #include "mozilla/dom/InstallTriggerBinding.h"
+#include "mozilla/dom/Report.h"
+#include "mozilla/dom/ReportingObserver.h"
 #include "mozilla/dom/ServiceWorker.h"
 #include "mozilla/dom/ServiceWorkerRegistration.h"
 #include "mozilla/dom/ServiceWorkerRegistrationDescriptor.h"
 #include "mozilla/dom/U2F.h"
 #include "mozilla/dom/WebIDLGlobalNameHash.h"
 #include "mozilla/dom/Worklet.h"
 #ifdef HAVE_SIDEBAR
 #include "mozilla/dom/ExternalBinding.h"
@@ -332,16 +334,19 @@ using mozilla::dom::cache::CacheStorage;
 #define MAX_SUCCESSIVE_DIALOG_COUNT 5
 
 // Idle fuzz time upper limit
 #define MAX_IDLE_FUZZ_TIME_MS 90000
 
 // Min idle notification time in seconds.
 #define MIN_IDLE_NOTIFICATION_TIME_S 1
 
+// Max number of Report objects
+#define MAX_REPORT_RECORDS 100
+
 static LazyLogModule gDOMLeakPRLogInner("DOMLeakInner");
 
 static bool                 gIdleObserversAPIFuzzTimeDisabled = false;
 static FILE                *gDumpFile                         = nullptr;
 
 nsGlobalWindowInner::InnerWindowByIdTable *nsGlobalWindowInner::sInnerWindowsById = nullptr;
 
 bool nsGlobalWindowInner::sDragServiceDisabled = false;
@@ -1464,16 +1469,18 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScrollbars)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrypto)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mU2F)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPaintWorklet)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExternal)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInstallTrigger)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIntlUtils)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReportRecords)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReportingObservers)
 
   tmp->TraverseHostObjectURIs(cb);
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mMessageManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeFields.mGroupMessageManagers)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingPromises)
 
@@ -1553,16 +1560,18 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mScrollbars)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrypto)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mU2F)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPaintWorklet)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mExternal)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mInstallTrigger)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mIntlUtils)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mReportRecords)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mReportingObservers)
 
   tmp->UnlinkHostObjectURIs();
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mIdleRequestExecutor)
 
   // Here the IdleRequest list would've been unlinked, but we rely on
   // that IdleRequest objects have been traced and will remove
   // themselves while unlinking.
@@ -8074,13 +8083,69 @@ nsPIDOMWindowInner::nsPIDOMWindowInner(n
   mHasTriedToCacheTopInnerWindow(false),
   mNumOfIndexedDBDatabases(0),
   mNumOfOpenWebSockets(0),
   mEvent(nullptr)
 {
   MOZ_ASSERT(aOuterWindow);
 }
 
+void
+nsPIDOMWindowInner::RegisterReportingObserver(ReportingObserver* aObserver,
+                                              bool aBuffered)
+{
+  MOZ_ASSERT(aObserver);
+
+  if (mReportingObservers.Contains(aObserver)) {
+    return;
+  }
+
+  if (NS_WARN_IF(!mReportingObservers.AppendElement(aObserver, fallible))) {
+    return;
+  }
+
+  if (!aBuffered) {
+    return;
+  }
+
+  for (Report* report : mReportRecords) {
+    aObserver->MaybeReport(report);
+  }
+}
+
+void
+nsPIDOMWindowInner::UnregisterReportingObserver(ReportingObserver* aObserver)
+{
+  MOZ_ASSERT(aObserver);
+  mReportingObservers.RemoveElement(aObserver);
+}
+
+void
+nsPIDOMWindowInner::BroadcastReport(Report* aReport)
+{
+  MOZ_ASSERT(aReport);
+
+  for (ReportingObserver* observer : mReportingObservers) {
+    observer->MaybeReport(aReport);
+  }
+
+  if (NS_WARN_IF(!mReportRecords.AppendElement(aReport, fallible))) {
+    return;
+  }
+
+  while (mReportRecords.Length() > MAX_REPORT_RECORDS) {
+    mReportRecords.RemoveElementAt(0);
+  }
+}
+
+void
+nsPIDOMWindowInner::NotifyReportingObservers()
+{
+  for (ReportingObserver* observer : mReportingObservers) {
+    observer->MaybeNotify();
+  }
+}
+
 nsPIDOMWindowInner::~nsPIDOMWindowInner() {}
 
 #undef FORWARD_TO_OUTER
 #undef FORWARD_TO_OUTER_OR_THROW
 #undef FORWARD_TO_OUTER_VOID
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -53,16 +53,19 @@ class ClientInfo;
 class ClientState;
 class ContentFrameMessageManager;
 class DocGroup;
 class TabGroup;
 class Element;
 class MozIdleObserver;
 class Navigator;
 class Performance;
+class Report;
+class ReportBody;
+class ReportingObserver;
 class Selection;
 class ServiceWorker;
 class ServiceWorkerDescriptor;
 class Timeout;
 class TimeoutManager;
 class CustomElementRegistry;
 enum class CallerType : uint32_t;
 } // namespace dom
@@ -613,16 +616,29 @@ public:
   virtual nsISerialEventTarget*
   EventTargetFor(mozilla::TaskCategory aCategory) const = 0;
 
   // Returns the AutoplayPermissionManager that documents in this window should
   // use to request permission to autoplay.
   already_AddRefed<mozilla::AutoplayPermissionManager>
   GetAutoplayPermissionManager();
 
+  void
+  RegisterReportingObserver(mozilla::dom::ReportingObserver* aObserver,
+                            bool aBuffered);
+
+  void
+  UnregisterReportingObserver(mozilla::dom::ReportingObserver* aObserver);
+
+  void
+  BroadcastReport(mozilla::dom::Report* aReport);
+
+  void
+  NotifyReportingObservers();
+
 protected:
   void CreatePerformanceObjectIfNeeded();
 
   // Lazily instantiate an about:blank document if necessary, and if
   // we have what it takes to do so.
   void MaybeCreateDoc();
 
   void SetChromeEventHandlerInternal(mozilla::dom::EventTarget* aChromeEventHandler) {
@@ -704,16 +720,20 @@ protected:
   // If we're in the process of requesting permission for this window to
   // play audible media, or we've already been granted permission by the
   // user, this is non-null, and encapsulates the request.
   RefPtr<mozilla::AutoplayPermissionManager> mAutoplayPermissionManager;
 
   // The event dispatch code sets and unsets this while keeping
   // the event object alive.
   mozilla::dom::Event* mEvent;
+
+  // List of Report objects for ReportingObservers.
+  nsTArray<RefPtr<mozilla::dom::ReportingObserver>> mReportingObservers;
+  nsTArray<RefPtr<mozilla::dom::Report>> mReportRecords;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMWindowInner, NS_PIDOMWINDOWINNER_IID)
 
 class nsPIDOMWindowOuter : public mozIDOMWindowProxy
 {
 protected:
   explicit nsPIDOMWindowOuter(uint64_t aWindowID);
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -25,16 +25,17 @@
 #include "nsContentUtils.h"
 #include "nsGlobalWindow.h"
 #include "nsHTMLTags.h"
 #include "nsIDocShell.h"
 #include "nsIDOMGlobalPropertyInitializer.h"
 #include "nsINode.h"
 #include "nsIPermissionManager.h"
 #include "nsIPrincipal.h"
+#include "nsIURIFixup.h"
 #include "nsIXPConnect.h"
 #include "nsUTF8Utils.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 #include "WrapperFactory.h"
 #include "xpcprivate.h"
 #include "XrayWrapper.h"
 #include "nsPrintfCString.h"
@@ -4117,18 +4118,32 @@ void
 ReportDeprecation(nsPIDOMWindowInner* aWindow, nsIURI* aURI,
                   nsIDocument::DeprecatedOperations aOperation,
                   const nsAString& aFileName,
                   const Nullable<uint32_t>& aLineNumber,
                   const Nullable<uint32_t>& aColumnNumber)
 {
   MOZ_ASSERT(aURI);
 
+  // Anonymize the URL.
+  // Strip the URL of any possible username/password and make it ready to be
+  // presented in the UI.
+  nsCOMPtr<nsIURIFixup> urifixup = services::GetURIFixup();
+  if (NS_WARN_IF(!urifixup)) {
+    return;
+  }
+
+  nsCOMPtr<nsIURI> exposableURI;
+  nsresult rv = urifixup->CreateExposableURI(aURI, getter_AddRefs(exposableURI));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
   nsAutoCString spec;
-  nsresult rv = aURI->GetSpec(spec);
+  rv = exposableURI->GetSpec(spec);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 
   nsAutoString type;
   type.AssignASCII(kDeprecatedOperations[aOperation]);
 
   nsAutoCString key;
--- a/dom/reporting/Report.cpp
+++ b/dom/reporting/Report.cpp
@@ -29,16 +29,24 @@ Report::Report(nsPIDOMWindowInner* aWind
   , mURL(aURL)
   , mBody(aBody)
 {
   MOZ_ASSERT(aWindow);
 }
 
 Report::~Report() = default;
 
+already_AddRefed<Report>
+Report::Clone()
+{
+  RefPtr<Report> report =
+    new Report(mWindow, mType, mURL, mBody);
+  return report.forget();
+}
+
 JSObject*
 Report::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return Report_Binding::Wrap(aCx, this, aGivenProto);
 }
 
 void
 Report::GetType(nsAString& aType) const
--- a/dom/reporting/Report.h
+++ b/dom/reporting/Report.h
@@ -24,16 +24,19 @@ public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Report)
 
   Report(nsPIDOMWindowInner* aWindow,
          const nsAString& aType,
          const nsAString& aURL,
          ReportBody* aBody);
 
+  already_AddRefed<Report>
+  Clone();
+
   JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   nsPIDOMWindowInner*
   GetParentObject() const
   {
     return mWindow;
   }
--- a/dom/reporting/ReportingObserver.cpp
+++ b/dom/reporting/ReportingObserver.cpp
@@ -10,22 +10,24 @@
 #include "nsGlobalWindowInner.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(ReportingObserver)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ReportingObserver)
   tmp->Disconnect();
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mReports)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ReportingObserver)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReports)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(ReportingObserver)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(ReportingObserver)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(ReportingObserver)
 
@@ -74,25 +76,88 @@ JSObject*
 ReportingObserver::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return ReportingObserver_Binding::Wrap(aCx, this, aGivenProto);
 }
 
 void
 ReportingObserver::Observe()
 {
-  // TODO
+  mWindow->RegisterReportingObserver(this, mBuffered);
 }
 
 void
 ReportingObserver::Disconnect()
 {
-  // TODO
+  if (mWindow) {
+    mWindow->UnregisterReportingObserver(this);
+  }
 }
 
 void
 ReportingObserver::TakeRecords(nsTArray<RefPtr<Report>>& aRecords)
 {
-  // TODO
+  mReports.SwapElements(aRecords);
+}
+
+void
+ReportingObserver::MaybeReport(Report* aReport)
+{
+  MOZ_ASSERT(aReport);
+
+  if (!mTypes.IsEmpty()) {
+    nsAutoString type;
+    aReport->GetType(type);
+
+    if (!mTypes.Contains(type)) {
+      return;
+    }
+  }
+
+  bool wasEmpty = mReports.IsEmpty();
+
+  RefPtr<Report> report = aReport->Clone();
+  MOZ_ASSERT(report);
+
+  if (NS_WARN_IF(!mReports.AppendElement(report, fallible))) {
+    return;
+  }
+
+  if (!wasEmpty) {
+    return;
+  }
+
+  nsCOMPtr<nsPIDOMWindowInner> window = mWindow;
+
+  nsCOMPtr<nsIRunnable> r =
+    NS_NewRunnableFunction(
+      "ReportingObserver::MaybeReport",
+      [window]() {
+        window->NotifyReportingObservers();
+      });
+
+  NS_DispatchToCurrentThread(r);
+}
+
+void
+ReportingObserver::MaybeNotify()
+{
+  if (mReports.IsEmpty()) {
+    return;
+  }
+
+  // Let's take the ownership of the reports.
+  nsTArray<RefPtr<Report>> list;
+  list.SwapElements(mReports);
+
+  Sequence<OwningNonNull<Report>> reports;
+  for (Report* report : list) {
+    if (NS_WARN_IF(!reports.AppendElement(*report, fallible))) {
+      return;
+    }
+  }
+
+  // We should report if this throws exception. But where?
+  mCallback->Call(reports, *this);
 }
 
 } // dom namespace
 } // mozilla namespace
--- a/dom/reporting/ReportingObserver.h
+++ b/dom/reporting/ReportingObserver.h
@@ -6,22 +6,27 @@
 
 #ifndef mozilla_dom_ReportingObserver_h
 #define mozilla_dom_ReportingObserver_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsWrapperCache.h"
+#include "nsTArray.h"
 
 class nsPIDOMWindowInner;
 
 namespace mozilla {
 namespace dom {
 
+class Report;
+class ReportingObserverCallback;
+struct ReportingObserverOptions;
+
 class ReportingObserver final : public nsISupports
                               , public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ReportingObserver)
 
   static already_AddRefed<ReportingObserver>
@@ -48,19 +53,27 @@ public:
   Observe();
 
   void
   Disconnect();
 
   void
   TakeRecords(nsTArray<RefPtr<Report>>& aRecords);
 
+  void
+  MaybeReport(Report* aReport);
+
+  void
+  MaybeNotify();
+
 private:
   ~ReportingObserver();
 
+  nsTArray<RefPtr<Report>> mReports;
+
   nsCOMPtr<nsPIDOMWindowInner> mWindow;
   RefPtr<ReportingObserverCallback> mCallback;
   nsTArray<nsString> mTypes;
   bool mBuffered;
 };
 
 } // dom namespace
 } // mozilla namespace
--- a/dom/reporting/ReportingUtils.cpp
+++ b/dom/reporting/ReportingUtils.cpp
@@ -1,23 +1,33 @@
 /* -*- 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/dom/ReportingUtils.h"
 #include "mozilla/dom/ReportBody.h"
+#include "mozilla/dom/Report.h"
+#include "nsAtom.h"
+#include "nsPIDOMWindow.h"
 
 namespace mozilla {
 namespace dom {
 
 /* static */ void
 ReportingUtils::Report(nsPIDOMWindowInner* aWindow,
                        nsAtom* aType,
                        const nsAString& aURL,
                        ReportBody* aBody)
 {
-  // TODO
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aWindow);
+  MOZ_ASSERT(aBody);
+
+  RefPtr<mozilla::dom::Report> report =
+    new mozilla::dom::Report(aWindow, nsDependentAtomString(aType), aURL,
+                             aBody);
+  aWindow->BroadcastReport(report);
 }
 
 } // dom namespace
 } // mozilla namespace