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"
13 #include "nsICookieNotification.h"
14 #include "nsIObserverService.h"
15 #include "mozilla/dom/BrowsingContext.h"
16 #include "nsICookie.h"
17 #include "nsIPrincipal.h"
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);
44 BounceTrackingStorageObserver::Observe(nsISupports
* aSubject
,
46 const char16_t
* aData
) {
47 MOZ_LOG(gBounceTrackingProtectionLog
, LogLevel::Verbose
,
48 ("Observe topic %s", aTopic
));
50 NS_ENSURE_TRUE(aSubject
, NS_ERROR_FAILURE
);
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
) {
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."));
77 // Filter http(s) cookies
78 nsCOMPtr
<nsICookie
> cookie
;
79 rv
= notification
->GetCookie(getter_AddRefs(cookie
));
80 NS_ENSURE_SUCCESS(rv
, rv
);
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."));
94 dom::BrowsingContext
* topBC
= browsingContext
->Top();
95 dom::BrowsingContextWebProgress
* webProgress
=
96 topBC
->Canonical()->GetWebProgress();
101 RefPtr
<BounceTrackingState
> bounceTrackingState
=
102 webProgress
->GetBounceTrackingState();
103 if (!bounceTrackingState
) {
104 MOZ_LOG(gBounceTrackingProtectionLog
, LogLevel::Verbose
,
105 ("BC does not have BounceTrackingState."));
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
) {
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__
));
143 nsAutoCString baseDomain
;
144 rv
= cookiePrincipal
->GetBaseDomain(baseDomain
);
145 NS_ENSURE_SUCCESS(rv
, rv
);
147 return bounceTrackingState
->OnCookieWrite(baseDomain
);
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();
168 nsIPrincipal
* storagePrincipal
=
169 innerWindow
->GetEffectiveStoragePrincipal();
170 if (!BounceTrackingState::ShouldTrackPrincipal(storagePrincipal
)) {
171 MOZ_LOG(gBounceTrackingProtectionLog
, LogLevel::Verbose
,
172 ("%s: Skipping principal (content process).", __FUNCTION__
));
177 dom::WindowGlobalChild
* windowGlobalChild
=
178 aWindowContext
->GetWindowGlobalChild();
179 NS_ENSURE_TRUE(windowGlobalChild
, NS_ERROR_FAILURE
);
180 NS_ENSURE_TRUE(windowGlobalChild
->SendOnInitialStorageAccess(),
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__
));
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
211 if (!bounceTrackingState
) {
215 return bounceTrackingState
->OnStorageAccess(storagePrincipal
);
218 } // namespace mozilla