/* -*- 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/ReportingObserver.h" #include "mozilla/dom/Report.h" #include "mozilla/dom/ReportingBinding.h" #include "nsContentUtils.h" #include "nsIGlobalObject.h" #include "nsThreadUtils.h" namespace mozilla::dom { NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(ReportingObserver) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ReportingObserver) tmp->Disconnect(); NS_IMPL_CYCLE_COLLECTION_UNLINK(mReports) NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal) 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(mGlobal) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END /* static */ already_AddRefed<ReportingObserver> ReportingObserver::Constructor( const GlobalObject& aGlobal, ReportingObserverCallback& aCallback, const ReportingObserverOptions& aOptions, ErrorResult& aRv) { nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); MOZ_ASSERT(global); nsTArray<nsString> types; if (aOptions.mTypes.WasPassed()) { types = aOptions.mTypes.Value(); } RefPtr<ReportingObserver> ro = new ReportingObserver(global, aCallback, types, aOptions.mBuffered); return ro.forget(); } ReportingObserver::ReportingObserver(nsIGlobalObject* aGlobal, ReportingObserverCallback& aCallback, const nsTArray<nsString>& aTypes, bool aBuffered) : mGlobal(aGlobal), mCallback(&aCallback), mTypes(aTypes.Clone()), mBuffered(aBuffered) { MOZ_ASSERT(aGlobal); } ReportingObserver::~ReportingObserver() { Disconnect(); } JSObject* ReportingObserver::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { return ReportingObserver_Binding::Wrap(aCx, this, aGivenProto); } void ReportingObserver::Observe() { mGlobal->RegisterReportingObserver(this, mBuffered); } void ReportingObserver::Disconnect() { if (mGlobal) { mGlobal->UnregisterReportingObserver(this); } } void ReportingObserver::TakeRecords(nsTArray<RefPtr<Report>>& aRecords) { mReports.SwapElements(aRecords); } namespace { class ReportRunnable final : public DiscardableRunnable { public: explicit ReportRunnable(nsIGlobalObject* aGlobal) : DiscardableRunnable("ReportRunnable"), mGlobal(aGlobal) {} // MOZ_CAN_RUN_SCRIPT_BOUNDARY until Runnable::Run is MOZ_CAN_RUN_SCRIPT. See // bug 1535398. MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override { MOZ_KnownLive(mGlobal)->NotifyReportingObservers(); return NS_OK; } private: const nsCOMPtr<nsIGlobalObject> mGlobal; }; } // namespace 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; } RefPtr<ReportRunnable> r = new ReportRunnable(mGlobal); NS_DispatchToCurrentThread(r); } void ReportingObserver::MaybeNotify() { if (mReports.IsEmpty()) { return; } // Let's take the ownership of the reports. nsTArray<RefPtr<Report>> list = std::move(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? RefPtr<ReportingObserverCallback> callback(mCallback); callback->Call(reports, *this); } void ReportingObserver::ForgetReports() { mReports.Clear(); } } // namespace mozilla::dom