ReportingObserver.cpp (4335B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "mozilla/dom/ReportingObserver.h" 8 9 #include "mozilla/dom/Report.h" 10 #include "mozilla/dom/ReportingBinding.h" 11 #include "nsContentUtils.h" 12 #include "nsIGlobalObject.h" 13 #include "nsThreadUtils.h" 14 15 namespace mozilla::dom { 16 17 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(ReportingObserver) 18 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ReportingObserver) 19 tmp->Disconnect(); 20 NS_IMPL_CYCLE_COLLECTION_UNLINK(mReports) 21 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal) 22 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback) 23 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER 24 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 25 26 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ReportingObserver) 27 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReports) 28 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal) 29 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback) 30 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 31 32 /* static */ 33 already_AddRefed<ReportingObserver> ReportingObserver::Constructor( 34 const GlobalObject& aGlobal, ReportingObserverCallback& aCallback, 35 const ReportingObserverOptions& aOptions, ErrorResult& aRv) { 36 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); 37 MOZ_ASSERT(global); 38 39 nsTArray<nsString> types; 40 if (aOptions.mTypes.WasPassed()) { 41 types = aOptions.mTypes.Value(); 42 } 43 44 RefPtr<ReportingObserver> ro = 45 new ReportingObserver(global, aCallback, types, aOptions.mBuffered); 46 47 return ro.forget(); 48 } 49 50 ReportingObserver::ReportingObserver(nsIGlobalObject* aGlobal, 51 ReportingObserverCallback& aCallback, 52 const nsTArray<nsString>& aTypes, 53 bool aBuffered) 54 : mGlobal(aGlobal), 55 mCallback(&aCallback), 56 mTypes(aTypes.Clone()), 57 mBuffered(aBuffered) { 58 MOZ_ASSERT(aGlobal); 59 } 60 61 ReportingObserver::~ReportingObserver() { Disconnect(); } 62 63 JSObject* ReportingObserver::WrapObject(JSContext* aCx, 64 JS::Handle<JSObject*> aGivenProto) { 65 return ReportingObserver_Binding::Wrap(aCx, this, aGivenProto); 66 } 67 68 void ReportingObserver::Observe() { 69 mGlobal->RegisterReportingObserver(this, mBuffered); 70 } 71 72 void ReportingObserver::Disconnect() { 73 if (mGlobal) { 74 mGlobal->UnregisterReportingObserver(this); 75 } 76 } 77 78 void ReportingObserver::TakeRecords(nsTArray<RefPtr<Report>>& aRecords) { 79 mReports.SwapElements(aRecords); 80 } 81 82 namespace { 83 84 class ReportRunnable final : public DiscardableRunnable { 85 public: 86 explicit ReportRunnable(nsIGlobalObject* aGlobal) 87 : DiscardableRunnable("ReportRunnable"), mGlobal(aGlobal) {} 88 89 // MOZ_CAN_RUN_SCRIPT_BOUNDARY until Runnable::Run is MOZ_CAN_RUN_SCRIPT. See 90 // bug 1535398. 91 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override { 92 MOZ_KnownLive(mGlobal)->NotifyReportingObservers(); 93 return NS_OK; 94 } 95 96 private: 97 const nsCOMPtr<nsIGlobalObject> mGlobal; 98 }; 99 100 } // namespace 101 102 void ReportingObserver::MaybeReport(Report* aReport) { 103 MOZ_ASSERT(aReport); 104 105 if (!mTypes.IsEmpty()) { 106 nsAutoString type; 107 aReport->GetType(type); 108 109 if (!mTypes.Contains(type)) { 110 return; 111 } 112 } 113 114 bool wasEmpty = mReports.IsEmpty(); 115 116 RefPtr<Report> report = aReport->Clone(); 117 MOZ_ASSERT(report); 118 119 if (NS_WARN_IF(!mReports.AppendElement(report, fallible))) { 120 return; 121 } 122 123 if (!wasEmpty) { 124 return; 125 } 126 127 RefPtr<ReportRunnable> r = new ReportRunnable(mGlobal); 128 NS_DispatchToCurrentThread(r); 129 } 130 131 void ReportingObserver::MaybeNotify() { 132 if (mReports.IsEmpty()) { 133 return; 134 } 135 136 // Let's take the ownership of the reports. 137 nsTArray<RefPtr<Report>> list = std::move(mReports); 138 139 Sequence<OwningNonNull<Report>> reports; 140 for (Report* report : list) { 141 if (NS_WARN_IF(!reports.AppendElement(*report, fallible))) { 142 return; 143 } 144 } 145 146 // We should report if this throws exception. But where? 147 RefPtr<ReportingObserverCallback> callback(mCallback); 148 callback->Call(reports, *this); 149 } 150 151 void ReportingObserver::ForgetReports() { mReports.Clear(); } 152 153 } // namespace mozilla::dom