CookieStoreNotifier.cpp (5628B)
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 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "CookieStoreNotifier.h" 8 9 #include "CookieChangeEvent.h" 10 #include "CookieStore.h" 11 #include "mozilla/dom/Document.h" 12 #include "mozilla/dom/WorkerPrivate.h" 13 #include "mozilla/net/Cookie.h" 14 #include "mozilla/net/CookieCommons.h" 15 #include "nsGlobalWindowInner.h" 16 #include "nsICookie.h" 17 #include "nsICookieNotification.h" 18 #include "nsISerialEventTarget.h" 19 20 namespace mozilla::dom { 21 22 NS_IMPL_ISUPPORTS(CookieStoreNotifier, nsIObserver); 23 24 // static 25 already_AddRefed<CookieStoreNotifier> CookieStoreNotifier::Create( 26 CookieStore* aCookieStore) { 27 MOZ_ASSERT(NS_IsMainThread()); 28 29 nsCOMPtr<nsPIDOMWindowInner> window = aCookieStore->GetOwnerWindow(); 30 MOZ_ASSERT(window); 31 32 nsCOMPtr<Document> document = window->GetExtantDoc(); 33 if (NS_WARN_IF(!document)) { 34 return nullptr; 35 } 36 37 nsIPrincipal* principal = document->NodePrincipal(); 38 if (NS_WARN_IF(!principal)) { 39 return nullptr; 40 } 41 42 nsCString baseDomain; 43 if (NS_WARN_IF(NS_FAILED( 44 net::CookieCommons::GetBaseDomain(principal, baseDomain)))) { 45 return nullptr; 46 } 47 48 if (baseDomain.IsEmpty()) { 49 return nullptr; 50 } 51 52 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); 53 if (NS_WARN_IF(!os)) { 54 return nullptr; 55 } 56 57 RefPtr<CookieStoreNotifier> notifier = new CookieStoreNotifier( 58 aCookieStore, baseDomain, principal->OriginAttributesRef()); 59 60 nsresult rv = 61 os->AddObserver(notifier, 62 principal->OriginAttributesRef().IsPrivateBrowsing() 63 ? "private-cookie-changed" 64 : "cookie-changed", 65 false); 66 (void)NS_WARN_IF(NS_FAILED(rv)); 67 68 return notifier.forget(); 69 } 70 71 CookieStoreNotifier::CookieStoreNotifier( 72 CookieStore* aCookieStore, const nsACString& aBaseDomain, 73 const OriginAttributes& aOriginAttributes) 74 : mCookieStore(aCookieStore), 75 mBaseDomain(aBaseDomain), 76 mOriginAttributes(aOriginAttributes) { 77 MOZ_ASSERT(NS_IsMainThread()); 78 MOZ_ASSERT(aCookieStore); 79 } 80 81 CookieStoreNotifier::~CookieStoreNotifier() = default; 82 83 void CookieStoreNotifier::Disentangle() { 84 MOZ_ASSERT(NS_IsMainThread()); 85 86 mCookieStore = nullptr; 87 88 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); 89 if (NS_WARN_IF(!os)) { 90 return; 91 } 92 93 nsresult rv = os->RemoveObserver(this, mOriginAttributes.IsPrivateBrowsing() 94 ? "private-cookie-changed" 95 : "cookie-changed"); 96 (void)NS_WARN_IF(NS_FAILED(rv)); 97 } 98 99 NS_IMETHODIMP 100 CookieStoreNotifier::Observe(nsISupports* aSubject, const char* aTopic, 101 const char16_t* aData) { 102 MOZ_ASSERT(NS_IsMainThread()); 103 104 nsCOMPtr<nsICookieNotification> notification = do_QueryInterface(aSubject); 105 NS_ENSURE_TRUE(notification, NS_ERROR_FAILURE); 106 107 auto action = notification->GetAction(); 108 if (action != nsICookieNotification::COOKIE_DELETED && 109 action != nsICookieNotification::COOKIE_ADDED && 110 action != nsICookieNotification::COOKIE_CHANGED) { 111 return NS_OK; 112 } 113 114 nsAutoCString baseDomain; 115 nsresult rv = notification->GetBaseDomain(baseDomain); 116 if (NS_WARN_IF(NS_FAILED(rv)) || baseDomain.IsEmpty()) { 117 return rv; 118 } 119 120 if (baseDomain != mBaseDomain) { 121 return NS_OK; 122 } 123 124 nsCOMPtr<nsICookie> cookie; 125 rv = notification->GetCookie(getter_AddRefs(cookie)); 126 if (NS_WARN_IF(NS_FAILED(rv))) { 127 return rv; 128 } 129 130 if (cookie->OriginAttributesNative() != mOriginAttributes) { 131 return NS_OK; 132 } 133 134 bool isHttpOnly; 135 rv = cookie->GetIsHttpOnly(&isHttpOnly); 136 if (NS_WARN_IF(NS_FAILED(rv))) { 137 return rv; 138 } 139 140 if (isHttpOnly) { 141 return NS_OK; 142 } 143 144 CookieListItem item; 145 CookieStore::CookieStructToItem(net::Cookie::Cast(cookie)->ToIPC(), &item); 146 147 if (action == nsICookieNotification::COOKIE_DELETED) { 148 item.mValue.Reset(); 149 } 150 151 bool deletedEvent = action == nsICookieNotification::COOKIE_DELETED; 152 153 GetCurrentSerialEventTarget()->Dispatch(NS_NewRunnableFunction( 154 __func__, [self = RefPtr(this), item, deletedEvent] { 155 self->DispatchEvent(item, deletedEvent); 156 })); 157 158 return NS_OK; 159 } 160 161 void CookieStoreNotifier::DispatchEvent(const CookieListItem& aItem, 162 bool aDeletedEvent) { 163 MOZ_ASSERT(NS_IsMainThread()); 164 165 if (!mCookieStore) { 166 return; 167 } 168 169 RefPtr<Event> event = 170 aDeletedEvent 171 ? CookieChangeEvent::CreateForDeletedCookie(mCookieStore, aItem) 172 : CookieChangeEvent::CreateForChangedCookie(mCookieStore, aItem); 173 174 if (!event) { 175 return; 176 } 177 178 nsCOMPtr<nsPIDOMWindowInner> window = mCookieStore->GetOwnerWindow(); 179 if (!window) { 180 return; 181 } 182 183 RefPtr<BrowsingContext> bc = window->GetBrowsingContext(); 184 if (!bc) { 185 return; 186 } 187 188 if (bc->IsInBFCache() || 189 (window->GetExtantDoc() && window->GetExtantDoc()->GetBFCacheEntry())) { 190 mDelayedDOMEvents.AppendElement(event); 191 return; 192 } 193 194 mCookieStore->DispatchEvent(*event); 195 } 196 197 void CookieStoreNotifier::FireDelayedDOMEvents() { 198 MOZ_ASSERT(NS_IsMainThread()); 199 200 nsTArray<RefPtr<Event>> delayedDOMEvents; 201 delayedDOMEvents.SwapElements(mDelayedDOMEvents); 202 203 for (Event* event : delayedDOMEvents) { 204 mCookieStore->DispatchEvent(*event); 205 } 206 } 207 208 } // namespace mozilla::dom