nsIGlobalObject.cpp (16696B)
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 "nsIGlobalObject.h" 8 9 #include "mozilla/BasePrincipal.h" 10 #include "mozilla/CycleCollectedJSContext.h" 11 #include "mozilla/GlobalFreezeObserver.h" 12 #include "mozilla/GlobalTeardownObserver.h" 13 #include "mozilla/Result.h" 14 #include "mozilla/StaticPrefs_dom.h" 15 #include "mozilla/StorageAccess.h" 16 #include "mozilla/dom/BindingDeclarations.h" 17 #include "mozilla/dom/BlobURLProtocolHandler.h" 18 #include "mozilla/dom/FunctionBinding.h" 19 #include "mozilla/dom/Report.h" 20 #include "mozilla/dom/ReportingObserver.h" 21 #include "mozilla/dom/ServiceWorker.h" 22 #include "mozilla/dom/ServiceWorkerContainer.h" 23 #include "mozilla/dom/ServiceWorkerRegistration.h" 24 #include "mozilla/ipc/PBackgroundSharedTypes.h" 25 #include "nsContentUtils.h" 26 #include "nsGlobalWindowInner.h" 27 #include "nsThreadUtils.h" 28 29 using mozilla::AutoSlowOperation; 30 using mozilla::CycleCollectedJSContext; 31 using mozilla::DOMEventTargetHelper; 32 using mozilla::ErrorResult; 33 using mozilla::GlobalFreezeObserver; 34 using mozilla::GlobalTeardownObserver; 35 using mozilla::IgnoredErrorResult; 36 using mozilla::MallocSizeOf; 37 using mozilla::Maybe; 38 using mozilla::MicroTaskRunnable; 39 using mozilla::dom::BlobURLProtocolHandler; 40 using mozilla::dom::CallerType; 41 using mozilla::dom::ClientInfo; 42 using mozilla::dom::ClientState; 43 using mozilla::dom::Report; 44 using mozilla::dom::ReportingObserver; 45 using mozilla::dom::ServiceWorker; 46 using mozilla::dom::ServiceWorkerContainer; 47 using mozilla::dom::ServiceWorkerDescriptor; 48 using mozilla::dom::ServiceWorkerRegistration; 49 using mozilla::dom::ServiceWorkerRegistrationDescriptor; 50 using mozilla::dom::VoidFunction; 51 52 nsIGlobalObject::nsIGlobalObject() : mIsDying(false), mIsInnerWindow(false) {} 53 54 bool nsIGlobalObject::IsScriptForbidden(JSObject* aCallback, 55 bool aIsJSImplementedWebIDL) const { 56 if (mIsDying) { 57 return true; 58 } 59 60 if (NS_IsMainThread()) { 61 if (aIsJSImplementedWebIDL) { 62 return false; 63 } 64 65 if (!xpc::Scriptability::AllowedIfExists(aCallback)) { 66 return true; 67 } 68 } 69 70 return false; 71 } 72 73 nsIGlobalObject::~nsIGlobalObject() { 74 UnlinkObjectsInGlobal(); 75 DisconnectGlobalTeardownObservers(); 76 DisconnectGlobalFreezeObservers(); 77 MOZ_DIAGNOSTIC_ASSERT(mGlobalTeardownObservers.isEmpty()); 78 MOZ_DIAGNOSTIC_ASSERT(mGlobalFreezeObservers.isEmpty()); 79 } 80 81 nsIPrincipal* nsIGlobalObject::PrincipalOrNull() const { 82 JSObject* global = GetGlobalJSObjectPreserveColor(); 83 if (NS_WARN_IF(!global)) return nullptr; 84 85 return nsContentUtils::ObjectPrincipal(global); 86 } 87 88 void nsIGlobalObject::RegisterHostObjectURI(const nsACString& aURI) { 89 MOZ_ASSERT(!mHostObjectURIs.Contains(aURI)); 90 mHostObjectURIs.AppendElement(aURI); 91 } 92 93 void nsIGlobalObject::UnregisterHostObjectURI(const nsACString& aURI) { 94 mHostObjectURIs.RemoveElement(aURI); 95 } 96 97 namespace { 98 99 class UnlinkHostObjectURIsRunnable final : public mozilla::Runnable { 100 public: 101 explicit UnlinkHostObjectURIsRunnable(nsTArray<nsCString>&& aURIs) 102 : mozilla::Runnable("UnlinkHostObjectURIsRunnable"), 103 mURIs(std::move(aURIs)) {} 104 105 NS_IMETHOD Run() override { 106 MOZ_ASSERT(NS_IsMainThread()); 107 BlobURLProtocolHandler::RemoveDataEntries(mURIs); 108 return NS_OK; 109 } 110 111 private: 112 ~UnlinkHostObjectURIsRunnable() = default; 113 114 const nsTArray<nsCString> mURIs; 115 }; 116 117 } // namespace 118 119 void nsIGlobalObject::UnlinkObjectsInGlobal() { 120 if (!mHostObjectURIs.IsEmpty()) { 121 // BlobURLProtocolHandler is main-thread only. 122 if (NS_IsMainThread()) { 123 BlobURLProtocolHandler::RemoveDataEntries(mHostObjectURIs); 124 mHostObjectURIs.Clear(); 125 } else { 126 RefPtr<UnlinkHostObjectURIsRunnable> runnable = 127 new UnlinkHostObjectURIsRunnable(std::move(mHostObjectURIs)); 128 MOZ_ASSERT(mHostObjectURIs.IsEmpty()); 129 130 nsresult rv = NS_DispatchToMainThread(runnable); 131 if (NS_FAILED(rv)) { 132 NS_WARNING("Failed to dispatch a runnable to the main-thread."); 133 } 134 } 135 } 136 137 ClearReports(); 138 mReportingObservers.Clear(); 139 mCountQueuingStrategySizeFunction = nullptr; 140 mByteLengthQueuingStrategySizeFunction = nullptr; 141 } 142 143 void nsIGlobalObject::TraverseObjectsInGlobal( 144 nsCycleCollectionTraversalCallback& cb) { 145 // Currently we only store BlobImpl objects off the the main-thread and they 146 // are not CCed. 147 if (!mHostObjectURIs.IsEmpty() && NS_IsMainThread()) { 148 for (uint32_t index = 0; index < mHostObjectURIs.Length(); ++index) { 149 BlobURLProtocolHandler::Traverse(mHostObjectURIs[index], cb); 150 } 151 } 152 153 nsIGlobalObject* tmp = this; 154 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReportBuffer) 155 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReportingObservers) 156 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCountQueuingStrategySizeFunction) 157 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mByteLengthQueuingStrategySizeFunction) 158 } 159 160 void nsIGlobalObject::AddGlobalTeardownObserver( 161 GlobalTeardownObserver* aObject) { 162 MOZ_DIAGNOSTIC_ASSERT(aObject); 163 MOZ_ASSERT(!aObject->isInList()); 164 mGlobalTeardownObservers.insertBack(aObject); 165 } 166 167 void nsIGlobalObject::RemoveGlobalTeardownObserver( 168 GlobalTeardownObserver* aObject) { 169 MOZ_DIAGNOSTIC_ASSERT(aObject); 170 MOZ_ASSERT(aObject->isInList()); 171 MOZ_ASSERT(aObject->GetOwnerGlobal() == this); 172 aObject->remove(); 173 } 174 175 void nsIGlobalObject::AddGlobalFreezeObserver(GlobalFreezeObserver* aObserver) { 176 MOZ_DIAGNOSTIC_ASSERT(aObserver); 177 MOZ_ASSERT(!aObserver->isInList()); 178 mGlobalFreezeObservers.insertBack(aObserver); 179 } 180 181 void nsIGlobalObject::RemoveGlobalFreezeObserver( 182 GlobalFreezeObserver* aObserver) { 183 MOZ_DIAGNOSTIC_ASSERT(aObserver); 184 MOZ_ASSERT(aObserver->isInList()); 185 aObserver->remove(); 186 } 187 188 void nsIGlobalObject::ForEachGlobalTeardownObserver( 189 const std::function<void(GlobalTeardownObserver*, bool* aDoneOut)>& aFunc) 190 const { 191 // Protect against the function call triggering a mutation of the list 192 // while we are iterating by copying the observer references to a temporary 193 // list. 194 AutoTArray<RefPtr<GlobalTeardownObserver>, 64> targetList; 195 for (const GlobalTeardownObserver* observer : mGlobalTeardownObservers) { 196 targetList.AppendElement(const_cast<GlobalTeardownObserver*>(observer)); 197 } 198 199 // Iterate the target list and call the function on each one. 200 bool done = false; 201 for (auto& target : targetList) { 202 // Check to see if a previous iteration's callback triggered the removal 203 // of this target as a side-effect. If it did, then just ignore it. 204 if (target->GetOwnerGlobal() != this) { 205 continue; 206 } 207 aFunc(target, &done); 208 if (done) { 209 break; 210 } 211 } 212 } 213 214 void nsIGlobalObject::DisconnectGlobalTeardownObservers() { 215 ForEachGlobalTeardownObserver( 216 [&](GlobalTeardownObserver* aTarget, bool* aDoneOut) { 217 aTarget->DisconnectFromOwner(); 218 219 // Calling DisconnectFromOwner() should result in 220 // RemoveGlobalTeardownObserver() being called. 221 MOZ_DIAGNOSTIC_ASSERT(aTarget->GetOwnerGlobal() != this); 222 }); 223 } 224 225 void nsIGlobalObject::ForEachGlobalFreezeObserver( 226 const std::function<void(GlobalFreezeObserver*, bool* aDoneOut)>& aFunc) 227 const { 228 // Protect against the function call triggering a mutation of the list 229 // while we are iterating by copying the observer references to a temporary 230 // list. 231 AutoTArray<RefPtr<GlobalFreezeObserver>, 64> targetList; 232 for (const GlobalFreezeObserver* observer : mGlobalFreezeObservers) { 233 targetList.AppendElement(const_cast<GlobalFreezeObserver*>(observer)); 234 } 235 236 // Iterate the target list and call the function on each one. 237 bool done = false; 238 for (auto& target : targetList) { 239 // Check to see if a previous iteration's callback triggered the removal 240 // of this target as a side-effect. If it did, then just ignore it. 241 if (!target->Observing()) { 242 continue; 243 } 244 aFunc(target, &done); 245 if (done) { 246 break; 247 } 248 } 249 } 250 251 void nsIGlobalObject::DisconnectGlobalFreezeObservers() { 252 ForEachGlobalFreezeObserver( 253 [&](GlobalFreezeObserver* aTarget, bool* aDoneOut) { 254 aTarget->DisconnectFreezeObserver(); 255 }); 256 } 257 258 void nsIGlobalObject::NotifyGlobalFrozen() { 259 ForEachGlobalFreezeObserver( 260 [&](GlobalFreezeObserver* aTarget, bool* aDoneOut) { 261 aTarget->FrozenCallback(this); 262 }); 263 } 264 265 void nsIGlobalObject::NotifyGlobalThawed() { 266 ForEachGlobalFreezeObserver( 267 [&](GlobalFreezeObserver* aTarget, bool* aDoneOut) { 268 aTarget->ThawedCallback(this); 269 }); 270 } 271 272 nsIURI* nsIGlobalObject::GetBaseURI() const { return nullptr; } 273 274 Maybe<ClientInfo> nsIGlobalObject::GetClientInfo() const { 275 // By default globals do not expose themselves as a client. Only real 276 // window and worker globals are currently considered clients. 277 return Maybe<ClientInfo>(); 278 } 279 280 Maybe<ClientState> nsIGlobalObject::GetClientState() const { 281 // By default globals do not expose themselves as a client. Only real 282 // window and worker globals are currently considered clients. 283 return Maybe<ClientState>(); 284 } 285 286 Maybe<nsID> nsIGlobalObject::GetAgentClusterId() const { 287 Maybe<ClientInfo> ci = GetClientInfo(); 288 if (ci.isSome()) { 289 return ci.value().AgentClusterId(); 290 } 291 return mozilla::Nothing(); 292 } 293 294 Maybe<ServiceWorkerDescriptor> nsIGlobalObject::GetController() const { 295 // By default globals do not have a service worker controller. Only real 296 // window and worker globals can currently be controlled as a client. 297 return Maybe<ServiceWorkerDescriptor>(); 298 } 299 300 already_AddRefed<ServiceWorkerContainer> 301 nsIGlobalObject::GetServiceWorkerContainer() { 302 return nullptr; 303 } 304 305 RefPtr<ServiceWorker> nsIGlobalObject::GetOrCreateServiceWorker( 306 const ServiceWorkerDescriptor& aDescriptor) { 307 MOZ_DIAGNOSTIC_CRASH("this global should not have any service workers"); 308 return nullptr; 309 } 310 311 RefPtr<ServiceWorkerRegistration> nsIGlobalObject::GetServiceWorkerRegistration( 312 const mozilla::dom::ServiceWorkerRegistrationDescriptor& aDescriptor) 313 const { 314 MOZ_DIAGNOSTIC_CRASH("this global should not have any service workers"); 315 return nullptr; 316 } 317 318 RefPtr<ServiceWorkerRegistration> 319 nsIGlobalObject::GetOrCreateServiceWorkerRegistration( 320 const ServiceWorkerRegistrationDescriptor& aDescriptor) { 321 MOZ_DIAGNOSTIC_ASSERT( 322 false, "this global should not have any service worker registrations"); 323 return nullptr; 324 } 325 326 mozilla::StorageAccess nsIGlobalObject::GetStorageAccess() { 327 return mozilla::StorageAccess::eDeny; 328 } 329 330 nsICookieJarSettings* nsIGlobalObject::GetCookieJarSettings() { 331 return nullptr; 332 } 333 334 nsPIDOMWindowInner* nsIGlobalObject::GetAsInnerWindow() { 335 if (MOZ_LIKELY(mIsInnerWindow)) { 336 return static_cast<nsPIDOMWindowInner*>( 337 static_cast<nsGlobalWindowInner*>(this)); 338 } 339 return nullptr; 340 } 341 342 size_t nsIGlobalObject::ShallowSizeOfExcludingThis(MallocSizeOf aSizeOf) const { 343 size_t rtn = mHostObjectURIs.ShallowSizeOfExcludingThis(aSizeOf); 344 return rtn; 345 } 346 347 class QueuedMicrotask : public MicroTaskRunnable { 348 public: 349 QueuedMicrotask(nsIGlobalObject* aGlobal, VoidFunction& aCallback) 350 : mGlobal(aGlobal), mCallback(&aCallback) {} 351 352 MOZ_CAN_RUN_SCRIPT_BOUNDARY void Run(AutoSlowOperation& aAso) final { 353 IgnoredErrorResult rv; 354 MOZ_KnownLive(mCallback)->Call(static_cast<ErrorResult&>(rv)); 355 } 356 357 bool Suppressed() final { return mGlobal->IsInSyncOperation(); } 358 359 private: 360 nsCOMPtr<nsIGlobalObject> mGlobal; 361 RefPtr<VoidFunction> mCallback; 362 }; 363 364 void nsIGlobalObject::QueueMicrotask(VoidFunction& aCallback) { 365 CycleCollectedJSContext* context = CycleCollectedJSContext::Get(); 366 if (context) { 367 RefPtr<MicroTaskRunnable> mt = new QueuedMicrotask(this, aCallback); 368 context->DispatchToMicroTask(mt.forget()); 369 } 370 } 371 372 void nsIGlobalObject::RegisterReportingObserver(ReportingObserver* aObserver, 373 bool aBuffered) { 374 MOZ_ASSERT(aObserver); 375 376 if (mReportingObservers.Contains(aObserver)) { 377 return; 378 } 379 380 if (NS_WARN_IF( 381 !mReportingObservers.AppendElement(aObserver, mozilla::fallible))) { 382 return; 383 } 384 385 if (!aBuffered) { 386 return; 387 } 388 389 for (const auto& report : mReportBuffer) { 390 aObserver->MaybeReport(report); 391 } 392 } 393 394 void nsIGlobalObject::UnregisterReportingObserver( 395 ReportingObserver* aObserver) { 396 MOZ_ASSERT(aObserver); 397 mReportingObservers.RemoveElement(aObserver); 398 } 399 400 void nsIGlobalObject::BroadcastReport(Report* aReport) { 401 MOZ_ASSERT(aReport); 402 403 for (ReportingObserver* observer : mReportingObservers) { 404 observer->MaybeReport(aReport); 405 } 406 407 if (NS_WARN_IF(!mReportBuffer.AppendElement(aReport, mozilla::fallible))) { 408 return; 409 } 410 411 uint32_t& count = mReportPerTypeCount.LookupOrInsert(aReport->Type()); 412 ++count; 413 414 const uint32_t maxReportCount = 415 mozilla::StaticPrefs::dom_reporting_delivering_maxReports(); 416 const nsString& reportType = aReport->Type(); 417 418 for (size_t i = 0u; count > maxReportCount && i < mReportBuffer.Length();) { 419 if (mReportBuffer[i]->Type() == reportType) { 420 mReportBuffer.RemoveElementAt(i); 421 --count; 422 } else { 423 ++i; 424 } 425 } 426 } 427 428 void nsIGlobalObject::NotifyReportingObservers() { 429 for (auto& observer : mReportingObservers.Clone()) { 430 // MOZ_KnownLive because the clone of 'mReportingObservers' is guaranteed to 431 // keep it alive. 432 // 433 // This can go away once 434 // https://bugzilla.mozilla.org/show_bug.cgi?id=1620312 is fixed. 435 MOZ_KnownLive(observer)->MaybeNotify(); 436 } 437 } 438 439 void nsIGlobalObject::RemoveReportRecords() { 440 ClearReports(); 441 442 for (auto& observer : mReportingObservers) { 443 observer->ForgetReports(); 444 } 445 } 446 447 already_AddRefed<mozilla::dom::Function> 448 nsIGlobalObject::GetCountQueuingStrategySizeFunction() { 449 return do_AddRef(mCountQueuingStrategySizeFunction); 450 } 451 452 void nsIGlobalObject::SetCountQueuingStrategySizeFunction( 453 mozilla::dom::Function* aFunction) { 454 mCountQueuingStrategySizeFunction = aFunction; 455 } 456 457 already_AddRefed<mozilla::dom::Function> 458 nsIGlobalObject::GetByteLengthQueuingStrategySizeFunction() { 459 return do_AddRef(mByteLengthQueuingStrategySizeFunction); 460 } 461 462 void nsIGlobalObject::SetByteLengthQueuingStrategySizeFunction( 463 mozilla::dom::Function* aFunction) { 464 mByteLengthQueuingStrategySizeFunction = aFunction; 465 } 466 467 mozilla::Result<mozilla::ipc::PrincipalInfo, nsresult> 468 nsIGlobalObject::GetStorageKey() { 469 return mozilla::Err(NS_ERROR_NOT_AVAILABLE); 470 } 471 472 mozilla::Result<bool, nsresult> nsIGlobalObject::HasEqualStorageKey( 473 const mozilla::ipc::PrincipalInfo& aStorageKey) { 474 auto result = GetStorageKey(); 475 if (result.isErr()) { 476 return result.propagateErr(); 477 } 478 479 const auto& storageKey = result.inspect(); 480 481 return mozilla::ipc::StorageKeysEqual(storageKey, aStorageKey); 482 } 483 484 mozilla::RTPCallerType nsIGlobalObject::GetRTPCallerType() const { 485 if (PrincipalOrNull() && PrincipalOrNull()->IsSystemPrincipal()) { 486 return RTPCallerType::SystemPrincipal; 487 } 488 489 if (ShouldResistFingerprinting(RFPTarget::ReduceTimerPrecision)) { 490 return RTPCallerType::ResistFingerprinting; 491 } 492 493 if (CrossOriginIsolated()) { 494 return RTPCallerType::CrossOriginIsolated; 495 } 496 497 return RTPCallerType::Normal; 498 } 499 500 bool nsIGlobalObject::ShouldResistFingerprinting(CallerType aCallerType, 501 RFPTarget aTarget) const { 502 return aCallerType != CallerType::System && 503 ShouldResistFingerprinting(aTarget); 504 } 505 506 bool nsIGlobalObject::IsRFPTargetActive(const nsAString& aTargetName, 507 mozilla::ErrorResult& aRv) { 508 MOZ_ASSERT(mozilla::StaticPrefs::privacy_fingerprintingProtection_testing()); 509 510 Maybe<RFPTarget> target = mozilla::nsRFPService::TextToRFPTarget(aTargetName); 511 if (NS_WARN_IF(!target)) { 512 aRv.Throw(NS_ERROR_INVALID_ARG); 513 return false; 514 } 515 516 return ShouldResistFingerprinting(*target); 517 } 518 519 void nsIGlobalObject::ReportToConsole( 520 uint32_t aErrorFlags, const nsCString& aCategory, 521 nsContentUtils::PropertiesFile aFile, const nsCString& aMessageName, 522 const nsTArray<nsString>& aParams, 523 const mozilla::SourceLocation& aLocation) { 524 // We pass nullptr for the document because nsGlobalWindowInner handles the 525 // case where it should be non-null. We also expect the worker impl to 526 // override. 527 nsContentUtils::ReportToConsole(aErrorFlags, aCategory, nullptr, aFile, 528 aMessageName.get(), aParams, aLocation); 529 } 530 531 void nsIGlobalObject::ClearReports() { 532 mReportBuffer.Clear(); 533 mReportPerTypeCount.Clear(); 534 }