Bug 1472338: part 2) Change `clipboard.readText()` to read from the clipboard asynchr...
[gecko.git] / dom / reporting / ReportingHeader.cpp
blobd22cc3d4eca2e6063d9139d014106a528c25a849
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 file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/ReportingHeader.h"
9 #include "js/Array.h" // JS::GetArrayLength, JS::IsArrayObject
10 #include "js/JSON.h"
11 #include "js/PropertyAndElement.h" // JS_GetElement
12 #include "mozilla/dom/ReportingBinding.h"
13 #include "mozilla/dom/ScriptSettings.h"
14 #include "mozilla/dom/SimpleGlobalObject.h"
15 #include "mozilla/ipc/BackgroundUtils.h"
16 #include "mozilla/OriginAttributes.h"
17 #include "mozilla/Services.h"
18 #include "mozilla/StaticPrefs_dom.h"
19 #include "mozilla/StaticPtr.h"
20 #include "nsCOMPtr.h"
21 #include "nsContentUtils.h"
22 #include "nsIEffectiveTLDService.h"
23 #include "nsIHttpChannel.h"
24 #include "nsIHttpProtocolHandler.h"
25 #include "nsIObserverService.h"
26 #include "nsIPrincipal.h"
27 #include "nsIRandomGenerator.h"
28 #include "nsIScriptError.h"
29 #include "nsNetUtil.h"
30 #include "nsXULAppAPI.h"
32 #define REPORTING_PURGE_ALL "reporting:purge-all"
33 #define REPORTING_PURGE_HOST "reporting:purge-host"
35 namespace mozilla {
36 namespace dom {
38 namespace {
40 StaticRefPtr<ReportingHeader> gReporting;
42 } // namespace
44 /* static */
45 void ReportingHeader::Initialize() {
46 MOZ_ASSERT(!gReporting);
47 MOZ_ASSERT(NS_IsMainThread());
49 if (!XRE_IsParentProcess()) {
50 return;
53 RefPtr<ReportingHeader> service = new ReportingHeader();
55 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
56 if (NS_WARN_IF(!obs)) {
57 return;
60 obs->AddObserver(service, NS_HTTP_ON_EXAMINE_RESPONSE_TOPIC, false);
61 obs->AddObserver(service, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
62 obs->AddObserver(service, "clear-origin-attributes-data", false);
63 obs->AddObserver(service, REPORTING_PURGE_HOST, false);
64 obs->AddObserver(service, REPORTING_PURGE_ALL, false);
66 gReporting = service;
69 /* static */
70 void ReportingHeader::Shutdown() {
71 MOZ_ASSERT(NS_IsMainThread());
73 if (!gReporting) {
74 return;
77 RefPtr<ReportingHeader> service = gReporting;
78 gReporting = nullptr;
80 if (service->mCleanupTimer) {
81 service->mCleanupTimer->Cancel();
82 service->mCleanupTimer = nullptr;
85 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
86 if (NS_WARN_IF(!obs)) {
87 return;
90 obs->RemoveObserver(service, NS_HTTP_ON_EXAMINE_RESPONSE_TOPIC);
91 obs->RemoveObserver(service, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
92 obs->RemoveObserver(service, "clear-origin-attributes-data");
93 obs->RemoveObserver(service, REPORTING_PURGE_HOST);
94 obs->RemoveObserver(service, REPORTING_PURGE_ALL);
97 ReportingHeader::ReportingHeader() = default;
98 ReportingHeader::~ReportingHeader() = default;
100 NS_IMETHODIMP
101 ReportingHeader::Observe(nsISupports* aSubject, const char* aTopic,
102 const char16_t* aData) {
103 if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
104 Shutdown();
105 return NS_OK;
108 // Pref disabled.
109 if (!StaticPrefs::dom_reporting_header_enabled()) {
110 return NS_OK;
113 if (!strcmp(aTopic, NS_HTTP_ON_EXAMINE_RESPONSE_TOPIC)) {
114 nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aSubject);
115 if (NS_WARN_IF(!channel)) {
116 return NS_OK;
119 ReportingFromChannel(channel);
120 return NS_OK;
123 if (!strcmp(aTopic, REPORTING_PURGE_HOST)) {
124 RemoveOriginsFromHost(nsDependentString(aData));
125 return NS_OK;
128 if (!strcmp(aTopic, "clear-origin-attributes-data")) {
129 OriginAttributesPattern pattern;
130 if (!pattern.Init(nsDependentString(aData))) {
131 NS_ERROR("Cannot parse origin attributes pattern");
132 return NS_ERROR_FAILURE;
135 RemoveOriginsFromOriginAttributesPattern(pattern);
136 return NS_OK;
139 if (!strcmp(aTopic, REPORTING_PURGE_ALL)) {
140 RemoveOrigins();
141 return NS_OK;
144 return NS_ERROR_FAILURE;
147 void ReportingHeader::ReportingFromChannel(nsIHttpChannel* aChannel) {
148 MOZ_ASSERT(aChannel);
150 if (!StaticPrefs::dom_reporting_header_enabled()) {
151 return;
154 // We want to use the final URI to check if Report-To should be allowed or
155 // not.
156 nsCOMPtr<nsIURI> uri;
157 nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
158 if (NS_WARN_IF(NS_FAILED(rv))) {
159 return;
162 if (!IsSecureURI(uri)) {
163 return;
166 if (NS_UsePrivateBrowsing(aChannel)) {
167 return;
170 nsAutoCString headerValue;
171 rv = aChannel->GetResponseHeader("Report-To"_ns, headerValue);
172 if (NS_FAILED(rv)) {
173 return;
176 nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
177 if (NS_WARN_IF(!ssm)) {
178 return;
181 nsCOMPtr<nsIPrincipal> principal;
182 rv = ssm->GetChannelURIPrincipal(aChannel, getter_AddRefs(principal));
183 if (NS_WARN_IF(NS_FAILED(rv)) || !principal) {
184 return;
187 nsAutoCString origin;
188 rv = principal->GetOrigin(origin);
189 if (NS_WARN_IF(NS_FAILED(rv))) {
190 return;
193 UniquePtr<Client> client = ParseHeader(aChannel, uri, headerValue);
194 if (!client) {
195 return;
198 // Here we override the previous data.
199 mOrigins.InsertOrUpdate(origin, std::move(client));
201 MaybeCreateCleanupTimer();
204 /* static */ UniquePtr<ReportingHeader::Client> ReportingHeader::ParseHeader(
205 nsIHttpChannel* aChannel, nsIURI* aURI, const nsACString& aHeaderValue) {
206 MOZ_ASSERT(aURI);
207 // aChannel can be null in gtest
209 AutoJSAPI jsapi;
211 JSObject* cleanGlobal =
212 SimpleGlobalObject::Create(SimpleGlobalObject::GlobalType::BindingDetail);
213 if (NS_WARN_IF(!cleanGlobal)) {
214 return nullptr;
217 if (NS_WARN_IF(!jsapi.Init(cleanGlobal))) {
218 return nullptr;
221 // WebIDL dictionary parses single items. Let's create a object to parse the
222 // header.
223 nsAutoString json;
224 json.AppendASCII("{ \"items\": [");
225 json.Append(NS_ConvertUTF8toUTF16(aHeaderValue));
226 json.AppendASCII("]}");
228 JSContext* cx = jsapi.cx();
229 JS::Rooted<JS::Value> jsonValue(cx);
230 bool ok = JS_ParseJSON(cx, json.BeginReading(), json.Length(), &jsonValue);
231 if (!ok) {
232 LogToConsoleInvalidJSON(aChannel, aURI);
233 return nullptr;
236 dom::ReportingHeaderValue data;
237 if (!data.Init(cx, jsonValue)) {
238 LogToConsoleInvalidJSON(aChannel, aURI);
239 return nullptr;
242 if (!data.mItems.WasPassed() || data.mItems.Value().IsEmpty()) {
243 return nullptr;
246 UniquePtr<Client> client = MakeUnique<Client>();
248 for (const dom::ReportingItem& item : data.mItems.Value()) {
249 nsAutoString groupName;
251 if (item.mGroup.isUndefined()) {
252 groupName.AssignLiteral("default");
253 } else if (!item.mGroup.isString()) {
254 LogToConsoleInvalidNameItem(aChannel, aURI);
255 continue;
256 } else {
257 JS::Rooted<JSString*> groupStr(cx, item.mGroup.toString());
258 MOZ_ASSERT(groupStr);
260 nsAutoJSString string;
261 if (NS_WARN_IF(!string.init(cx, groupStr))) {
262 continue;
265 groupName = string;
268 if (!item.mMax_age.isNumber() || !item.mEndpoints.isObject()) {
269 LogToConsoleIncompleteItem(aChannel, aURI, groupName);
270 continue;
273 JS::Rooted<JSObject*> endpoints(cx, &item.mEndpoints.toObject());
274 MOZ_ASSERT(endpoints);
276 bool isArray = false;
277 if (!JS::IsArrayObject(cx, endpoints, &isArray) || !isArray) {
278 LogToConsoleIncompleteItem(aChannel, aURI, groupName);
279 continue;
282 uint32_t endpointsLength;
283 if (!JS::GetArrayLength(cx, endpoints, &endpointsLength) ||
284 endpointsLength == 0) {
285 LogToConsoleIncompleteItem(aChannel, aURI, groupName);
286 continue;
289 const auto [begin, end] = client->mGroups.NonObservingRange();
290 if (std::any_of(begin, end, [&groupName](const Group& group) {
291 return group.mName == groupName;
292 })) {
293 LogToConsoleDuplicateGroup(aChannel, aURI, groupName);
294 continue;
297 Group* group = client->mGroups.AppendElement();
298 group->mName = groupName;
299 group->mIncludeSubdomains = item.mInclude_subdomains;
300 group->mTTL = item.mMax_age.toNumber();
301 group->mCreationTime = TimeStamp::Now();
303 for (uint32_t i = 0; i < endpointsLength; ++i) {
304 JS::Rooted<JS::Value> element(cx);
305 if (!JS_GetElement(cx, endpoints, i, &element)) {
306 return nullptr;
309 RootedDictionary<ReportingEndpoint> endpoint(cx);
310 if (!endpoint.Init(cx, element)) {
311 LogToConsoleIncompleteEndpoint(aChannel, aURI, groupName);
312 continue;
315 if (!endpoint.mUrl.isString() ||
316 (!endpoint.mPriority.isUndefined() &&
317 (!endpoint.mPriority.isNumber() ||
318 endpoint.mPriority.toNumber() < 0)) ||
319 (!endpoint.mWeight.isUndefined() &&
320 (!endpoint.mWeight.isNumber() || endpoint.mWeight.toNumber() < 0))) {
321 LogToConsoleIncompleteEndpoint(aChannel, aURI, groupName);
322 continue;
325 JS::Rooted<JSString*> endpointUrl(cx, endpoint.mUrl.toString());
326 MOZ_ASSERT(endpointUrl);
328 nsAutoJSString endpointString;
329 if (NS_WARN_IF(!endpointString.init(cx, endpointUrl))) {
330 continue;
333 nsCOMPtr<nsIURI> uri;
334 nsresult rv = NS_NewURI(getter_AddRefs(uri), endpointString);
335 if (NS_FAILED(rv)) {
336 LogToConsoleInvalidURLEndpoint(aChannel, aURI, groupName,
337 endpointString);
338 continue;
341 Endpoint* ep = group->mEndpoints.AppendElement();
342 ep->mUrl = uri;
343 ep->mPriority =
344 endpoint.mPriority.isUndefined() ? 1 : endpoint.mPriority.toNumber();
345 ep->mWeight =
346 endpoint.mWeight.isUndefined() ? 1 : endpoint.mWeight.toNumber();
350 if (client->mGroups.IsEmpty()) {
351 return nullptr;
354 return client;
357 bool ReportingHeader::IsSecureURI(nsIURI* aURI) const {
358 MOZ_ASSERT(aURI);
360 bool prioriAuthenticated = false;
361 if (NS_WARN_IF(NS_FAILED(NS_URIChainHasFlags(
362 aURI, nsIProtocolHandler::URI_IS_POTENTIALLY_TRUSTWORTHY,
363 &prioriAuthenticated)))) {
364 return false;
367 return prioriAuthenticated;
370 /* static */
371 void ReportingHeader::LogToConsoleInvalidJSON(nsIHttpChannel* aChannel,
372 nsIURI* aURI) {
373 nsTArray<nsString> params;
374 LogToConsoleInternal(aChannel, aURI, "ReportingHeaderInvalidJSON", params);
377 /* static */
378 void ReportingHeader::LogToConsoleDuplicateGroup(nsIHttpChannel* aChannel,
379 nsIURI* aURI,
380 const nsAString& aName) {
381 nsTArray<nsString> params;
382 params.AppendElement(aName);
384 LogToConsoleInternal(aChannel, aURI, "ReportingHeaderDuplicateGroup", params);
387 /* static */
388 void ReportingHeader::LogToConsoleInvalidNameItem(nsIHttpChannel* aChannel,
389 nsIURI* aURI) {
390 nsTArray<nsString> params;
391 LogToConsoleInternal(aChannel, aURI, "ReportingHeaderInvalidNameItem",
392 params);
395 /* static */
396 void ReportingHeader::LogToConsoleIncompleteItem(nsIHttpChannel* aChannel,
397 nsIURI* aURI,
398 const nsAString& aName) {
399 nsTArray<nsString> params;
400 params.AppendElement(aName);
402 LogToConsoleInternal(aChannel, aURI, "ReportingHeaderInvalidItem", params);
405 /* static */
406 void ReportingHeader::LogToConsoleIncompleteEndpoint(nsIHttpChannel* aChannel,
407 nsIURI* aURI,
408 const nsAString& aName) {
409 nsTArray<nsString> params;
410 params.AppendElement(aName);
412 LogToConsoleInternal(aChannel, aURI, "ReportingHeaderInvalidEndpoint",
413 params);
416 /* static */
417 void ReportingHeader::LogToConsoleInvalidURLEndpoint(nsIHttpChannel* aChannel,
418 nsIURI* aURI,
419 const nsAString& aName,
420 const nsAString& aURL) {
421 nsTArray<nsString> params;
422 params.AppendElement(aURL);
423 params.AppendElement(aName);
425 LogToConsoleInternal(aChannel, aURI, "ReportingHeaderInvalidURLEndpoint",
426 params);
429 /* static */
430 void ReportingHeader::LogToConsoleInternal(nsIHttpChannel* aChannel,
431 nsIURI* aURI, const char* aMsg,
432 const nsTArray<nsString>& aParams) {
433 MOZ_ASSERT(aURI);
435 if (!aChannel) {
436 // We are in a gtest.
437 return;
440 uint64_t windowID = 0;
442 nsresult rv = aChannel->GetTopLevelContentWindowId(&windowID);
443 if (NS_WARN_IF(NS_FAILED(rv))) {
444 return;
447 if (!windowID) {
448 nsCOMPtr<nsILoadGroup> loadGroup;
449 nsresult rv = aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
450 if (NS_WARN_IF(NS_FAILED(rv))) {
451 return;
454 if (loadGroup) {
455 windowID = nsContentUtils::GetInnerWindowID(loadGroup);
459 nsAutoString localizedMsg;
460 rv = nsContentUtils::FormatLocalizedString(
461 nsContentUtils::eSECURITY_PROPERTIES, aMsg, aParams, localizedMsg);
462 if (NS_WARN_IF(NS_FAILED(rv))) {
463 return;
466 rv = nsContentUtils::ReportToConsoleByWindowID(
467 localizedMsg, nsIScriptError::infoFlag, "Reporting"_ns, windowID, aURI);
468 Unused << NS_WARN_IF(NS_FAILED(rv));
471 /* static */
472 void ReportingHeader::GetEndpointForReport(
473 const nsAString& aGroupName,
474 const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
475 nsACString& aEndpointURI) {
476 auto principalOrErr = PrincipalInfoToPrincipal(aPrincipalInfo);
477 if (NS_WARN_IF(principalOrErr.isErr())) {
478 return;
481 nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap();
482 GetEndpointForReport(aGroupName, principal, aEndpointURI);
485 /* static */
486 void ReportingHeader::GetEndpointForReport(const nsAString& aGroupName,
487 nsIPrincipal* aPrincipal,
488 nsACString& aEndpointURI) {
489 MOZ_ASSERT(aEndpointURI.IsEmpty());
491 if (!gReporting) {
492 return;
495 nsAutoCString origin;
496 nsresult rv = aPrincipal->GetOrigin(origin);
497 if (NS_WARN_IF(NS_FAILED(rv))) {
498 return;
501 Client* client = gReporting->mOrigins.Get(origin);
502 if (!client) {
503 return;
506 const auto [begin, end] = client->mGroups.NonObservingRange();
507 const auto foundIt = std::find_if(
508 begin, end,
509 [&aGroupName](const Group& group) { return group.mName == aGroupName; });
510 if (foundIt != end) {
511 GetEndpointForReportInternal(*foundIt, aEndpointURI);
514 // XXX More explicitly report an error if not found?
517 /* static */
518 void ReportingHeader::GetEndpointForReportInternal(
519 const ReportingHeader::Group& aGroup, nsACString& aEndpointURI) {
520 TimeDuration diff = TimeStamp::Now() - aGroup.mCreationTime;
521 if (diff.ToSeconds() > aGroup.mTTL) {
522 // Expired.
523 return;
526 if (aGroup.mEndpoints.IsEmpty()) {
527 return;
530 int64_t minPriority = -1;
531 uint32_t totalWeight = 0;
533 for (const Endpoint& endpoint : aGroup.mEndpoints.NonObservingRange()) {
534 if (minPriority == -1 || minPriority > endpoint.mPriority) {
535 minPriority = endpoint.mPriority;
536 totalWeight = endpoint.mWeight;
537 } else if (minPriority == endpoint.mPriority) {
538 totalWeight += endpoint.mWeight;
542 nsCOMPtr<nsIRandomGenerator> randomGenerator =
543 do_GetService("@mozilla.org/security/random-generator;1");
544 if (NS_WARN_IF(!randomGenerator)) {
545 return;
548 uint32_t randomNumber = 0;
550 uint8_t* buffer;
551 nsresult rv =
552 randomGenerator->GenerateRandomBytes(sizeof(randomNumber), &buffer);
553 if (NS_WARN_IF(NS_FAILED(rv))) {
554 return;
557 memcpy(&randomNumber, buffer, sizeof(randomNumber));
558 free(buffer);
560 totalWeight = randomNumber % totalWeight;
562 const auto [begin, end] = aGroup.mEndpoints.NonObservingRange();
563 const auto foundIt = std::find_if(
564 begin, end, [minPriority, totalWeight](const Endpoint& endpoint) {
565 return minPriority == endpoint.mPriority &&
566 totalWeight < endpoint.mWeight;
568 if (foundIt != end) {
569 Unused << NS_WARN_IF(NS_FAILED(foundIt->mUrl->GetSpec(aEndpointURI)));
571 // XXX More explicitly report an error if not found?
574 /* static */
575 void ReportingHeader::RemoveEndpoint(
576 const nsAString& aGroupName, const nsACString& aEndpointURL,
577 const mozilla::ipc::PrincipalInfo& aPrincipalInfo) {
578 if (!gReporting) {
579 return;
582 nsCOMPtr<nsIURI> uri;
583 nsresult rv = NS_NewURI(getter_AddRefs(uri), aEndpointURL);
584 if (NS_WARN_IF(NS_FAILED(rv))) {
585 return;
588 auto principalOrErr = PrincipalInfoToPrincipal(aPrincipalInfo);
589 if (NS_WARN_IF(principalOrErr.isErr())) {
590 return;
593 nsAutoCString origin;
594 rv = principalOrErr.unwrap()->GetOrigin(origin);
595 if (NS_WARN_IF(NS_FAILED(rv))) {
596 return;
599 Client* client = gReporting->mOrigins.Get(origin);
600 if (!client) {
601 return;
604 // Scope for the group iterator.
606 nsTObserverArray<Group>::BackwardIterator iter(client->mGroups);
607 while (iter.HasMore()) {
608 const Group& group = iter.GetNext();
609 if (group.mName != aGroupName) {
610 continue;
613 // Scope for the endpoint iterator.
615 nsTObserverArray<Endpoint>::BackwardIterator endpointIter(
616 group.mEndpoints);
617 while (endpointIter.HasMore()) {
618 const Endpoint& endpoint = endpointIter.GetNext();
620 bool equal = false;
621 rv = endpoint.mUrl->Equals(uri, &equal);
622 if (NS_WARN_IF(NS_FAILED(rv))) {
623 continue;
626 if (equal) {
627 endpointIter.Remove();
628 break;
633 if (group.mEndpoints.IsEmpty()) {
634 iter.Remove();
637 break;
641 if (client->mGroups.IsEmpty()) {
642 gReporting->mOrigins.Remove(origin);
643 gReporting->MaybeCancelCleanupTimer();
647 void ReportingHeader::RemoveOriginsFromHost(const nsAString& aHost) {
648 nsCOMPtr<nsIEffectiveTLDService> tldService =
649 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
650 if (NS_WARN_IF(!tldService)) {
651 return;
654 NS_ConvertUTF16toUTF8 host(aHost);
656 for (auto iter = mOrigins.Iter(); !iter.Done(); iter.Next()) {
657 bool hasRootDomain = false;
658 nsresult rv = tldService->HasRootDomain(iter.Key(), host, &hasRootDomain);
659 if (NS_WARN_IF(NS_FAILED(rv)) || !hasRootDomain) {
660 continue;
663 iter.Remove();
666 MaybeCancelCleanupTimer();
669 void ReportingHeader::RemoveOriginsFromOriginAttributesPattern(
670 const OriginAttributesPattern& aPattern) {
671 for (auto iter = mOrigins.Iter(); !iter.Done(); iter.Next()) {
672 nsAutoCString suffix;
673 OriginAttributes attr;
674 if (NS_WARN_IF(!attr.PopulateFromOrigin(iter.Key(), suffix))) {
675 continue;
678 if (aPattern.Matches(attr)) {
679 iter.Remove();
683 MaybeCancelCleanupTimer();
686 void ReportingHeader::RemoveOrigins() {
687 mOrigins.Clear();
688 MaybeCancelCleanupTimer();
691 void ReportingHeader::RemoveOriginsForTTL() {
692 TimeStamp now = TimeStamp::Now();
694 for (auto iter = mOrigins.Iter(); !iter.Done(); iter.Next()) {
695 Client* client = iter.UserData();
697 // Scope of the iterator.
699 nsTObserverArray<Group>::BackwardIterator groupIter(client->mGroups);
700 while (groupIter.HasMore()) {
701 const Group& group = groupIter.GetNext();
702 TimeDuration diff = now - group.mCreationTime;
703 if (diff.ToSeconds() > group.mTTL) {
704 groupIter.Remove();
705 return;
710 if (client->mGroups.IsEmpty()) {
711 iter.Remove();
716 /* static */
717 bool ReportingHeader::HasReportingHeaderForOrigin(const nsACString& aOrigin) {
718 if (!gReporting) {
719 return false;
722 return gReporting->mOrigins.Contains(aOrigin);
725 NS_IMETHODIMP
726 ReportingHeader::Notify(nsITimer* aTimer) {
727 mCleanupTimer = nullptr;
729 RemoveOriginsForTTL();
730 MaybeCreateCleanupTimer();
732 return NS_OK;
735 NS_IMETHODIMP
736 ReportingHeader::GetName(nsACString& aName) {
737 aName.AssignLiteral("ReportingHeader");
738 return NS_OK;
741 void ReportingHeader::MaybeCreateCleanupTimer() {
742 if (mCleanupTimer) {
743 return;
746 if (mOrigins.Count() == 0) {
747 return;
750 uint32_t timeout = StaticPrefs::dom_reporting_cleanup_timeout() * 1000;
751 nsresult rv =
752 NS_NewTimerWithCallback(getter_AddRefs(mCleanupTimer), this, timeout,
753 nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY);
754 Unused << NS_WARN_IF(NS_FAILED(rv));
757 void ReportingHeader::MaybeCancelCleanupTimer() {
758 if (!mCleanupTimer) {
759 return;
762 if (mOrigins.Count() != 0) {
763 return;
766 mCleanupTimer->Cancel();
767 mCleanupTimer = nullptr;
770 NS_INTERFACE_MAP_BEGIN(ReportingHeader)
771 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
772 NS_INTERFACE_MAP_ENTRY(nsIObserver)
773 NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
774 NS_INTERFACE_MAP_ENTRY(nsINamed)
775 NS_INTERFACE_MAP_END
777 NS_IMPL_ADDREF(ReportingHeader)
778 NS_IMPL_RELEASE(ReportingHeader)
780 } // namespace dom
781 } // namespace mozilla