SharedWorker.cpp (14791B)
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 file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "SharedWorker.h" 8 9 #include "mozilla/AntiTrackingUtils.h" 10 #include "mozilla/AsyncEventDispatcher.h" 11 #include "mozilla/BasePrincipal.h" 12 #include "mozilla/EventDispatcher.h" 13 #include "mozilla/StorageAccess.h" 14 #include "mozilla/dom/ClientInfo.h" 15 #include "mozilla/dom/Event.h" 16 #include "mozilla/dom/MessageChannel.h" 17 #include "mozilla/dom/MessagePort.h" 18 #include "mozilla/dom/PMessagePort.h" 19 #include "mozilla/dom/RemoteWorkerManager.h" // RemoteWorkerManager::GetRemoteType 20 #include "mozilla/dom/RemoteWorkerTypes.h" 21 #include "mozilla/dom/SharedWorkerBinding.h" 22 #include "mozilla/dom/SharedWorkerChild.h" 23 #include "mozilla/dom/TrustedTypeUtils.h" 24 #include "mozilla/dom/TrustedTypesConstants.h" 25 #include "mozilla/dom/WorkerBinding.h" 26 #include "mozilla/dom/WorkerLoadInfo.h" 27 #include "mozilla/dom/WorkerPrivate.h" 28 #include "mozilla/ipc/BackgroundChild.h" 29 #include "mozilla/ipc/BackgroundUtils.h" 30 #include "mozilla/ipc/PBackgroundChild.h" 31 #include "mozilla/ipc/URIUtils.h" 32 #include "mozilla/net/CookieJarSettings.h" 33 #include "nsGlobalWindowInner.h" 34 #include "nsPIDOMWindow.h" 35 36 #ifdef XP_WIN 37 # undef PostMessage 38 #endif 39 40 using namespace mozilla; 41 using namespace mozilla::dom; 42 using namespace mozilla::ipc; 43 44 SharedWorker::SharedWorker(nsPIDOMWindowInner* aWindow, 45 SharedWorkerChild* aActor, MessagePort* aMessagePort) 46 : DOMEventTargetHelper(aWindow), 47 mWindow(aWindow), 48 mActor(aActor), 49 mMessagePort(aMessagePort), 50 mFrozen(false) { 51 AssertIsOnMainThread(); 52 MOZ_ASSERT(aActor); 53 MOZ_ASSERT(aMessagePort); 54 } 55 56 SharedWorker::~SharedWorker() { 57 AssertIsOnMainThread(); 58 Close(); 59 } 60 61 // static 62 already_AddRefed<SharedWorker> SharedWorker::Constructor( 63 const GlobalObject& aGlobal, const TrustedScriptURLOrUSVString& aScriptURL, 64 const StringOrWorkerOptions& aOptions, ErrorResult& aRv) { 65 AssertIsOnMainThread(); 66 67 if (aOptions.IsString()) { 68 WorkerOptions options; 69 options.mName = aOptions.GetAsString(); 70 return SharedWorker::Constructor(aGlobal, aScriptURL, options, aRv); 71 } 72 73 return SharedWorker::Constructor(aGlobal, aScriptURL, 74 aOptions.GetAsWorkerOptions(), aRv); 75 } 76 77 // static 78 already_AddRefed<SharedWorker> SharedWorker::Constructor( 79 const GlobalObject& aGlobal, const TrustedScriptURLOrUSVString& aScriptURL, 80 const WorkerOptions& aOptions, ErrorResult& aRv) { 81 AssertIsOnMainThread(); 82 83 nsCOMPtr<nsPIDOMWindowInner> window = 84 do_QueryInterface(aGlobal.GetAsSupports()); 85 MOZ_ASSERT(window); 86 87 // Our current idiom is that storage-related APIs specialize for the system 88 // principal themselves, which is consistent with StorageAllowedForwindow not 89 // specializing for the system principal. Without this specialization we 90 // would end up with ePrivateBrowsing for system principaled private browsing 91 // windows which is explicitly not what we want. System Principal code always 92 // should have access to storage. It may make sense to enhance 93 // StorageAllowedForWindow in the future to handle this after comprehensive 94 // auditing. 95 nsCOMPtr<nsIPrincipal> principal = aGlobal.GetSubjectPrincipal(); 96 StorageAccess storageAllowed; 97 if (principal && principal->IsSystemPrincipal()) { 98 storageAllowed = StorageAccess::eAllow; 99 } else { 100 storageAllowed = StorageAllowedForWindow(window); 101 } 102 103 if (storageAllowed == StorageAccess::eDeny) { 104 aRv.ThrowSecurityError("StorageAccess denied."); 105 return nullptr; 106 } 107 108 if (ShouldPartitionStorage(storageAllowed) && 109 !StoragePartitioningEnabled( 110 storageAllowed, window->GetExtantDoc()->CookieJarSettings())) { 111 aRv.ThrowSecurityError("StoragePartitioning not enabled."); 112 return nullptr; 113 } 114 115 // Assert that the principal private browsing state matches the 116 // StorageAccess value. 117 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED 118 if (storageAllowed == StorageAccess::ePrivateBrowsing) { 119 uint32_t privateBrowsingId = 0; 120 if (principal) { 121 MOZ_ALWAYS_SUCCEEDS(principal->GetPrivateBrowsingId(&privateBrowsingId)); 122 } 123 MOZ_DIAGNOSTIC_ASSERT(privateBrowsingId != 0); 124 } 125 #endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED 126 127 PBackgroundChild* actorChild = BackgroundChild::GetOrCreateForCurrentThread(); 128 if (!actorChild || !actorChild->CanSend()) { 129 aRv.ThrowSecurityError("PBackground not available."); 130 return nullptr; 131 } 132 133 JSContext* cx = aGlobal.Context(); 134 135 constexpr nsLiteralString sink = u"SharedWorker constructor"_ns; 136 Maybe<nsAutoString> compliantStringHolder; 137 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(window); 138 const nsAString* compliantString = 139 TrustedTypeUtils::GetTrustedTypesCompliantString( 140 aScriptURL, sink, kTrustedTypesOnlySinkGroup, *global, principal, 141 compliantStringHolder, aRv); 142 if (aRv.Failed()) { 143 return nullptr; 144 } 145 146 WorkerLoadInfo loadInfo; 147 aRv = WorkerPrivate::GetLoadInfo(cx, window, nullptr, *compliantString, 148 aOptions.mType, aOptions.mCredentials, false, 149 WorkerPrivate::OverrideLoadGroup, 150 WorkerKindShared, &loadInfo); 151 if (NS_WARN_IF(aRv.Failed())) { 152 return nullptr; 153 } 154 155 PrincipalInfo principalInfo; 156 aRv = PrincipalToPrincipalInfo(loadInfo.mPrincipal, &principalInfo); 157 if (NS_WARN_IF(aRv.Failed())) { 158 return nullptr; 159 } 160 161 PrincipalInfo loadingPrincipalInfo; 162 aRv = PrincipalToPrincipalInfo(loadInfo.mLoadingPrincipal, 163 &loadingPrincipalInfo); 164 if (NS_WARN_IF(aRv.Failed())) { 165 return nullptr; 166 } 167 168 // Here, the PartitionedPrincipal is always equal to the SharedWorker's 169 // principal because the channel is not opened yet, and, because of this, it's 170 // not classified. We need to force the correct originAttributes. 171 // 172 // The sharedWorker's principal could be a null principal, e.g. loading a 173 // data url. In this case, we don't need to force the OAs for the partitioned 174 // principal because creating storage from a null principal will fail anyway. 175 // We should only do this for content principals. 176 // 177 // You can find more details in StoragePrincipalHelper.h 178 if (ShouldPartitionStorage(storageAllowed) && 179 BasePrincipal::Cast(loadInfo.mPrincipal)->IsContentPrincipal()) { 180 nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(window); 181 if (!sop) { 182 aRv.ThrowSecurityError("ScriptObjectPrincipal not available."); 183 return nullptr; 184 } 185 186 nsIPrincipal* windowPrincipal = sop->GetPrincipal(); 187 if (!windowPrincipal) { 188 aRv.ThrowSecurityError("WindowPrincipal not available."); 189 return nullptr; 190 } 191 192 nsIPrincipal* windowPartitionedPrincipal = sop->PartitionedPrincipal(); 193 if (!windowPartitionedPrincipal) { 194 aRv.ThrowSecurityError("WindowPartitionedPrincipal not available."); 195 return nullptr; 196 } 197 198 if (!windowPrincipal->Equals(windowPartitionedPrincipal)) { 199 loadInfo.mPartitionedPrincipal = 200 BasePrincipal::Cast(loadInfo.mPrincipal) 201 ->CloneForcingOriginAttributes( 202 BasePrincipal::Cast(windowPartitionedPrincipal) 203 ->OriginAttributesRef()); 204 } 205 } 206 207 PrincipalInfo partitionedPrincipalInfo; 208 if (loadInfo.mPrincipal->Equals(loadInfo.mPartitionedPrincipal)) { 209 partitionedPrincipalInfo = principalInfo; 210 } else { 211 aRv = PrincipalToPrincipalInfo(loadInfo.mPartitionedPrincipal, 212 &partitionedPrincipalInfo); 213 if (NS_WARN_IF(aRv.Failed())) { 214 return nullptr; 215 } 216 } 217 218 // We don't actually care about this MessageChannel, but we use it to 'steal' 219 // its 2 connected ports. 220 RefPtr<MessageChannel> channel = MessageChannel::Constructor(global, aRv); 221 if (NS_WARN_IF(aRv.Failed())) { 222 return nullptr; 223 } 224 225 UniqueMessagePortId portIdentifier; 226 channel->Port1()->CloneAndDisentangle(portIdentifier); 227 228 URIParams resolvedScriptURL; 229 SerializeURI(loadInfo.mResolvedScriptURI, resolvedScriptURL); 230 231 URIParams baseURL; 232 SerializeURI(loadInfo.mBaseURI, baseURL); 233 234 // Register this component to PBackground. 235 bool isSecureContext = JS::GetIsSecureContext(js::GetContextRealm(cx)); 236 237 Maybe<IPCClientInfo> ipcClientInfo; 238 Maybe<ClientInfo> clientInfo = window->GetClientInfo(); 239 if (clientInfo.isSome()) { 240 ipcClientInfo.emplace(clientInfo.value().ToIPC()); 241 } 242 243 nsID agentClusterId = nsID::GenerateUUID(); 244 245 net::CookieJarSettingsArgs cjsData; 246 MOZ_ASSERT(loadInfo.mCookieJarSettings); 247 net::CookieJarSettings::Cast(loadInfo.mCookieJarSettings)->Serialize(cjsData); 248 249 auto remoteType = RemoteWorkerManager::GetRemoteType( 250 loadInfo.mPrincipal, WorkerKind::WorkerKindShared); 251 if (NS_WARN_IF(remoteType.isErr())) { 252 aRv.Throw(remoteType.unwrapErr()); 253 return nullptr; 254 } 255 256 Maybe<RFPTargetSet> overriddenFingerprintingSettingsArg; 257 if (loadInfo.mOverriddenFingerprintingSettings.isSome()) { 258 overriddenFingerprintingSettingsArg.emplace( 259 loadInfo.mOverriddenFingerprintingSettings.ref()); 260 } 261 262 RemoteWorkerData remoteWorkerData( 263 nsString(*compliantString), baseURL, resolvedScriptURL, aOptions, 264 loadingPrincipalInfo, principalInfo, partitionedPrincipalInfo, 265 loadInfo.mUseRegularPrincipal, loadInfo.mUsingStorageAccess, cjsData, 266 loadInfo.mDomain, isSecureContext, ipcClientInfo, loadInfo.mReferrerInfo, 267 storageAllowed, AntiTrackingUtils::IsThirdPartyWindow(window, nullptr), 268 loadInfo.mShouldResistFingerprinting, overriddenFingerprintingSettingsArg, 269 loadInfo.mIsOn3PCBExceptionList, 270 OriginTrials::FromWindow(nsGlobalWindowInner::Cast(window)), 271 void_t() /* OptionalServiceWorkerData */, agentClusterId, 272 remoteType.unwrap()); 273 274 PSharedWorkerChild* pActor = actorChild->SendPSharedWorkerConstructor( 275 remoteWorkerData, loadInfo.mWindowID, portIdentifier.release()); 276 if (!pActor) { 277 MOZ_ASSERT_UNREACHABLE("We already checked PBackground above."); 278 aRv.ThrowSecurityError("PBackground not available."); 279 return nullptr; 280 } 281 282 RefPtr<SharedWorkerChild> actor = static_cast<SharedWorkerChild*>(pActor); 283 284 RefPtr<SharedWorker> sharedWorker = 285 new SharedWorker(window, actor, channel->Port2()); 286 287 // Let's inform the window about this SharedWorker. 288 nsGlobalWindowInner::Cast(window)->StoreSharedWorker(sharedWorker); 289 actor->SetParent(sharedWorker); 290 291 if (nsGlobalWindowInner::Cast(window)->IsSuspended()) { 292 sharedWorker->Suspend(); 293 } 294 295 return sharedWorker.forget(); 296 } 297 298 MessagePort* SharedWorker::Port() { 299 AssertIsOnMainThread(); 300 return mMessagePort; 301 } 302 303 void SharedWorker::Freeze() { 304 AssertIsOnMainThread(); 305 MOZ_ASSERT(!IsFrozen()); 306 307 if (mFrozen) { 308 return; 309 } 310 311 mFrozen = true; 312 313 if (mActor) { 314 mActor->SendFreeze(); 315 } 316 } 317 318 void SharedWorker::Thaw() { 319 AssertIsOnMainThread(); 320 MOZ_ASSERT(IsFrozen()); 321 322 if (!mFrozen) { 323 return; 324 } 325 326 mFrozen = false; 327 328 if (mActor) { 329 mActor->SendThaw(); 330 } 331 332 if (!mFrozenEvents.IsEmpty()) { 333 nsTArray<RefPtr<Event>> events = std::move(mFrozenEvents); 334 335 for (uint32_t index = 0; index < events.Length(); index++) { 336 RefPtr<Event>& event = events[index]; 337 MOZ_ASSERT(event); 338 339 RefPtr<EventTarget> target = event->GetTarget(); 340 ErrorResult rv; 341 target->DispatchEvent(*event, rv); 342 if (rv.Failed()) { 343 NS_WARNING("Failed to dispatch event!"); 344 } 345 } 346 } 347 } 348 349 void SharedWorker::QueueEvent(Event* aEvent) { 350 AssertIsOnMainThread(); 351 MOZ_ASSERT(aEvent); 352 MOZ_ASSERT(IsFrozen()); 353 354 mFrozenEvents.AppendElement(aEvent); 355 } 356 357 void SharedWorker::Close() { 358 AssertIsOnMainThread(); 359 360 if (mWindow) { 361 nsGlobalWindowInner::Cast(mWindow)->ForgetSharedWorker(this); 362 mWindow = nullptr; 363 } 364 365 if (mActor) { 366 mActor->SendClose(); 367 mActor->SetParent(nullptr); 368 mActor = nullptr; 369 } 370 371 if (mMessagePort) { 372 mMessagePort->Close(); 373 } 374 } 375 376 void SharedWorker::Suspend() { 377 if (mActor) { 378 mActor->SendSuspend(); 379 } 380 } 381 382 void SharedWorker::Resume() { 383 if (mActor) { 384 mActor->SendResume(); 385 } 386 } 387 388 void SharedWorker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage, 389 const Sequence<JSObject*>& aTransferable, 390 ErrorResult& aRv) { 391 AssertIsOnMainThread(); 392 MOZ_ASSERT(mMessagePort); 393 394 mMessagePort->PostMessage(aCx, aMessage, aTransferable, aRv); 395 } 396 397 NS_IMPL_ADDREF_INHERITED(SharedWorker, DOMEventTargetHelper) 398 NS_IMPL_RELEASE_INHERITED(SharedWorker, DOMEventTargetHelper) 399 400 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SharedWorker) 401 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) 402 403 NS_IMPL_CYCLE_COLLECTION_CLASS(SharedWorker) 404 405 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SharedWorker, 406 DOMEventTargetHelper) 407 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow) 408 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessagePort) 409 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrozenEvents) 410 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 411 412 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SharedWorker, 413 DOMEventTargetHelper) 414 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow) 415 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessagePort) 416 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrozenEvents) 417 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 418 419 JSObject* SharedWorker::WrapObject(JSContext* aCx, 420 JS::Handle<JSObject*> aGivenProto) { 421 AssertIsOnMainThread(); 422 423 return SharedWorker_Binding::Wrap(aCx, this, aGivenProto); 424 } 425 426 void SharedWorker::GetEventTargetParent(EventChainPreVisitor& aVisitor) { 427 AssertIsOnMainThread(); 428 429 if (IsFrozen()) { 430 RefPtr<Event> event = aVisitor.mDOMEvent; 431 if (!event) { 432 event = EventDispatcher::CreateEvent(aVisitor.mEvent->mOriginalTarget, 433 aVisitor.mPresContext, 434 aVisitor.mEvent, u""_ns); 435 } 436 437 QueueEvent(event); 438 439 aVisitor.mCanHandle = false; 440 aVisitor.SetParentTarget(nullptr, false); 441 return; 442 } 443 444 DOMEventTargetHelper::GetEventTargetParent(aVisitor); 445 } 446 447 void SharedWorker::DisconnectFromOwner() { 448 Close(); 449 DOMEventTargetHelper::DisconnectFromOwner(); 450 } 451 452 void SharedWorker::ErrorPropagation(nsresult aError) { 453 AssertIsOnMainThread(); 454 MOZ_ASSERT(mActor); 455 MOZ_ASSERT(NS_FAILED(aError)); 456 457 RefPtr<AsyncEventDispatcher> errorEvent = 458 new AsyncEventDispatcher(this, u"error"_ns, CanBubble::eNo); 459 errorEvent->PostDOMEvent(); 460 461 Close(); 462 }