ServiceWorkerInfo.cpp (11509B)
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 "ServiceWorkerInfo.h" 8 9 #include "ServiceWorkerManager.h" 10 #include "ServiceWorkerPrivate.h" 11 #include "ServiceWorkerScriptCache.h" 12 #include "ServiceWorkerUtils.h" 13 #include "mozilla/dom/ClientIPCTypes.h" 14 #include "mozilla/dom/ClientState.h" 15 #include "mozilla/dom/Promise.h" 16 #include "mozilla/dom/RemoteWorkerTypes.h" 17 #include "mozilla/dom/WorkerPrivate.h" 18 19 namespace mozilla::dom { 20 21 using mozilla::ipc::PrincipalInfo; 22 23 static_assert(nsIServiceWorkerInfo::STATE_PARSED == 24 static_cast<uint16_t>(ServiceWorkerState::Parsed), 25 "ServiceWorkerState enumeration value should match state values " 26 "from nsIServiceWorkerInfo."); 27 static_assert(nsIServiceWorkerInfo::STATE_INSTALLING == 28 static_cast<uint16_t>(ServiceWorkerState::Installing), 29 "ServiceWorkerState enumeration value should match state values " 30 "from nsIServiceWorkerInfo."); 31 static_assert(nsIServiceWorkerInfo::STATE_INSTALLED == 32 static_cast<uint16_t>(ServiceWorkerState::Installed), 33 "ServiceWorkerState enumeration value should match state values " 34 "from nsIServiceWorkerInfo."); 35 static_assert(nsIServiceWorkerInfo::STATE_ACTIVATING == 36 static_cast<uint16_t>(ServiceWorkerState::Activating), 37 "ServiceWorkerState enumeration value should match state values " 38 "from nsIServiceWorkerInfo."); 39 static_assert(nsIServiceWorkerInfo::STATE_ACTIVATED == 40 static_cast<uint16_t>(ServiceWorkerState::Activated), 41 "ServiceWorkerState enumeration value should match state values " 42 "from nsIServiceWorkerInfo."); 43 static_assert(nsIServiceWorkerInfo::STATE_REDUNDANT == 44 static_cast<uint16_t>(ServiceWorkerState::Redundant), 45 "ServiceWorkerState enumeration value should match state values " 46 "from nsIServiceWorkerInfo."); 47 static_assert(nsIServiceWorkerInfo::STATE_UNKNOWN == 48 ContiguousEnumSize<ServiceWorkerState>::value, 49 "ServiceWorkerState enumeration value should match state values " 50 "from nsIServiceWorkerInfo."); 51 52 NS_IMPL_ISUPPORTS(ServiceWorkerInfo, nsIServiceWorkerInfo) 53 54 NS_IMETHODIMP 55 ServiceWorkerInfo::GetId(nsAString& aId) { 56 MOZ_ASSERT(NS_IsMainThread()); 57 aId = mWorkerPrivateId; 58 return NS_OK; 59 } 60 61 NS_IMETHODIMP 62 ServiceWorkerInfo::GetScriptSpec(nsAString& aScriptSpec) { 63 MOZ_ASSERT(NS_IsMainThread()); 64 CopyUTF8toUTF16(mDescriptor.ScriptURL(), aScriptSpec); 65 return NS_OK; 66 } 67 68 NS_IMETHODIMP 69 ServiceWorkerInfo::GetCacheName(nsAString& aCacheName) { 70 MOZ_ASSERT(NS_IsMainThread()); 71 aCacheName = mCacheName; 72 return NS_OK; 73 } 74 75 NS_IMETHODIMP 76 ServiceWorkerInfo::GetLaunchCount(uint32_t* aLaunchCount) { 77 MOZ_ASSERT(aLaunchCount); 78 MOZ_ASSERT(NS_IsMainThread()); 79 *aLaunchCount = mServiceWorkerPrivate->GetLaunchCount(); 80 return NS_OK; 81 } 82 83 NS_IMETHODIMP 84 ServiceWorkerInfo::GetState(uint16_t* aState) { 85 MOZ_ASSERT(aState); 86 MOZ_ASSERT(NS_IsMainThread()); 87 *aState = static_cast<uint16_t>(State()); 88 return NS_OK; 89 } 90 91 NS_IMETHODIMP 92 ServiceWorkerInfo::GetDebugger(nsIWorkerDebugger** aResult) { 93 if (NS_WARN_IF(!aResult)) { 94 return NS_ERROR_FAILURE; 95 } 96 97 return mServiceWorkerPrivate->GetDebugger(aResult); 98 } 99 100 NS_IMETHODIMP 101 ServiceWorkerInfo::GetHandlesFetchEvents(bool* aValue) { 102 MOZ_ASSERT(aValue); 103 MOZ_ASSERT(NS_IsMainThread()); 104 105 if (mHandlesFetch == Unknown) { 106 return NS_ERROR_FAILURE; 107 } 108 109 *aValue = HandlesFetch(); 110 return NS_OK; 111 } 112 113 NS_IMETHODIMP 114 ServiceWorkerInfo::GetInstalledTime(PRTime* _retval) { 115 MOZ_ASSERT(NS_IsMainThread()); 116 MOZ_ASSERT(_retval); 117 *_retval = mInstalledTime; 118 return NS_OK; 119 } 120 121 NS_IMETHODIMP 122 ServiceWorkerInfo::GetActivatedTime(PRTime* _retval) { 123 MOZ_ASSERT(NS_IsMainThread()); 124 MOZ_ASSERT(_retval); 125 *_retval = mActivatedTime; 126 return NS_OK; 127 } 128 129 NS_IMETHODIMP 130 ServiceWorkerInfo::GetRedundantTime(PRTime* _retval) { 131 MOZ_ASSERT(NS_IsMainThread()); 132 MOZ_ASSERT(_retval); 133 *_retval = mRedundantTime; 134 return NS_OK; 135 } 136 137 NS_IMETHODIMP 138 ServiceWorkerInfo::GetLifetimeDeadline(double* aLifetimeDeadline) { 139 MOZ_ASSERT(NS_IsMainThread()); 140 MOZ_ASSERT(aLifetimeDeadline); 141 TimeStamp deadline = mServiceWorkerPrivate->GetLifetimeDeadline(); 142 if (deadline.IsNull()) { 143 *aLifetimeDeadline = 0; 144 return NS_OK; 145 } 146 *aLifetimeDeadline = 147 (deadline - TimeStamp::ProcessCreation()).ToMilliseconds(); 148 return NS_OK; 149 } 150 151 NS_IMETHODIMP 152 ServiceWorkerInfo::GetNavigationFaultCount(uint32_t* aNavigationFaultCount) { 153 MOZ_ASSERT(NS_IsMainThread()); 154 MOZ_ASSERT(aNavigationFaultCount); 155 *aNavigationFaultCount = mNavigationFaultCount; 156 return NS_OK; 157 } 158 159 NS_IMETHODIMP 160 ServiceWorkerInfo::GetTestingInjectCancellation( 161 nsresult* aTestingInjectCancellation) { 162 MOZ_ASSERT(NS_IsMainThread()); 163 MOZ_ASSERT(aTestingInjectCancellation); 164 *aTestingInjectCancellation = mTestingInjectCancellation; 165 return NS_OK; 166 } 167 168 NS_IMETHODIMP 169 ServiceWorkerInfo::SetTestingInjectCancellation( 170 nsresult aTestingInjectCancellation) { 171 MOZ_ASSERT(NS_IsMainThread()); 172 mTestingInjectCancellation = aTestingInjectCancellation; 173 return NS_OK; 174 } 175 176 NS_IMETHODIMP 177 ServiceWorkerInfo::AttachDebugger() { 178 return mServiceWorkerPrivate->AttachDebugger(); 179 } 180 181 NS_IMETHODIMP 182 ServiceWorkerInfo::DetachDebugger() { 183 return mServiceWorkerPrivate->DetachDebugger(); 184 } 185 186 NS_IMETHODIMP 187 ServiceWorkerInfo::TerminateWorker(JSContext* aCx, 188 mozilla::dom::Promise** aPromise) { 189 nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx); 190 if (NS_WARN_IF(!global)) { 191 return NS_ERROR_FAILURE; 192 } 193 194 ErrorResult rv; 195 RefPtr<Promise> outer = Promise::Create(global, rv); 196 if (NS_WARN_IF(rv.Failed())) { 197 return rv.StealNSResult(); 198 } 199 200 mServiceWorkerPrivate->TerminateWorker(Some(outer)); 201 return NS_OK; 202 } 203 204 void ServiceWorkerInfo::UpdateState(ServiceWorkerState aState) { 205 MOZ_ASSERT(NS_IsMainThread()); 206 #ifdef DEBUG 207 // Any state can directly transition to redundant, but everything else is 208 // ordered. 209 if (aState != ServiceWorkerState::Redundant) { 210 MOZ_ASSERT_IF(State() == ServiceWorkerState::Installing, 211 aState == ServiceWorkerState::Installed); 212 MOZ_ASSERT_IF(State() == ServiceWorkerState::Installed, 213 aState == ServiceWorkerState::Activating); 214 MOZ_ASSERT_IF(State() == ServiceWorkerState::Activating, 215 aState == ServiceWorkerState::Activated); 216 } 217 // Activated can only go to redundant. 218 MOZ_ASSERT_IF(State() == ServiceWorkerState::Activated, 219 aState == ServiceWorkerState::Redundant); 220 #endif 221 // Flush any pending functional events to the worker when it transitions to 222 // the activated state. 223 // TODO: Do we care that these events will race with the propagation of the 224 // state change? 225 if (State() != aState) { 226 mServiceWorkerPrivate->UpdateState(aState); 227 } 228 mDescriptor.SetState(aState); 229 if (State() == ServiceWorkerState::Redundant) { 230 serviceWorkerScriptCache::PurgeCache(mPrincipal, mCacheName); 231 mServiceWorkerPrivate->NoteDeadServiceWorkerInfo(); 232 } 233 } 234 235 ServiceWorkerInfo::ServiceWorkerInfo( 236 nsIPrincipal* aPrincipal, const nsACString& aScope, const WorkerType& aType, 237 uint64_t aRegistrationId, uint64_t aRegistrationVersion, 238 const nsACString& aScriptSpec, const nsAString& aCacheName, 239 nsLoadFlags aImportsLoadFlags) 240 : mPrincipal(aPrincipal), 241 mDescriptor(GetNextID(), aRegistrationId, aRegistrationVersion, 242 aPrincipal, aScope, aType, aScriptSpec, 243 ServiceWorkerState::Parsed), 244 mCacheName(aCacheName), 245 mWorkerPrivateId(ComputeWorkerPrivateId()), 246 mImportsLoadFlags(aImportsLoadFlags), 247 mCreationTime(PR_Now()), 248 mCreationTimeStamp(TimeStamp::Now()), 249 mInstalledTime(0), 250 mActivatedTime(0), 251 mRedundantTime(0), 252 mServiceWorkerPrivate(new ServiceWorkerPrivate(this)), 253 mSkipWaitingFlag(false), 254 mHandlesFetch(Unknown), 255 mNavigationFaultCount(0), 256 mTestingInjectCancellation(NS_OK) { 257 MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default); 258 MOZ_ASSERT(mPrincipal); 259 // cache origin attributes so we can use them off main thread 260 mOriginAttributes = mPrincipal->OriginAttributesRef(); 261 MOZ_ASSERT(!mDescriptor.ScriptURL().IsEmpty()); 262 MOZ_ASSERT(!mCacheName.IsEmpty()); 263 MOZ_ASSERT(!mWorkerPrivateId.IsEmpty()); 264 265 // Scripts of a service worker should always be loaded bypass service workers. 266 // Otherwise, we might not be able to update a service worker correctly, if 267 // there is a service worker generating the script. 268 MOZ_DIAGNOSTIC_ASSERT(mImportsLoadFlags & 269 nsIChannel::LOAD_BYPASS_SERVICE_WORKER); 270 } 271 272 ServiceWorkerInfo::~ServiceWorkerInfo() { 273 MOZ_ASSERT(mServiceWorkerPrivate); 274 mServiceWorkerPrivate->NoteDeadServiceWorkerInfo(); 275 } 276 277 static uint64_t gServiceWorkerInfoCurrentID = 0; 278 279 uint64_t ServiceWorkerInfo::GetNextID() const { 280 return ++gServiceWorkerInfoCurrentID; 281 } 282 283 void ServiceWorkerInfo::PostMessage(RefPtr<ServiceWorkerCloneData>&& aData, 284 const PostMessageSource& aSource) { 285 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); 286 if (NS_WARN_IF(!swm)) { 287 return; 288 } 289 290 ServiceWorkerLifetimeExtension lifetime = 291 ServiceWorkerLifetimeExtension(NoLifetimeExtension{}); 292 293 switch (aSource.type()) { 294 case PostMessageSource::TClientInfoAndState: 295 lifetime = swm->DetermineLifetimeForClient( 296 ClientInfo(aSource.get_ClientInfoAndState().info())); 297 break; 298 case PostMessageSource::TIPCServiceWorkerDescriptor: 299 lifetime = swm->DetermineLifetimeForServiceWorker( 300 ServiceWorkerDescriptor(aSource.get_IPCServiceWorkerDescriptor())); 301 break; 302 default: 303 MOZ_ASSERT_UNREACHABLE("Unexpected source type"); 304 return; 305 } 306 307 mServiceWorkerPrivate->SendMessageEvent(std::move(aData), lifetime, aSource); 308 } 309 310 Maybe<ClientInfo> ServiceWorkerInfo::GetClientInfo() { 311 return mServiceWorkerPrivate->GetClientInfo(); 312 } 313 314 TimeStamp ServiceWorkerInfo::LifetimeDeadline() { 315 return mServiceWorkerPrivate->GetLifetimeDeadline(); 316 } 317 318 void ServiceWorkerInfo::UpdateInstalledTime() { 319 MOZ_ASSERT(State() == ServiceWorkerState::Installed); 320 MOZ_ASSERT(mInstalledTime == 0); 321 322 mInstalledTime = 323 mCreationTime + 324 static_cast<PRTime>( 325 (TimeStamp::Now() - mCreationTimeStamp).ToMicroseconds()); 326 } 327 328 void ServiceWorkerInfo::UpdateActivatedTime() { 329 MOZ_ASSERT(State() == ServiceWorkerState::Activated); 330 MOZ_ASSERT(mActivatedTime == 0); 331 332 mActivatedTime = 333 mCreationTime + 334 static_cast<PRTime>( 335 (TimeStamp::Now() - mCreationTimeStamp).ToMicroseconds()); 336 } 337 338 void ServiceWorkerInfo::UpdateRedundantTime() { 339 MOZ_ASSERT(State() == ServiceWorkerState::Redundant); 340 MOZ_ASSERT(mRedundantTime == 0); 341 342 mRedundantTime = 343 mCreationTime + 344 static_cast<PRTime>( 345 (TimeStamp::Now() - mCreationTimeStamp).ToMicroseconds()); 346 } 347 348 void ServiceWorkerInfo::SetRegistrationVersion(uint64_t aVersion) { 349 mDescriptor.SetRegistrationVersion(aVersion); 350 } 351 352 } // namespace mozilla::dom