Backed out 7 changesets (bug 1942424) for causing frequent crashes. a=backout
[gecko.git] / toolkit / components / antitracking / bouncetrackingprotection / BounceTrackingStorageObserver.cpp
bloba4361e0080cd1bcb6ee9a2be35c82e61a099cf41
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "BounceTrackingStorageObserver.h"
7 #include "BounceTrackingState.h"
8 #include "mozilla/Services.h"
9 #include "mozilla/dom/WindowContext.h"
10 #include "mozilla/dom/WindowGlobalChild.h"
11 #include "mozilla/dom/WindowGlobalParent.h"
12 #include "nsCOMPtr.h"
13 #include "nsICookieNotification.h"
14 #include "nsIObserverService.h"
15 #include "mozilla/dom/BrowsingContext.h"
16 #include "nsICookie.h"
17 #include "nsIPrincipal.h"
19 namespace mozilla {
21 NS_IMPL_ISUPPORTS(BounceTrackingStorageObserver, nsIObserver,
22 nsISupportsWeakReference);
24 nsresult BounceTrackingStorageObserver::Init() {
25 MOZ_ASSERT(XRE_IsParentProcess());
27 MOZ_LOG(gBounceTrackingProtectionLog, LogLevel::Debug,
28 ("BounceTrackingStorageObserver::%s", __FUNCTION__));
30 // Add observers to listen for cookie changes.
31 nsCOMPtr<nsIObserverService> observerService =
32 mozilla::services::GetObserverService();
33 NS_ENSURE_TRUE(observerService, NS_ERROR_FAILURE);
35 // Passing ownsWeak=true so we don't have to unregister the observer when
36 // BounceTrackingStorageObserver gets destroyed.
37 nsresult rv = observerService->AddObserver(this, "cookie-changed", true);
38 NS_ENSURE_SUCCESS(rv, rv);
39 return observerService->AddObserver(this, "private-cookie-changed", true);
42 // nsIObserver
43 NS_IMETHODIMP
44 BounceTrackingStorageObserver::Observe(nsISupports* aSubject,
45 const char* aTopic,
46 const char16_t* aData) {
47 MOZ_LOG(gBounceTrackingProtectionLog, LogLevel::Verbose,
48 ("Observe topic %s", aTopic));
50 NS_ENSURE_TRUE(aSubject, NS_ERROR_FAILURE);
52 nsresult rv = NS_OK;
53 nsCOMPtr<nsICookieNotification> notification =
54 do_QueryInterface(aSubject, &rv);
55 NS_ENSURE_SUCCESS(rv, rv);
57 nsICookieNotification::Action action = notification->GetAction();
58 // Filter for cookies added, changed or deleted. We don't care about other
59 // actions such as clearing the entire cookie store.
60 if (action != nsICookieNotification::COOKIE_ADDED &&
61 action != nsICookieNotification::COOKIE_CHANGED &&
62 action != nsICookieNotification::COOKIE_DELETED) {
63 return NS_OK;
66 // Ensure the notification is associated with a BrowsingContext. It's only set
67 // for cases where a website updated a cookie.
68 RefPtr<dom::BrowsingContext> browsingContext;
69 rv = notification->GetBrowsingContext(getter_AddRefs(browsingContext));
70 NS_ENSURE_SUCCESS(rv, rv);
71 if (!browsingContext) {
72 MOZ_LOG(gBounceTrackingProtectionLog, LogLevel::Verbose,
73 ("Could not get BC for CookieNotification."));
74 return NS_OK;
77 // Filter http(s) cookies
78 nsCOMPtr<nsICookie> cookie;
79 rv = notification->GetCookie(getter_AddRefs(cookie));
80 NS_ENSURE_SUCCESS(rv, rv);
81 MOZ_ASSERT(cookie);
83 nsICookie::schemeType schemeMap;
84 rv = cookie->GetSchemeMap(&schemeMap);
85 NS_ENSURE_SUCCESS(rv, rv);
87 if (!(schemeMap & (nsICookie::schemeType::SCHEME_HTTP |
88 nsICookie::schemeType::SCHEME_HTTPS))) {
89 MOZ_LOG(gBounceTrackingProtectionLog, LogLevel::Verbose,
90 ("Skipping non-HTTP(S) cookie."));
91 return NS_OK;
94 dom::BrowsingContext* topBC = browsingContext->Top();
95 dom::BrowsingContextWebProgress* webProgress =
96 topBC->Canonical()->GetWebProgress();
97 if (!webProgress) {
98 return NS_OK;
101 RefPtr<BounceTrackingState> bounceTrackingState =
102 webProgress->GetBounceTrackingState();
103 if (!bounceTrackingState) {
104 MOZ_LOG(gBounceTrackingProtectionLog, LogLevel::Verbose,
105 ("BC does not have BounceTrackingState."));
106 return NS_OK;
109 // For non third-party cookies we can just take the site host directly from
110 // the cookie as that matches the top level site host. This includes top level
111 // HTTP cookies set in redirects.
112 if (!notification->GetIsThirdParty()) {
113 nsAutoCString baseDomain;
114 rv = notification->GetBaseDomain(baseDomain);
115 NS_ENSURE_SUCCESS(rv, rv);
117 return bounceTrackingState->OnCookieWrite(baseDomain);
120 // For all other cases get the site host from the top window. This is
121 // important so cookie writes from cross-site iframes or subresources are
122 // correctly attributed to the top site. Only the top site appears in the
123 // bounce set. With stateful bounces enabled sites are only classified if they
124 // both bounced and set state.
125 dom::WindowContext* windowContext = topBC->GetCurrentWindowContext();
127 if (!windowContext) {
128 return NS_OK;
131 // Using the storage principal over the cookie principal is fine here since we
132 // only care about the base domain and not partition key.
133 nsIPrincipal* cookiePrincipal =
134 windowContext->Canonical()->DocumentStoragePrincipal();
135 NS_ENSURE_TRUE(cookiePrincipal, NS_ERROR_FAILURE);
137 if (!BounceTrackingState::ShouldTrackPrincipal(cookiePrincipal)) {
138 MOZ_LOG(gBounceTrackingProtectionLog, LogLevel::Verbose,
139 ("%s: Skipping principal.", __FUNCTION__));
140 return NS_OK;
143 nsAutoCString baseDomain;
144 rv = cookiePrincipal->GetBaseDomain(baseDomain);
145 NS_ENSURE_SUCCESS(rv, rv);
147 return bounceTrackingState->OnCookieWrite(baseDomain);
150 // static
151 nsresult BounceTrackingStorageObserver::OnInitialStorageAccess(
152 dom::WindowContext* aWindowContext) {
153 NS_ENSURE_ARG_POINTER(aWindowContext);
155 // Get the site host from the top window. This is important so storage access
156 // from cross-site iframes or subresources are correctly attributed to the top
157 // site. Only the top site appears in the bounce set. With stateful bounces
158 // enabled sites are only classified if they both bounced and set state.
159 dom::WindowContext* topWindowContext = aWindowContext->TopWindowContext();
160 NS_ENSURE_TRUE(topWindowContext, NS_ERROR_FAILURE);
162 if (!XRE_IsParentProcess()) {
163 // Check if the principal needs to be tracked for bounce tracking. Checking
164 // this in the content process may save us IPC to the parent.
165 nsGlobalWindowInner* innerWindow = topWindowContext->GetInnerWindow();
167 if (innerWindow) {
168 nsIPrincipal* storagePrincipal =
169 innerWindow->GetEffectiveStoragePrincipal();
170 if (!BounceTrackingState::ShouldTrackPrincipal(storagePrincipal)) {
171 MOZ_LOG(gBounceTrackingProtectionLog, LogLevel::Verbose,
172 ("%s: Skipping principal (content process).", __FUNCTION__));
173 return NS_OK;
177 dom::WindowGlobalChild* windowGlobalChild =
178 aWindowContext->GetWindowGlobalChild();
179 NS_ENSURE_TRUE(windowGlobalChild, NS_ERROR_FAILURE);
180 NS_ENSURE_TRUE(windowGlobalChild->SendOnInitialStorageAccess(),
181 NS_ERROR_FAILURE);
183 return NS_OK;
186 MOZ_ASSERT(XRE_IsParentProcess());
187 nsCOMPtr<nsIPrincipal> storagePrincipal =
188 topWindowContext->Canonical()->DocumentStoragePrincipal();
189 NS_ENSURE_TRUE(storagePrincipal, NS_ERROR_FAILURE);
191 if (!BounceTrackingState::ShouldTrackPrincipal(storagePrincipal)) {
192 MOZ_LOG(gBounceTrackingProtectionLog, LogLevel::Verbose,
193 ("%s: Skipping principal.", __FUNCTION__));
194 return NS_OK;
197 dom::BrowsingContext* browsingContext =
198 topWindowContext->GetBrowsingContext();
199 NS_ENSURE_TRUE(browsingContext, NS_ERROR_FAILURE);
201 dom::BrowsingContextWebProgress* webProgress =
202 browsingContext->Top()->Canonical()->GetWebProgress();
203 NS_ENSURE_TRUE(webProgress, NS_ERROR_FAILURE);
205 RefPtr<BounceTrackingState> bounceTrackingState =
206 webProgress->GetBounceTrackingState();
208 // We may not always get a BounceTrackingState, e.g. if the feature is
209 // disabled or we don't keep track of bounce tracking for the given
210 // BrowsingContext.
211 if (!bounceTrackingState) {
212 return NS_OK;
215 return bounceTrackingState->OnStorageAccess(storagePrincipal);
218 } // namespace mozilla