StorageActivityService.cpp (8557B)
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 "StorageActivityService.h" 8 9 #include "mozilla/BasePrincipal.h" 10 #include "mozilla/SchedulerGroup.h" 11 #include "mozilla/Services.h" 12 #include "mozilla/StaticPtr.h" 13 #include "mozilla/ipc/BackgroundChild.h" 14 #include "mozilla/ipc/BackgroundUtils.h" 15 #include "mozilla/ipc/PBackgroundChild.h" 16 #include "mozilla/ipc/PBackgroundSharedTypes.h" 17 #include "nsCOMPtr.h" 18 #include "nsComponentManagerUtils.h" 19 #include "nsIMutableArray.h" 20 #include "nsIObserverService.h" 21 #include "nsIPrincipal.h" 22 #include "nsIUserIdleService.h" 23 #include "nsSupportsPrimitives.h" 24 #include "nsXPCOM.h" 25 26 // This const is used to know when origin activities should be purged because 27 // too old. This value should be in sync with what the UI needs. 28 #define TIME_MAX_SECS 86400 /* 24 hours */ 29 30 namespace mozilla::dom { 31 32 static StaticRefPtr<StorageActivityService> gStorageActivityService; 33 static bool gStorageActivityShutdown = false; 34 35 /* static */ 36 void StorageActivityService::SendActivity(nsIPrincipal* aPrincipal) { 37 MOZ_ASSERT(NS_IsMainThread()); 38 39 if (!aPrincipal || BasePrincipal::Cast(aPrincipal)->Kind() != 40 BasePrincipal::eContentPrincipal) { 41 // Only content principals. 42 return; 43 } 44 45 RefPtr<StorageActivityService> service = GetOrCreate(); 46 if (NS_WARN_IF(!service)) { 47 return; 48 } 49 50 service->SendActivityInternal(aPrincipal); 51 } 52 53 /* static */ 54 void StorageActivityService::SendActivity( 55 const mozilla::ipc::PrincipalInfo& aPrincipalInfo) { 56 if (aPrincipalInfo.type() != 57 mozilla::ipc::PrincipalInfo::TContentPrincipalInfo) { 58 // only content principal. 59 return; 60 } 61 62 RefPtr<Runnable> r = NS_NewRunnableFunction( 63 "StorageActivityService::SendActivity", [aPrincipalInfo]() { 64 MOZ_ASSERT(NS_IsMainThread()); 65 66 auto principalOrErr = 67 mozilla::ipc::PrincipalInfoToPrincipal(aPrincipalInfo); 68 69 if (principalOrErr.isOk()) { 70 nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap(); 71 StorageActivityService::SendActivity(principal); 72 } else { 73 NS_WARNING( 74 "Could not obtain principal from " 75 "mozilla::ipc::PrincipalInfoToPrincipal"); 76 } 77 }); 78 79 SchedulerGroup::Dispatch(r.forget()); 80 } 81 82 /* static */ 83 void StorageActivityService::SendActivity(const nsACString& aOrigin) { 84 MOZ_ASSERT(XRE_IsParentProcess()); 85 86 nsCString origin; 87 origin.Assign(aOrigin); 88 89 RefPtr<Runnable> r = NS_NewRunnableFunction( 90 "StorageActivityService::SendActivity", [origin]() { 91 MOZ_ASSERT(NS_IsMainThread()); 92 93 RefPtr<StorageActivityService> service = GetOrCreate(); 94 if (NS_WARN_IF(!service)) { 95 return; 96 } 97 98 service->SendActivityInternal(origin); 99 }); 100 101 if (NS_IsMainThread()) { 102 (void)r->Run(); 103 } else { 104 NS_DispatchToMainThread(r.forget()); 105 } 106 } 107 108 /* static */ 109 already_AddRefed<StorageActivityService> StorageActivityService::GetOrCreate() { 110 MOZ_ASSERT(NS_IsMainThread()); 111 112 if (!gStorageActivityService && !gStorageActivityShutdown) { 113 RefPtr<StorageActivityService> service = new StorageActivityService(); 114 115 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 116 if (NS_WARN_IF(!obs)) { 117 return nullptr; 118 } 119 120 nsresult rv = 121 obs->AddObserver(service, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true); 122 if (NS_WARN_IF(NS_FAILED(rv))) { 123 return nullptr; 124 } 125 126 gStorageActivityService = service; 127 } 128 129 RefPtr<StorageActivityService> service = gStorageActivityService; 130 return service.forget(); 131 } 132 133 StorageActivityService::StorageActivityService() { 134 MOZ_ASSERT(NS_IsMainThread()); 135 } 136 137 StorageActivityService::~StorageActivityService() { 138 MOZ_ASSERT(NS_IsMainThread()); 139 } 140 141 void StorageActivityService::SendActivityInternal(nsIPrincipal* aPrincipal) { 142 MOZ_ASSERT(NS_IsMainThread()); 143 MOZ_ASSERT(aPrincipal); 144 MOZ_ASSERT(BasePrincipal::Cast(aPrincipal)->Kind() == 145 BasePrincipal::eContentPrincipal); 146 147 if (!XRE_IsParentProcess()) { 148 SendActivityToParent(aPrincipal); 149 return; 150 } 151 152 nsAutoCString origin; 153 nsresult rv = aPrincipal->GetOrigin(origin); 154 if (NS_WARN_IF(NS_FAILED(rv))) { 155 return; 156 } 157 158 SendActivityInternal(origin); 159 } 160 161 void StorageActivityService::SendActivityInternal(const nsACString& aOrigin) { 162 MOZ_ASSERT(XRE_IsParentProcess()); 163 164 bool shouldAddObserver = mActivities.Count() == 0; 165 mActivities.InsertOrUpdate(aOrigin, PR_Now()); 166 167 if (shouldAddObserver) { 168 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 169 if (NS_WARN_IF(!obs)) { 170 return; 171 } 172 173 obs->AddObserver(this, OBSERVER_TOPIC_IDLE_DAILY, true); 174 } 175 } 176 177 void StorageActivityService::SendActivityToParent(nsIPrincipal* aPrincipal) { 178 MOZ_ASSERT(NS_IsMainThread()); 179 MOZ_ASSERT(!XRE_IsParentProcess()); 180 181 ::mozilla::ipc::PBackgroundChild* actor = 182 ::mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread(); 183 if (NS_WARN_IF(!actor)) { 184 return; 185 } 186 187 mozilla::ipc::PrincipalInfo principalInfo; 188 nsresult rv = 189 mozilla::ipc::PrincipalToPrincipalInfo(aPrincipal, &principalInfo); 190 if (NS_WARN_IF(NS_FAILED(rv))) { 191 return; 192 } 193 194 actor->SendStorageActivity(principalInfo); 195 } 196 197 NS_IMETHODIMP 198 StorageActivityService::Observe(nsISupports* aSubject, const char* aTopic, 199 const char16_t* aData) { 200 MOZ_ASSERT(NS_IsMainThread()); 201 202 if (!strcmp(aTopic, OBSERVER_TOPIC_IDLE_DAILY)) { 203 CleanUp(); 204 return NS_OK; 205 } 206 207 MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)); 208 209 gStorageActivityShutdown = true; 210 gStorageActivityService = nullptr; 211 return NS_OK; 212 } 213 214 void StorageActivityService::CleanUp() { 215 MOZ_ASSERT(NS_IsMainThread()); 216 217 uint64_t now = PR_Now(); 218 219 for (auto iter = mActivities.Iter(); !iter.Done(); iter.Next()) { 220 if ((now - iter.UserData()) / PR_USEC_PER_SEC > TIME_MAX_SECS) { 221 iter.Remove(); 222 } 223 } 224 225 // If no activities, remove the observer. 226 if (mActivities.Count() == 0) { 227 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 228 if (obs) { 229 obs->RemoveObserver(this, OBSERVER_TOPIC_IDLE_DAILY); 230 } 231 } 232 } 233 234 NS_IMETHODIMP 235 StorageActivityService::GetActiveOrigins(PRTime aFrom, PRTime aTo, 236 nsIArray** aRetval) { 237 uint64_t now = PR_Now(); 238 if (((now - aFrom) / PR_USEC_PER_SEC) > TIME_MAX_SECS || aFrom >= aTo) { 239 return NS_ERROR_INVALID_ARG; 240 } 241 242 // Remove expired entries first. 243 CleanUp(); 244 245 nsresult rv = NS_OK; 246 nsCOMPtr<nsIMutableArray> devices = 247 do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); 248 if (NS_WARN_IF(NS_FAILED(rv))) { 249 return rv; 250 } 251 252 for (const auto& activityEntry : mActivities) { 253 if (activityEntry.GetData() >= aFrom && activityEntry.GetData() <= aTo) { 254 RefPtr<BasePrincipal> principal = 255 BasePrincipal::CreateContentPrincipal(activityEntry.GetKey()); 256 MOZ_ASSERT(principal); 257 258 rv = devices->AppendElement(principal); 259 if (NS_WARN_IF(NS_FAILED(rv))) { 260 return rv; 261 } 262 } 263 } 264 265 devices.forget(aRetval); 266 return NS_OK; 267 } 268 269 NS_IMETHODIMP 270 StorageActivityService::MoveOriginInTime(nsIPrincipal* aPrincipal, 271 PRTime aWhen) { 272 if (!XRE_IsParentProcess()) { 273 return NS_ERROR_FAILURE; 274 } 275 276 nsAutoCString origin; 277 nsresult rv = aPrincipal->GetOrigin(origin); 278 if (NS_WARN_IF(NS_FAILED(rv))) { 279 return rv; 280 } 281 282 mActivities.InsertOrUpdate(origin, aWhen / PR_USEC_PER_SEC); 283 return NS_OK; 284 } 285 286 NS_IMETHODIMP 287 StorageActivityService::TestOnlyReset() { 288 const bool shouldRemoveObserver = mActivities.Count() > 0; 289 mActivities.Clear(); 290 if (shouldRemoveObserver) { 291 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 292 if (obs) { 293 obs->RemoveObserver(this, OBSERVER_TOPIC_IDLE_DAILY); 294 } 295 } 296 return NS_OK; 297 } 298 299 NS_INTERFACE_MAP_BEGIN(StorageActivityService) 300 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStorageActivityService) 301 NS_INTERFACE_MAP_ENTRY(nsIStorageActivityService) 302 NS_INTERFACE_MAP_ENTRY(nsIObserver) 303 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) 304 NS_INTERFACE_MAP_END 305 306 NS_IMPL_ADDREF(StorageActivityService) 307 NS_IMPL_RELEASE(StorageActivityService) 308 309 } // namespace mozilla::dom