no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / dom / reporting / ReportingHeader.cpp
blob552036852d38707eeaf54795c851284412c4c174
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::dom {
37 namespace {
39 StaticRefPtr<ReportingHeader> gReporting;
41 } // namespace
43 /* static */
44 void ReportingHeader::Initialize() {
45 MOZ_ASSERT(!gReporting);
46 MOZ_ASSERT(NS_IsMainThread());
48 if (!XRE_IsParentProcess()) {
49 return;
52 RefPtr<ReportingHeader> service = new ReportingHeader();
54 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
55 if (NS_WARN_IF(!obs)) {
56 return;
59 obs->AddObserver(service, NS_HTTP_ON_EXAMINE_RESPONSE_TOPIC, false);
60 obs->AddObserver(service, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
61 obs->AddObserver(service, "clear-origin-attributes-data", false);
62 obs->AddObserver(service, REPORTING_PURGE_HOST, false);
63 obs->AddObserver(service, REPORTING_PURGE_ALL, false);
65 gReporting = service;
68 /* static */
69 void ReportingHeader::Shutdown() {
70 MOZ_ASSERT(NS_IsMainThread());
72 if (!gReporting) {
73 return;
76 RefPtr<ReportingHeader> service = gReporting;
77 gReporting = nullptr;
79 if (service->mCleanupTimer) {
80 service->mCleanupTimer->Cancel();
81 service->mCleanupTimer = nullptr;
84 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
85 if (NS_WARN_IF(!obs)) {
86 return;
89 obs->RemoveObserver(service, NS_HTTP_ON_EXAMINE_RESPONSE_TOPIC);
90 obs->RemoveObserver(service, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
91 obs->RemoveObserver(service, "clear-origin-attributes-data");
92 obs->RemoveObserver(service, REPORTING_PURGE_HOST);
93 obs->RemoveObserver(service, REPORTING_PURGE_ALL);
96 ReportingHeader::ReportingHeader() = default;
97 ReportingHeader::~ReportingHeader() = default;
99 NS_IMETHODIMP
100 ReportingHeader::Observe(nsISupports* aSubject, const char* aTopic,
101 const char16_t* aData) {
102 if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
103 Shutdown();
104 return NS_OK;
107 // Pref disabled.
108 if (!StaticPrefs::dom_reporting_header_enabled()) {
109 return NS_OK;
112 if (!strcmp(aTopic, NS_HTTP_ON_EXAMINE_RESPONSE_TOPIC)) {
113 nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aSubject);
114 if (NS_WARN_IF(!channel)) {
115 return NS_OK;
118 ReportingFromChannel(channel);
119 return NS_OK;
122 if (!strcmp(aTopic, REPORTING_PURGE_HOST)) {
123 RemoveOriginsFromHost(nsDependentString(aData));
124 return NS_OK;
127 if (!strcmp(aTopic, "clear-origin-attributes-data")) {
128 OriginAttributesPattern pattern;
129 if (!pattern.Init(nsDependentString(aData))) {
130 NS_ERROR("Cannot parse origin attributes pattern");
131 return NS_ERROR_FAILURE;
134 RemoveOriginsFromOriginAttributesPattern(pattern);
135 return NS_OK;
138 if (!strcmp(aTopic, REPORTING_PURGE_ALL)) {
139 RemoveOrigins();
140 return NS_OK;
143 return NS_ERROR_FAILURE;
146 void ReportingHeader::ReportingFromChannel(nsIHttpChannel* aChannel) {
147 MOZ_ASSERT(aChannel);
149 if (!StaticPrefs::dom_reporting_header_enabled()) {
150 return;
153 // We want to use the final URI to check if Report-To should be allowed or
154 // not.
155 nsCOMPtr<nsIURI> uri;
156 nsresult rv = aChannel->GetURI(getter_AddRefs(uri));
157 if (NS_WARN_IF(NS_FAILED(rv))) {
158 return;
161 if (!IsSecureURI(uri)) {
162 return;
165 if (NS_UsePrivateBrowsing(aChannel)) {
166 return;
169 nsAutoCString headerValue;
170 rv = aChannel->GetResponseHeader("Report-To"_ns, headerValue);
171 if (NS_FAILED(rv)) {
172 return;
175 nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
176 if (NS_WARN_IF(!ssm)) {
177 return;
180 nsCOMPtr<nsIPrincipal> principal;
181 rv = ssm->GetChannelURIPrincipal(aChannel, getter_AddRefs(principal));
182 if (NS_WARN_IF(NS_FAILED(rv)) || !principal) {
183 return;
186 nsAutoCString origin;
187 rv = principal->GetOrigin(origin);
188 if (NS_WARN_IF(NS_FAILED(rv))) {
189 return;
192 UniquePtr<Client> client = ParseHeader(aChannel, uri, headerValue);
193 if (!client) {
194 return;
197 // Here we override the previous data.
198 mOrigins.InsertOrUpdate(origin, std::move(client));
200 MaybeCreateCleanupTimer();
203 /* static */ UniquePtr<ReportingHeader::Client> ReportingHeader::ParseHeader(
204 nsIHttpChannel* aChannel, nsIURI* aURI, const nsACString& aHeaderValue) {
205 MOZ_ASSERT(aURI);
206 // aChannel can be null in gtest
208 AutoJSAPI jsapi;
210 JSObject* cleanGlobal =
211 SimpleGlobalObject::Create(SimpleGlobalObject::GlobalType::BindingDetail);
212 if (NS_WARN_IF(!cleanGlobal)) {
213 return nullptr;
216 if (NS_WARN_IF(!jsapi.Init(cleanGlobal))) {
217 return nullptr;
220 // WebIDL dictionary parses single items. Let's create a object to parse the
221 // header.
222 nsAutoString json;
223 json.AppendASCII("{ \"items\": [");
224 json.Append(NS_ConvertUTF8toUTF16(aHeaderValue));
225 json.AppendASCII("]}");
227 JSContext* cx = jsapi.cx();
228 JS::Rooted<JS::Value> jsonValue(cx);
229 bool ok = JS_ParseJSON(cx, json.BeginReading(), json.Length(), &jsonValue);
230 if (!ok) {
231 LogToConsoleInvalidJSON(aChannel, aURI);
232 return nullptr;
235 dom::ReportingHeaderValue data;
236 if (!data.Init(cx, jsonValue)) {
237 LogToConsoleInvalidJSON(aChannel, aURI);
238 return nullptr;
241 if (!data.mItems.WasPassed() || data.mItems.Value().IsEmpty()) {
242 return nullptr;
245 UniquePtr<Client> client = MakeUnique<Client>();
247 for (const dom::ReportingItem& item : data.mItems.Value()) {
248 nsAutoString groupName;
250 if (item.mGroup.isUndefined()) {
251 groupName.AssignLiteral("default");
252 } else if (!item.mGroup.isString()) {
253 LogToConsoleInvalidNameItem(aChannel, aURI);
254 continue;
255 } else {
256 JS::Rooted<JSString*> groupStr(cx, item.mGroup.toString());
257 MOZ_ASSERT(groupStr);
259 nsAutoJSString string;
260 if (NS_WARN_IF(!string.init(cx, groupStr))) {
261 continue;
264 groupName = string;
267 if (!item.mMax_age.isNumber() || !item.mEndpoints.isObject()) {
268 LogToConsoleIncompleteItem(aChannel, aURI, groupName);
269 continue;
272 JS::Rooted<JSObject*> endpoints(cx, &item.mEndpoints.toObject());
273 MOZ_ASSERT(endpoints);
275 bool isArray = false;
276 if (!JS::IsArrayObject(cx, endpoints, &isArray) || !isArray) {
277 LogToConsoleIncompleteItem(aChannel, aURI, groupName);
278 continue;
281 uint32_t endpointsLength;
282 if (!JS::GetArrayLength(cx, endpoints, &endpointsLength) ||
283 endpointsLength == 0) {
284 LogToConsoleIncompleteItem(aChannel, aURI, groupName);
285 continue;
288 const auto [begin, end] = client->mGroups.NonObservingRange();
289 if (std::any_of(begin, end, [&groupName](const Group& group) {
290 return group.mName == groupName;
291 })) {
292 LogToConsoleDuplicateGroup(aChannel, aURI, groupName);
293 continue;
296 Group* group = client->mGroups.AppendElement();
297 group->mName = groupName;
298 group->mIncludeSubdomains = item.mInclude_subdomains;
299 group->mTTL = item.mMax_age.toNumber();
300 group->mCreationTime = TimeStamp::Now();
302 for (uint32_t i = 0; i < endpointsLength; ++i) {
303 JS::Rooted<JS::Value> element(cx);
304 if (!JS_GetElement(cx, endpoints, i, &element)) {
305 return nullptr;
308 RootedDictionary<ReportingEndpoint> endpoint(cx);
309 if (!endpoint.Init(cx, element)) {
310 LogToConsoleIncompleteEndpoint(aChannel, aURI, groupName);
311 continue;
314 if (!endpoint.mUrl.isString() ||
315 (!endpoint.mPriority.isUndefined() &&
316 (!endpoint.mPriority.isNumber() ||
317 endpoint.mPriority.toNumber() < 0)) ||
318 (!endpoint.mWeight.isUndefined() &&
319 (!endpoint.mWeight.isNumber() || endpoint.mWeight.toNumber() < 0))) {
320 LogToConsoleIncompleteEndpoint(aChannel, aURI, groupName);
321 continue;
324 JS::Rooted<JSString*> endpointUrl(cx, endpoint.mUrl.toString());
325 MOZ_ASSERT(endpointUrl);
327 nsAutoJSString endpointString;
328 if (NS_WARN_IF(!endpointString.init(cx, endpointUrl))) {
329 continue;
332 nsCOMPtr<nsIURI> uri;
333 nsresult rv = NS_NewURI(getter_AddRefs(uri), endpointString);
334 if (NS_FAILED(rv)) {
335 LogToConsoleInvalidURLEndpoint(aChannel, aURI, groupName,
336 endpointString);
337 continue;
340 Endpoint* ep = group->mEndpoints.AppendElement();
341 ep->mUrl = uri;
342 ep->mPriority =
343 endpoint.mPriority.isUndefined() ? 1 : endpoint.mPriority.toNumber();
344 ep->mWeight =
345 endpoint.mWeight.isUndefined() ? 1 : endpoint.mWeight.toNumber();
349 if (client->mGroups.IsEmpty()) {
350 return nullptr;
353 return client;
356 bool ReportingHeader::IsSecureURI(nsIURI* aURI) const {
357 MOZ_ASSERT(aURI);
359 bool prioriAuthenticated = false;
360 if (NS_WARN_IF(NS_FAILED(NS_URIChainHasFlags(
361 aURI, nsIProtocolHandler::URI_IS_POTENTIALLY_TRUSTWORTHY,
362 &prioriAuthenticated)))) {
363 return false;
366 return prioriAuthenticated;
369 /* static */
370 void ReportingHeader::LogToConsoleInvalidJSON(nsIHttpChannel* aChannel,
371 nsIURI* aURI) {
372 nsTArray<nsString> params;
373 LogToConsoleInternal(aChannel, aURI, "ReportingHeaderInvalidJSON", params);
376 /* static */
377 void ReportingHeader::LogToConsoleDuplicateGroup(nsIHttpChannel* aChannel,
378 nsIURI* aURI,
379 const nsAString& aName) {
380 nsTArray<nsString> params;
381 params.AppendElement(aName);
383 LogToConsoleInternal(aChannel, aURI, "ReportingHeaderDuplicateGroup", params);
386 /* static */
387 void ReportingHeader::LogToConsoleInvalidNameItem(nsIHttpChannel* aChannel,
388 nsIURI* aURI) {
389 nsTArray<nsString> params;
390 LogToConsoleInternal(aChannel, aURI, "ReportingHeaderInvalidNameItem",
391 params);
394 /* static */
395 void ReportingHeader::LogToConsoleIncompleteItem(nsIHttpChannel* aChannel,
396 nsIURI* aURI,
397 const nsAString& aName) {
398 nsTArray<nsString> params;
399 params.AppendElement(aName);
401 LogToConsoleInternal(aChannel, aURI, "ReportingHeaderInvalidItem", params);
404 /* static */
405 void ReportingHeader::LogToConsoleIncompleteEndpoint(nsIHttpChannel* aChannel,
406 nsIURI* aURI,
407 const nsAString& aName) {
408 nsTArray<nsString> params;
409 params.AppendElement(aName);
411 LogToConsoleInternal(aChannel, aURI, "ReportingHeaderInvalidEndpoint",
412 params);
415 /* static */
416 void ReportingHeader::LogToConsoleInvalidURLEndpoint(nsIHttpChannel* aChannel,
417 nsIURI* aURI,
418 const nsAString& aName,
419 const nsAString& aURL) {
420 nsTArray<nsString> params;
421 params.AppendElement(aURL);
422 params.AppendElement(aName);
424 LogToConsoleInternal(aChannel, aURI, "ReportingHeaderInvalidURLEndpoint",
425 params);
428 /* static */
429 void ReportingHeader::LogToConsoleInternal(nsIHttpChannel* aChannel,
430 nsIURI* aURI, const char* aMsg,
431 const nsTArray<nsString>& aParams) {
432 MOZ_ASSERT(aURI);
434 if (!aChannel) {
435 // We are in a gtest.
436 return;
439 uint64_t windowID = 0;
441 nsresult rv = aChannel->GetTopLevelContentWindowId(&windowID);
442 if (NS_WARN_IF(NS_FAILED(rv))) {
443 return;
446 if (!windowID) {
447 nsCOMPtr<nsILoadGroup> loadGroup;
448 nsresult rv = aChannel->GetLoadGroup(getter_AddRefs(loadGroup));
449 if (NS_WARN_IF(NS_FAILED(rv))) {
450 return;
453 if (loadGroup) {
454 windowID = nsContentUtils::GetInnerWindowID(loadGroup);
458 nsAutoString localizedMsg;
459 rv = nsContentUtils::FormatLocalizedString(
460 nsContentUtils::eSECURITY_PROPERTIES, aMsg, aParams, localizedMsg);
461 if (NS_WARN_IF(NS_FAILED(rv))) {
462 return;
465 rv = nsContentUtils::ReportToConsoleByWindowID(
466 localizedMsg, nsIScriptError::infoFlag, "Reporting"_ns, windowID, aURI);
467 Unused << NS_WARN_IF(NS_FAILED(rv));
470 /* static */
471 void ReportingHeader::GetEndpointForReport(
472 const nsAString& aGroupName,
473 const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
474 nsACString& aEndpointURI) {
475 auto principalOrErr = PrincipalInfoToPrincipal(aPrincipalInfo);
476 if (NS_WARN_IF(principalOrErr.isErr())) {
477 return;
480 nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap();
481 GetEndpointForReport(aGroupName, principal, aEndpointURI);
484 /* static */
485 void ReportingHeader::GetEndpointForReport(const nsAString& aGroupName,
486 nsIPrincipal* aPrincipal,
487 nsACString& aEndpointURI) {
488 MOZ_ASSERT(aEndpointURI.IsEmpty());
490 if (!gReporting) {
491 return;
494 nsAutoCString origin;
495 nsresult rv = aPrincipal->GetOrigin(origin);
496 if (NS_WARN_IF(NS_FAILED(rv))) {
497 return;
500 Client* client = gReporting->mOrigins.Get(origin);
501 if (!client) {
502 return;
505 const auto [begin, end] = client->mGroups.NonObservingRange();
506 const auto foundIt = std::find_if(
507 begin, end,
508 [&aGroupName](const Group& group) { return group.mName == aGroupName; });
509 if (foundIt != end) {
510 GetEndpointForReportInternal(*foundIt, aEndpointURI);
513 // XXX More explicitly report an error if not found?
516 /* static */
517 void ReportingHeader::GetEndpointForReportInternal(
518 const ReportingHeader::Group& aGroup, nsACString& aEndpointURI) {
519 TimeDuration diff = TimeStamp::Now() - aGroup.mCreationTime;
520 if (diff.ToSeconds() > aGroup.mTTL) {
521 // Expired.
522 return;
525 if (aGroup.mEndpoints.IsEmpty()) {
526 return;
529 int64_t minPriority = -1;
530 uint32_t totalWeight = 0;
532 for (const Endpoint& endpoint : aGroup.mEndpoints.NonObservingRange()) {
533 if (minPriority == -1 || minPriority > endpoint.mPriority) {
534 minPriority = endpoint.mPriority;
535 totalWeight = endpoint.mWeight;
536 } else if (minPriority == endpoint.mPriority) {
537 totalWeight += endpoint.mWeight;
541 nsCOMPtr<nsIRandomGenerator> randomGenerator =
542 do_GetService("@mozilla.org/security/random-generator;1");
543 if (NS_WARN_IF(!randomGenerator)) {
544 return;
547 uint32_t randomNumber = 0;
549 nsresult rv = randomGenerator->GenerateRandomBytesInto(randomNumber);
550 if (NS_WARN_IF(NS_FAILED(rv))) {
551 return;
554 totalWeight = randomNumber % totalWeight;
556 const auto [begin, end] = aGroup.mEndpoints.NonObservingRange();
557 const auto foundIt = std::find_if(
558 begin, end, [minPriority, totalWeight](const Endpoint& endpoint) {
559 return minPriority == endpoint.mPriority &&
560 totalWeight < endpoint.mWeight;
562 if (foundIt != end) {
563 Unused << NS_WARN_IF(NS_FAILED(foundIt->mUrl->GetSpec(aEndpointURI)));
565 // XXX More explicitly report an error if not found?
568 /* static */
569 void ReportingHeader::RemoveEndpoint(
570 const nsAString& aGroupName, const nsACString& aEndpointURL,
571 const mozilla::ipc::PrincipalInfo& aPrincipalInfo) {
572 if (!gReporting) {
573 return;
576 nsCOMPtr<nsIURI> uri;
577 nsresult rv = NS_NewURI(getter_AddRefs(uri), aEndpointURL);
578 if (NS_WARN_IF(NS_FAILED(rv))) {
579 return;
582 auto principalOrErr = PrincipalInfoToPrincipal(aPrincipalInfo);
583 if (NS_WARN_IF(principalOrErr.isErr())) {
584 return;
587 nsAutoCString origin;
588 rv = principalOrErr.unwrap()->GetOrigin(origin);
589 if (NS_WARN_IF(NS_FAILED(rv))) {
590 return;
593 Client* client = gReporting->mOrigins.Get(origin);
594 if (!client) {
595 return;
598 // Scope for the group iterator.
600 nsTObserverArray<Group>::BackwardIterator iter(client->mGroups);
601 while (iter.HasMore()) {
602 const Group& group = iter.GetNext();
603 if (group.mName != aGroupName) {
604 continue;
607 // Scope for the endpoint iterator.
609 nsTObserverArray<Endpoint>::BackwardIterator endpointIter(
610 group.mEndpoints);
611 while (endpointIter.HasMore()) {
612 const Endpoint& endpoint = endpointIter.GetNext();
614 bool equal = false;
615 rv = endpoint.mUrl->Equals(uri, &equal);
616 if (NS_WARN_IF(NS_FAILED(rv))) {
617 continue;
620 if (equal) {
621 endpointIter.Remove();
622 break;
627 if (group.mEndpoints.IsEmpty()) {
628 iter.Remove();
631 break;
635 if (client->mGroups.IsEmpty()) {
636 gReporting->mOrigins.Remove(origin);
637 gReporting->MaybeCancelCleanupTimer();
641 void ReportingHeader::RemoveOriginsFromHost(const nsAString& aHost) {
642 nsCOMPtr<nsIEffectiveTLDService> tldService =
643 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
644 if (NS_WARN_IF(!tldService)) {
645 return;
648 NS_ConvertUTF16toUTF8 host(aHost);
650 for (auto iter = mOrigins.Iter(); !iter.Done(); iter.Next()) {
651 bool hasRootDomain = false;
652 nsresult rv = tldService->HasRootDomain(iter.Key(), host, &hasRootDomain);
653 if (NS_WARN_IF(NS_FAILED(rv)) || !hasRootDomain) {
654 continue;
657 iter.Remove();
660 MaybeCancelCleanupTimer();
663 void ReportingHeader::RemoveOriginsFromOriginAttributesPattern(
664 const OriginAttributesPattern& aPattern) {
665 for (auto iter = mOrigins.Iter(); !iter.Done(); iter.Next()) {
666 nsAutoCString suffix;
667 OriginAttributes attr;
668 if (NS_WARN_IF(!attr.PopulateFromOrigin(iter.Key(), suffix))) {
669 continue;
672 if (aPattern.Matches(attr)) {
673 iter.Remove();
677 MaybeCancelCleanupTimer();
680 void ReportingHeader::RemoveOrigins() {
681 mOrigins.Clear();
682 MaybeCancelCleanupTimer();
685 void ReportingHeader::RemoveOriginsForTTL() {
686 TimeStamp now = TimeStamp::Now();
688 for (auto iter = mOrigins.Iter(); !iter.Done(); iter.Next()) {
689 Client* client = iter.UserData();
691 // Scope of the iterator.
693 nsTObserverArray<Group>::BackwardIterator groupIter(client->mGroups);
694 while (groupIter.HasMore()) {
695 const Group& group = groupIter.GetNext();
696 TimeDuration diff = now - group.mCreationTime;
697 if (diff.ToSeconds() > group.mTTL) {
698 groupIter.Remove();
699 return;
704 if (client->mGroups.IsEmpty()) {
705 iter.Remove();
710 /* static */
711 bool ReportingHeader::HasReportingHeaderForOrigin(const nsACString& aOrigin) {
712 if (!gReporting) {
713 return false;
716 return gReporting->mOrigins.Contains(aOrigin);
719 NS_IMETHODIMP
720 ReportingHeader::Notify(nsITimer* aTimer) {
721 mCleanupTimer = nullptr;
723 RemoveOriginsForTTL();
724 MaybeCreateCleanupTimer();
726 return NS_OK;
729 NS_IMETHODIMP
730 ReportingHeader::GetName(nsACString& aName) {
731 aName.AssignLiteral("ReportingHeader");
732 return NS_OK;
735 void ReportingHeader::MaybeCreateCleanupTimer() {
736 if (mCleanupTimer) {
737 return;
740 if (mOrigins.Count() == 0) {
741 return;
744 uint32_t timeout = StaticPrefs::dom_reporting_cleanup_timeout() * 1000;
745 nsresult rv =
746 NS_NewTimerWithCallback(getter_AddRefs(mCleanupTimer), this, timeout,
747 nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY);
748 Unused << NS_WARN_IF(NS_FAILED(rv));
751 void ReportingHeader::MaybeCancelCleanupTimer() {
752 if (!mCleanupTimer) {
753 return;
756 if (mOrigins.Count() != 0) {
757 return;
760 mCleanupTimer->Cancel();
761 mCleanupTimer = nullptr;
764 NS_INTERFACE_MAP_BEGIN(ReportingHeader)
765 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
766 NS_INTERFACE_MAP_ENTRY(nsIObserver)
767 NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
768 NS_INTERFACE_MAP_ENTRY(nsINamed)
769 NS_INTERFACE_MAP_END
771 NS_IMPL_ADDREF(ReportingHeader)
772 NS_IMPL_RELEASE(ReportingHeader)
774 } // namespace mozilla::dom