Navigator.cpp (73190B)
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 // Needs to be first. 8 #include "Navigator.h" 9 10 #include "Geolocation.h" 11 #include "base/basictypes.h" 12 #include "mozilla/Components.h" 13 #include "mozilla/ContentBlockingNotifier.h" 14 #include "mozilla/MemoryReporting.h" 15 #include "mozilla/Preferences.h" 16 #include "mozilla/StaticPrefs_dom.h" 17 #include "mozilla/dom/BodyExtractor.h" 18 #include "mozilla/dom/FetchBinding.h" 19 #include "mozilla/dom/File.h" 20 #include "nsCharSeparatedTokenizer.h" 21 #include "nsContentPolicyUtils.h" 22 #include "nsContentUtils.h" 23 #include "nsIClassOfService.h" 24 #include "nsIContentPolicy.h" 25 #include "nsIHttpProtocolHandler.h" 26 #include "nsIPrivateAttributionService.h" 27 #include "nsISupportsPriority.h" 28 #include "nsIWebProtocolHandlerRegistrar.h" 29 #include "nsIXULAppInfo.h" 30 #include "nsMimeTypeArray.h" 31 #include "nsPluginArray.h" 32 #include "nsUnicharUtils.h" 33 #ifdef FUZZING 34 # include "mozilla/StaticPrefs_fuzzing.h" 35 #endif 36 #include "BatteryManager.h" 37 #include "Connection.h" 38 #include "mozilla/ClearOnShutdown.h" 39 #include "mozilla/Hal.h" 40 #include "mozilla/StaticPrefs_media.h" 41 #include "mozilla/StaticPrefs_network.h" 42 #include "mozilla/StaticPrefs_pdfjs.h" 43 #include "mozilla/StaticPrefs_privacy.h" 44 #include "mozilla/StaticPtr.h" 45 #include "mozilla/StorageAccess.h" 46 #include "mozilla/dom/Clipboard.h" 47 #include "mozilla/dom/ContentChild.h" 48 #include "mozilla/dom/CredentialsContainer.h" 49 #include "mozilla/dom/Event.h" // for Event 50 #include "mozilla/dom/FeaturePolicyUtils.h" 51 #include "mozilla/dom/GamepadServiceTest.h" 52 #include "mozilla/dom/LockManager.h" 53 #include "mozilla/dom/MIDIAccessManager.h" 54 #include "mozilla/dom/MIDIOptionsBinding.h" 55 #include "mozilla/dom/MediaCapabilities.h" 56 #include "mozilla/dom/MediaSession.h" 57 #include "mozilla/dom/NavigatorLogin.h" 58 #include "mozilla/dom/Permissions.h" 59 #include "mozilla/dom/PrivateAttribution.h" 60 #include "mozilla/dom/ServiceWorkerContainer.h" 61 #include "mozilla/dom/StorageManager.h" 62 #include "mozilla/dom/TCPSocket.h" 63 #include "mozilla/dom/URLSearchParams.h" 64 #include "mozilla/dom/UserActivation.h" 65 #include "mozilla/dom/VRDisplay.h" 66 #include "mozilla/dom/VRDisplayEvent.h" 67 #include "mozilla/dom/VRServiceTest.h" 68 #include "mozilla/dom/WakeLockJS.h" 69 #include "mozilla/dom/XRSystem.h" 70 #include "mozilla/dom/power/PowerManagerService.h" 71 #include "mozilla/dom/workerinternals/RuntimeService.h" 72 #include "nsComponentManagerUtils.h" 73 #include "nsGlobalWindowInner.h" 74 #include "nsICookieManager.h" 75 #include "nsICookieService.h" 76 #include "nsIHttpChannel.h" 77 #include "nsIPermissionManager.h" 78 #include "nsMimeTypes.h" 79 #include "nsNetUtil.h" 80 #include "nsRFPService.h" 81 #include "nsStringStream.h" 82 #ifdef ENABLE_WEBDRIVER 83 # include "nsIMarionette.h" 84 # include "nsIRemoteAgent.h" 85 #endif 86 #include "BrowserChild.h" 87 #include "MediaManager.h" 88 #include "ReferrerInfo.h" 89 #include "WidgetUtils.h" 90 #include "mozilla/PermissionDelegateHandler.h" 91 #include "mozilla/dom/FormData.h" 92 #include "mozilla/dom/MediaDevices.h" 93 #include "mozilla/dom/Promise.h" 94 #include "mozilla/dom/WorkerPrivate.h" 95 #include "mozilla/dom/WorkerRunnable.h" 96 #include "mozilla/ipc/URIUtils.h" 97 #include "nsIDocShell.h" 98 #include "nsIExternalProtocolHandler.h" 99 #include "nsIScriptError.h" 100 #include "nsIUploadChannel2.h" 101 #include "nsJSUtils.h" 102 #include "nsStreamUtils.h" 103 104 #if defined(XP_WIN) 105 # include "mozilla/WindowsVersion.h" 106 #endif 107 108 #include "AutoplayPolicy.h" 109 #include "mozilla/DetailedPromise.h" 110 #include "mozilla/EMEUtils.h" 111 #include "mozilla/dom/AudioContext.h" 112 #include "mozilla/dom/HTMLMediaElement.h" 113 #include "mozilla/dom/WindowGlobalChild.h" 114 #include "mozilla/intl/LocaleService.h" 115 #include "mozilla/webgpu/Instance.h" 116 117 namespace mozilla::dom { 118 119 static const nsLiteralCString kVibrationPermissionType = "vibration"_ns; 120 121 Navigator::Navigator(nsPIDOMWindowInner* aWindow) : mWindow(aWindow) {} 122 123 Navigator::~Navigator() { Invalidate(); } 124 125 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Navigator) 126 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 127 NS_INTERFACE_MAP_ENTRY(nsISupports) 128 NS_INTERFACE_MAP_END 129 130 NS_IMPL_CYCLE_COLLECTING_ADDREF(Navigator) 131 NS_IMPL_CYCLE_COLLECTING_RELEASE(Navigator) 132 133 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(Navigator) 134 135 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Navigator) 136 tmp->Invalidate(); 137 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow) 138 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSharePromise) 139 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER 140 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 141 142 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Navigator) 143 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPlugins) 144 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPermissions) 145 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGeolocation) 146 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBatteryManager) 147 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBatteryPromise) 148 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConnection) 149 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStorageManager) 150 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCredentials) 151 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaDevices) 152 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServiceWorkerContainer) 153 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaCapabilities) 154 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaSession) 155 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAddonManager) 156 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebGpu) 157 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocks) 158 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLogin) 159 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrivateAttribution) 160 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUserActivation) 161 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWakeLock) 162 163 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow) 164 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaKeySystemAccessManager) 165 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepadServiceTest) 166 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRGetDisplaysPromises) 167 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRServiceTest) 168 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSharePromise) 169 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXRSystem) 170 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mClipboard) 171 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 172 173 void Navigator::Invalidate() { 174 // Don't clear mWindow here so we know we've got a non-null mWindow 175 // until we're unlinked. 176 177 mPlugins = nullptr; 178 179 mPermissions = nullptr; 180 181 if (mStorageManager) { 182 mStorageManager->Shutdown(); 183 mStorageManager = nullptr; 184 } 185 186 // If there is a page transition, make sure delete the geolocation object. 187 if (mGeolocation) { 188 mGeolocation->Shutdown(); 189 mGeolocation = nullptr; 190 } 191 192 if (mBatteryManager) { 193 mBatteryManager->Shutdown(); 194 mBatteryManager = nullptr; 195 } 196 197 mBatteryPromise = nullptr; 198 199 if (mConnection) { 200 mConnection->Shutdown(); 201 mConnection = nullptr; 202 } 203 204 mMediaDevices = nullptr; 205 206 mServiceWorkerContainer = nullptr; 207 208 if (mMediaKeySystemAccessManager) { 209 mMediaKeySystemAccessManager->Shutdown(); 210 mMediaKeySystemAccessManager = nullptr; 211 } 212 213 if (mGamepadServiceTest) { 214 mGamepadServiceTest->Shutdown(); 215 mGamepadServiceTest = nullptr; 216 } 217 218 mVRGetDisplaysPromises.Clear(); 219 220 if (mVRServiceTest) { 221 mVRServiceTest->Shutdown(); 222 mVRServiceTest = nullptr; 223 } 224 225 if (mXRSystem) { 226 mXRSystem->Shutdown(); 227 mXRSystem = nullptr; 228 } 229 230 mMediaCapabilities = nullptr; 231 232 if (mMediaSession) { 233 mMediaSession->Shutdown(); 234 mMediaSession = nullptr; 235 } 236 237 mAddonManager = nullptr; 238 239 mWebGpu = nullptr; 240 241 if (mLocks) { 242 // Unloading a page does not immediately destruct the lock manager actor, 243 // but we want to abort the lock requests as soon as possible. Explicitly 244 // call Shutdown() to do that. 245 mLocks->Shutdown(); 246 mLocks = nullptr; 247 } 248 249 mLogin = nullptr; 250 251 mPrivateAttribution = nullptr; 252 253 mUserActivation = nullptr; 254 255 mSharePromise = nullptr; 256 257 mWakeLock = nullptr; 258 259 mClipboard = nullptr; 260 } 261 262 void Navigator::GetUserAgent(nsAString& aUserAgent, CallerType aCallerType, 263 ErrorResult& aRv) const { 264 nsCOMPtr<nsPIDOMWindowInner> window; 265 266 if (mWindow) { 267 window = mWindow; 268 nsIDocShell* docshell = window->GetDocShell(); 269 nsString customUserAgent; 270 if (docshell) { 271 docshell->GetBrowsingContext()->GetCustomUserAgent(customUserAgent); 272 273 if (!customUserAgent.IsEmpty()) { 274 aUserAgent = customUserAgent; 275 return; 276 } 277 } 278 } 279 280 nsCOMPtr<Document> doc = mWindow->GetExtantDoc(); 281 nsresult rv = GetUserAgent( 282 mWindow, doc, aCallerType == CallerType::System ? Some(false) : Nothing(), 283 aUserAgent); 284 if (NS_WARN_IF(NS_FAILED(rv))) { 285 aRv.Throw(rv); 286 } 287 } 288 289 void Navigator::GetAppCodeName(nsAString& aAppCodeName, ErrorResult& aRv) { 290 nsresult rv; 291 292 nsCOMPtr<nsIHttpProtocolHandler> service( 293 do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv)); 294 if (NS_WARN_IF(NS_FAILED(rv))) { 295 aRv.Throw(rv); 296 return; 297 } 298 299 nsAutoCString appName; 300 rv = service->GetAppName(appName); 301 if (NS_WARN_IF(NS_FAILED(rv))) { 302 aRv.Throw(rv); 303 return; 304 } 305 306 CopyASCIItoUTF16(appName, aAppCodeName); 307 } 308 309 void Navigator::GetAppVersion(nsAString& aAppVersion, CallerType aCallerType, 310 ErrorResult& aRv) const { 311 nsCOMPtr<Document> doc = mWindow->GetExtantDoc(); 312 313 nsresult rv = GetAppVersion( 314 aAppVersion, doc, 315 /* aUsePrefOverriddenValue = */ aCallerType != CallerType::System); 316 if (NS_WARN_IF(NS_FAILED(rv))) { 317 aRv.Throw(rv); 318 } 319 } 320 321 void Navigator::GetAppName(nsAString& aAppName) const { 322 aAppName.AssignLiteral("Netscape"); 323 } 324 325 /** 326 * Returns the value of Accept-Languages (HTTP header) as a nsTArray of 327 * languages. The value is set in the preference by the user ("Content 328 * Languages"). 329 * 330 * "en", "en-US" and "i-cherokee" and "" are valid languages tokens. 331 * 332 * If there is no valid language, the value of getWebExposedLocales is 333 * used to ensure that locale spoofing is honored and to reduce 334 * fingerprinting. 335 * 336 * See RFC 7231, Section 9.7 "Browser Fingerprinting" and 337 * RFC 2616, Section 15.1.4 "Privacy Issues Connected to Accept Headers" 338 * for more detail. 339 */ 340 /* static */ 341 void Navigator::GetAcceptLanguages(nsTArray<nsString>& aLanguages, 342 const nsCString* aLanguageOverride) { 343 MOZ_ASSERT(NS_IsMainThread()); 344 345 aLanguages.Clear(); 346 347 // E.g. "de-de, en-us,en". 348 nsAutoCString acceptLang; 349 if (aLanguageOverride) { 350 acceptLang.Assign(aLanguageOverride->get()); 351 } else { 352 intl::LocaleService::GetInstance()->GetAcceptLanguages(acceptLang); 353 } 354 355 // Split values on commas. 356 for (nsDependentCSubstring lang : 357 nsCCharSeparatedTokenizer(acceptLang, ',').ToRange()) { 358 // Replace "_" with "-" to avoid POSIX/Windows "en_US" notation. 359 // NOTE: we should probably rely on the pref being set correctly. 360 if (lang.Length() > 2 && lang[2] == '_') { 361 lang.Replace(2, 1, '-'); 362 } 363 364 // Use uppercase for country part, e.g. "en-US", not "en-us", see BCP47 365 // only uppercase 2-letter country codes, not "zh-Hant", "de-DE-x-goethe". 366 // NOTE: we should probably rely on the pref being set correctly. 367 if (lang.Length() > 2) { 368 int32_t pos = 0; 369 bool first = true; 370 for (const nsACString& code : 371 nsCCharSeparatedTokenizer(lang, '-').ToRange()) { 372 if (code.Length() == 2 && !first) { 373 nsAutoCString upper(code); 374 ToUpperCase(upper); 375 lang.Replace(pos, code.Length(), upper); 376 } 377 378 pos += code.Length() + 1; // 1 is the separator 379 first = false; 380 } 381 } 382 383 aLanguages.AppendElement(NS_ConvertUTF8toUTF16(lang)); 384 } 385 if (aLanguages.Length() == 0) { 386 nsTArray<nsCString> locales; 387 mozilla::intl::LocaleService::GetInstance()->GetWebExposedLocales(locales); 388 aLanguages.AppendElement(NS_ConvertUTF8toUTF16(locales[0])); 389 } 390 } 391 392 /** 393 * Returns the first language from GetAcceptLanguages. 394 * 395 * Full details above in GetAcceptLanguages. 396 */ 397 void Navigator::GetLanguage(nsAString& aLanguage) { 398 nsTArray<nsString> languages; 399 GetLanguages(languages); 400 MOZ_ASSERT(languages.Length() >= 1); 401 aLanguage.Assign(languages[0]); 402 } 403 404 void Navigator::GetLanguages(nsTArray<nsString>& aLanguages) { 405 BrowsingContext* bc = mWindow ? mWindow->GetBrowsingContext() : nullptr; 406 if (bc) { 407 const nsCString& languageOverride = bc->Top()->GetLanguageOverride(); 408 409 if (!languageOverride.IsEmpty()) { 410 GetAcceptLanguages(aLanguages, &languageOverride); 411 412 return; 413 } 414 } 415 416 GetAcceptLanguages(aLanguages, nullptr); 417 418 // The returned value is cached by the binding code. The window listens to the 419 // accept languages change and will clear the cache when needed. It has to 420 // take care of dispatching the DOM event already and the invalidation and the 421 // event has to be timed correctly. 422 } 423 424 void Navigator::GetPlatform(nsAString& aPlatform, CallerType aCallerType, 425 ErrorResult& aRv) const { 426 if (mWindow) { 427 BrowsingContext* bc = mWindow->GetBrowsingContext(); 428 nsString customPlatform; 429 if (bc) { 430 bc->GetCustomPlatform(customPlatform); 431 432 if (!customPlatform.IsEmpty()) { 433 aPlatform = customPlatform; 434 return; 435 } 436 } 437 } 438 439 nsCOMPtr<Document> doc = mWindow->GetExtantDoc(); 440 441 nsresult rv = GetPlatform( 442 aPlatform, doc, 443 /* aUsePrefOverriddenValue = */ aCallerType != CallerType::System); 444 if (NS_WARN_IF(NS_FAILED(rv))) { 445 aRv.Throw(rv); 446 } 447 } 448 449 void Navigator::GetOscpu(nsAString& aOSCPU, CallerType aCallerType, 450 ErrorResult& aRv) const { 451 if (aCallerType != CallerType::System) { 452 // If fingerprinting resistance is on, we will spoof this value. See 453 // nsRFPService.h for details about spoofed values. 454 if (nsContentUtils::ShouldResistFingerprinting(GetDocShell(), 455 RFPTarget::NavigatorOscpu)) { 456 aOSCPU.AssignLiteral(SPOOFED_OSCPU); 457 return; 458 } 459 460 nsAutoString override; 461 nsresult rv = Preferences::GetString("general.oscpu.override", override); 462 if (NS_SUCCEEDED(rv)) { 463 aOSCPU = override; 464 return; 465 } 466 } 467 468 nsresult rv; 469 nsCOMPtr<nsIHttpProtocolHandler> service( 470 do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv)); 471 if (NS_WARN_IF(NS_FAILED(rv))) { 472 aRv.Throw(rv); 473 return; 474 } 475 476 nsAutoCString oscpu; 477 rv = service->GetOscpu(oscpu); 478 if (NS_WARN_IF(NS_FAILED(rv))) { 479 aRv.Throw(rv); 480 return; 481 } 482 483 CopyASCIItoUTF16(oscpu, aOSCPU); 484 } 485 486 void Navigator::GetVendor(nsAString& aVendor) { aVendor.Truncate(); } 487 488 void Navigator::GetVendorSub(nsAString& aVendorSub) { aVendorSub.Truncate(); } 489 490 void Navigator::GetProduct(nsAString& aProduct) { 491 aProduct.AssignLiteral("Gecko"); 492 } 493 494 void Navigator::GetProductSub(nsAString& aProductSub) { 495 // Legacy build date hardcoded for backward compatibility (bug 776376) 496 aProductSub.AssignLiteral(LEGACY_UA_GECKO_TRAIL); 497 } 498 499 nsMimeTypeArray* Navigator::GetMimeTypes(ErrorResult& aRv) { 500 auto* plugins = GetPlugins(aRv); 501 if (!plugins) { 502 return nullptr; 503 } 504 505 return plugins->MimeTypeArray(); 506 } 507 508 nsPluginArray* Navigator::GetPlugins(ErrorResult& aRv) { 509 if (!mPlugins) { 510 if (!mWindow) { 511 aRv.Throw(NS_ERROR_UNEXPECTED); 512 return nullptr; 513 } 514 mPlugins = MakeRefPtr<nsPluginArray>(mWindow); 515 } 516 517 return mPlugins; 518 } 519 520 bool Navigator::PdfViewerEnabled() { 521 return !StaticPrefs::pdfjs_disabled() || 522 nsContentUtils::ShouldResistFingerprinting(GetDocShell(), 523 RFPTarget::PdfjsSpoof); 524 } 525 526 Permissions* Navigator::GetPermissions(ErrorResult& aRv) { 527 if (!mWindow) { 528 aRv.Throw(NS_ERROR_UNEXPECTED); 529 return nullptr; 530 } 531 532 if (!mPermissions) { 533 mPermissions = new Permissions(mWindow->AsGlobal()); 534 } 535 536 return mPermissions; 537 } 538 539 StorageManager* Navigator::Storage() { 540 MOZ_ASSERT(mWindow); 541 542 if (!mStorageManager) { 543 mStorageManager = new StorageManager(mWindow->AsGlobal()); 544 } 545 546 return mStorageManager; 547 } 548 549 bool Navigator::CookieEnabled() { 550 // Check whether an exception overrides the global cookie behavior 551 // Note that the code for getting the URI here matches that in 552 // nsHTMLDocument::SetCookie. 553 if (!mWindow || !mWindow->GetDocShell()) { 554 return nsICookieManager::GetCookieBehavior(false) != 555 nsICookieService::BEHAVIOR_REJECT; 556 } 557 558 nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(mWindow); 559 uint32_t cookieBehavior = loadContext 560 ? nsICookieManager::GetCookieBehavior( 561 loadContext->UsePrivateBrowsing()) 562 : nsICookieManager::GetCookieBehavior(false); 563 bool cookieEnabled = cookieBehavior != nsICookieService::BEHAVIOR_REJECT; 564 565 nsCOMPtr<Document> doc = mWindow->GetExtantDoc(); 566 if (!doc) { 567 return cookieEnabled; 568 } 569 570 uint32_t rejectedReason = 0; 571 bool granted = false; 572 nsresult rv = doc->NodePrincipal()->HasFirstpartyStorageAccess( 573 mWindow, &rejectedReason, &granted); 574 if (NS_FAILED(rv)) { 575 // Not a content, so technically can't set cookies, but let's 576 // just return the default value. 577 return cookieEnabled; 578 } 579 580 // We should return true if the cookie is partitioned because the cookie is 581 // still available in this case. 582 if (!granted && 583 StoragePartitioningEnabled(rejectedReason, doc->CookieJarSettings())) { 584 granted = true; 585 } 586 587 ContentBlockingNotifier::OnDecision( 588 mWindow, 589 granted ? ContentBlockingNotifier::BlockingDecision::eAllow 590 : ContentBlockingNotifier::BlockingDecision::eBlock, 591 rejectedReason); 592 return granted; 593 } 594 595 bool Navigator::OnLine() { 596 if (nsContentUtils::ShouldResistFingerprinting( 597 GetDocShell(), RFPTarget::NetworkConnection)) { 598 return true; 599 } 600 601 if (mWindow) { 602 // Check if this tab is set to be offline. 603 BrowsingContext* bc = mWindow->GetBrowsingContext(); 604 if (bc && bc->Top()->GetForceOffline()) { 605 return false; 606 } 607 } 608 // Return the default browser value 609 return !NS_IsOffline(); 610 } 611 612 void Navigator::GetBuildID(nsAString& aBuildID, CallerType aCallerType, 613 ErrorResult& aRv) const { 614 if (aCallerType != CallerType::System) { 615 // If fingerprinting resistance is on, we will spoof this value. See 616 // nsRFPService.h for details about spoofed values. 617 if (nsContentUtils::ShouldResistFingerprinting( 618 GetDocShell(), RFPTarget::NavigatorBuildID)) { 619 aBuildID.AssignLiteral(LEGACY_BUILD_ID); 620 return; 621 } 622 623 nsAutoString override; 624 nsresult rv = Preferences::GetString("general.buildID.override", override); 625 if (NS_SUCCEEDED(rv)) { 626 aBuildID = override; 627 return; 628 } 629 630 nsAutoCString host; 631 bool isHTTPS = false; 632 if (mWindow) { 633 nsCOMPtr<Document> doc = mWindow->GetDoc(); 634 if (doc) { 635 nsIURI* uri = doc->GetDocumentURI(); 636 if (uri) { 637 isHTTPS = uri->SchemeIs("https"); 638 if (isHTTPS) { 639 MOZ_ALWAYS_SUCCEEDS(uri->GetHost(host)); 640 } 641 } 642 } 643 } 644 645 // Spoof the buildID on pages not loaded from "https://*.mozilla.org". 646 if (!isHTTPS || !StringEndsWith(host, ".mozilla.org"_ns)) { 647 aBuildID.AssignLiteral(LEGACY_BUILD_ID); 648 return; 649 } 650 } 651 652 nsCOMPtr<nsIXULAppInfo> appInfo = 653 do_GetService("@mozilla.org/xre/app-info;1"); 654 if (!appInfo) { 655 aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); 656 return; 657 } 658 659 nsAutoCString buildID; 660 nsresult rv = appInfo->GetAppBuildID(buildID); 661 if (NS_WARN_IF(NS_FAILED(rv))) { 662 aRv.Throw(rv); 663 return; 664 } 665 666 aBuildID.Truncate(); 667 AppendASCIItoUTF16(buildID, aBuildID); 668 } 669 670 void Navigator::GetDoNotTrack(nsAString& aResult) { 671 if (StaticPrefs::privacy_donottrackheader_enabled()) { 672 aResult.AssignLiteral("1"); 673 } else { 674 aResult.AssignLiteral("unspecified"); 675 } 676 } 677 678 bool Navigator::GlobalPrivacyControl() { 679 bool gpcStatus = StaticPrefs::privacy_globalprivacycontrol_enabled(); 680 if (!gpcStatus) { 681 nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(mWindow); 682 gpcStatus = loadContext && loadContext->UsePrivateBrowsing() && 683 StaticPrefs::privacy_globalprivacycontrol_pbmode_enabled(); 684 } 685 return StaticPrefs::privacy_globalprivacycontrol_functionality_enabled() && 686 gpcStatus; 687 } 688 689 uint64_t Navigator::HardwareConcurrency() { 690 workerinternals::RuntimeService* rts = 691 workerinternals::RuntimeService::GetOrCreateService(); 692 if (!rts) { 693 return 1; 694 } 695 696 return rts->ClampedHardwareConcurrency( 697 nsGlobalWindowInner::Cast(mWindow)->ShouldResistFingerprinting( 698 RFPTarget::NavigatorHWConcurrency), 699 nsGlobalWindowInner::Cast(mWindow)->ShouldResistFingerprinting( 700 RFPTarget::NavigatorHWConcurrencyTiered)); 701 } 702 703 namespace { 704 705 class VibrateWindowListener : public nsIDOMEventListener { 706 public: 707 VibrateWindowListener(nsPIDOMWindowInner* aWindow, Document* aDocument) 708 : mWindow(do_GetWeakReference(aWindow)), mDocument(aDocument) { 709 constexpr auto visibilitychange = u"visibilitychange"_ns; 710 aDocument->AddSystemEventListener(visibilitychange, this, /* listener */ 711 true, /* use capture */ 712 false /* wants untrusted */); 713 } 714 715 void RemoveListener(); 716 717 NS_DECL_ISUPPORTS 718 NS_DECL_NSIDOMEVENTLISTENER 719 720 private: 721 virtual ~VibrateWindowListener() = default; 722 723 nsWeakPtr mWindow; 724 WeakPtr<Document> mDocument; 725 }; 726 727 NS_IMPL_ISUPPORTS(VibrateWindowListener, nsIDOMEventListener) 728 729 StaticRefPtr<VibrateWindowListener> gVibrateWindowListener; 730 731 static bool MayVibrate(Document* doc) { 732 // Hidden documents cannot start or stop a vibration. 733 return (doc && !doc->Hidden()); 734 } 735 736 NS_IMETHODIMP 737 VibrateWindowListener::HandleEvent(Event* aEvent) { 738 nsCOMPtr<Document> doc = do_QueryInterface(aEvent->GetTarget()); 739 740 if (!MayVibrate(doc)) { 741 // It's important that we call CancelVibrate(), not Vibrate() with an 742 // empty list, because Vibrate() will fail if we're no longer focused, but 743 // CancelVibrate() will succeed, so long as nobody else has started a new 744 // vibration pattern. 745 nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mWindow); 746 hal::CancelVibrate(window); 747 RemoveListener(); 748 gVibrateWindowListener = nullptr; 749 // Careful: The line above might have deleted |this|! 750 } 751 752 return NS_OK; 753 } 754 755 void VibrateWindowListener::RemoveListener() { 756 nsCOMPtr<Document> target(mDocument); 757 if (!target) { 758 return; 759 } 760 constexpr auto visibilitychange = u"visibilitychange"_ns; 761 target->RemoveSystemEventListener(visibilitychange, this, 762 true /* use capture */); 763 } 764 765 } // namespace 766 767 void Navigator::SetVibrationPermission(bool aPermitted, bool aPersistent) { 768 MOZ_ASSERT(NS_IsMainThread()); 769 770 nsTArray<uint32_t> pattern = std::move(mRequestedVibrationPattern); 771 772 if (!mWindow) { 773 return; 774 } 775 776 nsCOMPtr<Document> doc = mWindow->GetExtantDoc(); 777 778 if (!MayVibrate(doc)) { 779 return; 780 } 781 782 if (aPermitted) { 783 // Add a listener to cancel the vibration if the document becomes hidden, 784 // and remove the old visibility listener, if there was one. 785 if (!gVibrateWindowListener) { 786 // If gVibrateWindowListener is null, this is the first time we've 787 // vibrated, and we need to register a listener to clear 788 // gVibrateWindowListener on shutdown. 789 ClearOnShutdown(&gVibrateWindowListener); 790 } else { 791 gVibrateWindowListener->RemoveListener(); 792 } 793 gVibrateWindowListener = new VibrateWindowListener(mWindow, doc); 794 hal::Vibrate(pattern, mWindow); 795 } 796 797 if (aPersistent) { 798 nsCOMPtr<nsIPermissionManager> permMgr = 799 components::PermissionManager::Service(); 800 if (!permMgr) { 801 return; 802 } 803 permMgr->AddFromPrincipal(doc->NodePrincipal(), kVibrationPermissionType, 804 aPermitted ? nsIPermissionManager::ALLOW_ACTION 805 : nsIPermissionManager::DENY_ACTION, 806 nsIPermissionManager::EXPIRE_SESSION, 0); 807 } 808 } 809 810 bool Navigator::Vibrate(uint32_t aDuration) { 811 AutoTArray<uint32_t, 1> pattern; 812 pattern.AppendElement(aDuration); 813 return Vibrate(pattern); 814 } 815 816 nsTArray<uint32_t> SanitizeVibratePattern(const nsTArray<uint32_t>& aPattern) { 817 nsTArray<uint32_t> pattern(aPattern.Clone()); 818 819 if (pattern.Length() > StaticPrefs::dom_vibrator_max_vibrate_list_len()) { 820 pattern.SetLength(StaticPrefs::dom_vibrator_max_vibrate_list_len()); 821 } 822 823 for (size_t i = 0; i < pattern.Length(); ++i) { 824 pattern[i] = 825 std::min(StaticPrefs::dom_vibrator_max_vibrate_ms(), pattern[i]); 826 } 827 828 return pattern; 829 } 830 831 bool Navigator::Vibrate(const nsTArray<uint32_t>& aPattern) { 832 MOZ_ASSERT(NS_IsMainThread()); 833 834 if (!mWindow) { 835 return false; 836 } 837 838 nsCOMPtr<Document> doc = mWindow->GetExtantDoc(); 839 840 if (!MayVibrate(doc)) { 841 return false; 842 } 843 844 nsTArray<uint32_t> pattern = SanitizeVibratePattern(aPattern); 845 846 mRequestedVibrationPattern = std::move(pattern); 847 848 PermissionDelegateHandler* permissionHandler = 849 doc->GetPermissionDelegateHandler(); 850 if (NS_WARN_IF(!permissionHandler)) { 851 return false; 852 } 853 854 uint32_t permission = nsIPermissionManager::UNKNOWN_ACTION; 855 856 permissionHandler->GetPermission(kVibrationPermissionType, &permission, 857 false); 858 859 if (permission == nsIPermissionManager::DENY_ACTION) { 860 // Abort without observer service or on denied session permission. 861 SetVibrationPermission(false /* permitted */, false /* persistent */); 862 return false; 863 } 864 865 if (permission == nsIPermissionManager::ALLOW_ACTION || 866 mRequestedVibrationPattern.IsEmpty() || 867 (mRequestedVibrationPattern.Length() == 1 && 868 mRequestedVibrationPattern[0] == 0)) { 869 // Always allow cancelling vibration and respect session permissions. 870 SetVibrationPermission(true /* permitted */, false /* persistent */); 871 return true; 872 } 873 874 // Request user permission. 875 nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); 876 if (!obs) { 877 return true; 878 } 879 880 obs->NotifyObservers(ToSupports(this), "Vibration:Request", nullptr); 881 882 return true; 883 } 884 885 //***************************************************************************** 886 // Pointer Events interface 887 //***************************************************************************** 888 889 uint32_t Navigator::MaxTouchPoints(CallerType aCallerType) { 890 nsIDocShell* docshell = GetDocShell(); 891 BrowsingContext* bc = docshell ? docshell->GetBrowsingContext() : nullptr; 892 893 // Responsive Design Mode overrides the maxTouchPoints property when 894 // touch simulation is enabled. 895 if (bc && bc->Top()->InRDMPane()) { 896 return bc->Top()->GetMaxTouchPointsOverride(); 897 } 898 899 // The maxTouchPoints is going to reveal the detail of users' hardware. So, 900 // we will spoof it into 0 if fingerprinting resistance is on. 901 if (aCallerType != CallerType::System && 902 nsContentUtils::ShouldResistFingerprinting(GetDocShell(), 903 RFPTarget::MaxTouchPoints)) { 904 return SPOOFED_MAX_TOUCH_POINTS; 905 } 906 907 nsCOMPtr<nsIWidget> widget = 908 widget::WidgetUtils::DOMWindowToWidget(mWindow->GetOuterWindow()); 909 910 NS_ENSURE_TRUE(widget, 0); 911 uint32_t maxTouchPoints = widget->GetMaxTouchPoints(); 912 913 if (aCallerType != CallerType::System && 914 nsContentUtils::ShouldResistFingerprinting( 915 GetDocShell(), RFPTarget::MaxTouchPointsCollapse)) { 916 return nsRFPService::CollapseMaxTouchPoints(maxTouchPoints); 917 } 918 return maxTouchPoints; 919 } 920 921 //***************************************************************************** 922 // Navigator::nsIDOMClientInformation 923 //***************************************************************************** 924 925 // This list should be kept up-to-date with the spec: 926 // https://html.spec.whatwg.org/multipage/system-state.html#custom-handlers 927 // If you change this list, please also update the copy in E10SUtils.sys.mjs. 928 static const char* const kSafeSchemes[] = { 929 // clang-format off 930 "bitcoin", 931 "ftp", 932 "ftps", 933 "geo", 934 "im", 935 "irc", 936 "ircs", 937 "magnet", 938 "mailto", 939 "matrix", 940 "mms", 941 "news", 942 "nntp", 943 "openpgp4fpr", 944 "sftp", 945 "sip", 946 "sms", 947 "smsto", 948 "ssh", 949 "tel", 950 "urn", 951 "webcal", 952 "wtai", 953 "xmpp", 954 // clang-format on 955 }; 956 957 void Navigator::CheckProtocolHandlerAllowed(const nsAString& aScheme, 958 nsIURI* aHandlerURI, 959 nsIURI* aDocumentURI, 960 ErrorResult& aRv) { 961 auto raisePermissionDeniedHandler = [&] { 962 nsAutoCString spec; 963 aHandlerURI->GetSpec(spec); 964 nsPrintfCString message("Permission denied to add %s as a protocol handler", 965 spec.get()); 966 aRv.ThrowSecurityError(message); 967 }; 968 969 auto raisePermissionDeniedScheme = [&] { 970 nsPrintfCString message( 971 "Permission denied to add a protocol handler for %s", 972 NS_ConvertUTF16toUTF8(aScheme).get()); 973 aRv.ThrowSecurityError(message); 974 }; 975 976 if (!aDocumentURI || !aHandlerURI) { 977 aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); 978 return; 979 } 980 981 nsCString spec; 982 aHandlerURI->GetSpec(spec); 983 // If the uri doesn't contain '%s', it won't be a good handler - the %s 984 // gets replaced with the handled URI. 985 if (!FindInReadable("%s"_ns, spec)) { 986 aRv.ThrowSyntaxError("Handler URI does not contain \"%s\"."); 987 return; 988 } 989 990 // For security reasons we reject non-http(s) urls (see bug 354316), 991 nsAutoCString docScheme; 992 nsAutoCString handlerScheme; 993 aDocumentURI->GetScheme(docScheme); 994 aHandlerURI->GetScheme(handlerScheme); 995 if ((!docScheme.EqualsLiteral("https") && !docScheme.EqualsLiteral("http")) || 996 (!handlerScheme.EqualsLiteral("https") && 997 !handlerScheme.EqualsLiteral("http"))) { 998 raisePermissionDeniedHandler(); 999 return; 1000 } 1001 1002 // Should be same-origin: 1003 nsAutoCString handlerHost; 1004 aHandlerURI->GetHostPort(handlerHost); 1005 nsAutoCString documentHost; 1006 aDocumentURI->GetHostPort(documentHost); 1007 if (!handlerHost.Equals(documentHost) || !handlerScheme.Equals(docScheme)) { 1008 raisePermissionDeniedHandler(); 1009 return; 1010 } 1011 1012 // Having checked the handler URI, check the scheme: 1013 nsAutoCString scheme; 1014 ToLowerCase(NS_ConvertUTF16toUTF8(aScheme), scheme); 1015 if (StringBeginsWith(scheme, "web+"_ns)) { 1016 // Check for non-ascii 1017 nsReadingIterator<char> iter; 1018 nsReadingIterator<char> iterEnd; 1019 auto remainingScheme = Substring(scheme, 4 /* web+ */); 1020 remainingScheme.BeginReading(iter); 1021 remainingScheme.EndReading(iterEnd); 1022 // Scheme suffix must be non-empty 1023 if (iter == iterEnd) { 1024 raisePermissionDeniedScheme(); 1025 return; 1026 } 1027 for (; iter != iterEnd; iter++) { 1028 if (*iter < 'a' || *iter > 'z') { 1029 raisePermissionDeniedScheme(); 1030 return; 1031 } 1032 } 1033 } else { 1034 bool matches = false; 1035 for (const char* safeScheme : kSafeSchemes) { 1036 if (scheme.Equals(safeScheme)) { 1037 matches = true; 1038 break; 1039 } 1040 } 1041 if (!matches) { 1042 raisePermissionDeniedScheme(); 1043 return; 1044 } 1045 } 1046 1047 nsCOMPtr<nsIProtocolHandler> handler; 1048 nsCOMPtr<nsIIOService> io = components::IO::Service(); 1049 if (NS_FAILED( 1050 io->GetProtocolHandler(scheme.get(), getter_AddRefs(handler)))) { 1051 raisePermissionDeniedScheme(); 1052 return; 1053 } 1054 1055 // check if we have prefs set saying not to add this. 1056 bool defaultExternal = 1057 Preferences::GetBool("network.protocol-handler.external-default"); 1058 nsPrintfCString specificPref("network.protocol-handler.external.%s", 1059 scheme.get()); 1060 if (!Preferences::GetBool(specificPref.get(), defaultExternal)) { 1061 raisePermissionDeniedScheme(); 1062 return; 1063 } 1064 1065 // Check to make sure this isn't already handled internally (we don't 1066 // want to let them take over, say "chrome"). In theory, the checks above 1067 // should have already taken care of this. 1068 nsCOMPtr<nsIExternalProtocolHandler> externalHandler = 1069 do_QueryInterface(handler); 1070 MOZ_RELEASE_ASSERT( 1071 externalHandler, 1072 "We should never allow overriding a builtin protocol handler"); 1073 } 1074 1075 void Navigator::RegisterProtocolHandler(const nsAString& aScheme, 1076 const nsAString& aURI, 1077 ErrorResult& aRv) { 1078 if (!mWindow || !mWindow->GetOuterWindow() || !mWindow->GetDocShell() || 1079 !mWindow->GetDoc()) { 1080 return; 1081 } 1082 nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(mWindow); 1083 if (loadContext->UsePrivateBrowsing()) { 1084 // If we're a private window, don't alert the user or webpage. We log to the 1085 // console so that web developers have some way to tell what's going wrong. 1086 nsContentUtils::ReportToConsole( 1087 nsIScriptError::warningFlag, "DOM"_ns, mWindow->GetDoc(), 1088 nsContentUtils::eDOM_PROPERTIES, 1089 "RegisterProtocolHandlerPrivateBrowsingWarning"); 1090 return; 1091 } 1092 1093 nsCOMPtr<Document> doc = mWindow->GetDoc(); 1094 1095 // Determine if doc is allowed to assign this handler 1096 nsIURI* docURI = doc->GetDocumentURIObject(); 1097 nsCOMPtr<nsIURI> handlerURI; 1098 NS_NewURI(getter_AddRefs(handlerURI), NS_ConvertUTF16toUTF8(aURI), 1099 doc->GetDocumentCharacterSet(), docURI); 1100 CheckProtocolHandlerAllowed(aScheme, handlerURI, docURI, aRv); 1101 if (aRv.Failed()) { 1102 return; 1103 } 1104 1105 // Determine a title from the document URI. 1106 nsAutoCString docDisplayHostPort; 1107 docURI->GetDisplayHostPort(docDisplayHostPort); 1108 NS_ConvertASCIItoUTF16 title(docDisplayHostPort); 1109 1110 if (XRE_IsContentProcess()) { 1111 nsAutoString scheme(aScheme); 1112 RefPtr<BrowserChild> browserChild = BrowserChild::GetFrom(mWindow); 1113 browserChild->SendRegisterProtocolHandler(scheme, handlerURI, title, 1114 docURI); 1115 return; 1116 } 1117 1118 nsCOMPtr<nsIWebProtocolHandlerRegistrar> registrar = 1119 do_GetService(NS_WEBPROTOCOLHANDLERREGISTRAR_CONTRACTID); 1120 if (registrar) { 1121 aRv = registrar->RegisterProtocolHandler(aScheme, handlerURI, title, docURI, 1122 mWindow->GetOuterWindow()); 1123 } 1124 } 1125 1126 Geolocation* Navigator::GetGeolocation(ErrorResult& aRv) { 1127 if (mGeolocation) { 1128 return mGeolocation; 1129 } 1130 1131 if (!mWindow || !mWindow->GetOuterWindow() || !mWindow->GetDocShell()) { 1132 aRv.Throw(NS_ERROR_FAILURE); 1133 return nullptr; 1134 } 1135 1136 mGeolocation = new Geolocation(); 1137 if (NS_FAILED(mGeolocation->Init(mWindow))) { 1138 mGeolocation = nullptr; 1139 aRv.Throw(NS_ERROR_FAILURE); 1140 return nullptr; 1141 } 1142 1143 return mGeolocation; 1144 } 1145 1146 class BeaconStreamListener final : public nsIStreamListener { 1147 ~BeaconStreamListener() = default; 1148 1149 public: 1150 BeaconStreamListener() : mLoadGroup(nullptr) {} 1151 1152 void SetLoadGroup(nsILoadGroup* aLoadGroup) { mLoadGroup = aLoadGroup; } 1153 1154 NS_DECL_ISUPPORTS 1155 NS_DECL_NSISTREAMLISTENER 1156 NS_DECL_NSIREQUESTOBSERVER 1157 1158 private: 1159 nsCOMPtr<nsILoadGroup> mLoadGroup; 1160 }; 1161 1162 NS_IMPL_ISUPPORTS(BeaconStreamListener, nsIStreamListener, nsIRequestObserver) 1163 1164 NS_IMETHODIMP 1165 BeaconStreamListener::OnStartRequest(nsIRequest* aRequest) { 1166 // release the loadgroup first 1167 mLoadGroup = nullptr; 1168 1169 return NS_ERROR_ABORT; 1170 } 1171 1172 NS_IMETHODIMP 1173 BeaconStreamListener::OnStopRequest(nsIRequest* aRequest, nsresult aStatus) { 1174 return NS_OK; 1175 } 1176 1177 NS_IMETHODIMP 1178 BeaconStreamListener::OnDataAvailable(nsIRequest* aRequest, 1179 nsIInputStream* inStr, 1180 uint64_t sourceOffset, uint32_t count) { 1181 MOZ_ASSERT(false); 1182 return NS_OK; 1183 } 1184 1185 bool Navigator::SendBeacon(const nsAString& aUrl, 1186 const Nullable<fetch::BodyInit>& aData, 1187 ErrorResult& aRv) { 1188 if (aData.IsNull()) { 1189 return SendBeaconInternal(aUrl, nullptr, eBeaconTypeOther, aRv); 1190 } 1191 1192 if (aData.Value().IsArrayBuffer()) { 1193 BodyExtractor<const ArrayBuffer> body(&aData.Value().GetAsArrayBuffer()); 1194 return SendBeaconInternal(aUrl, &body, eBeaconTypeArrayBuffer, aRv); 1195 } 1196 1197 if (aData.Value().IsArrayBufferView()) { 1198 BodyExtractor<const ArrayBufferView> body( 1199 &aData.Value().GetAsArrayBufferView()); 1200 return SendBeaconInternal(aUrl, &body, eBeaconTypeArrayBuffer, aRv); 1201 } 1202 1203 if (aData.Value().IsBlob()) { 1204 BodyExtractor<const Blob> body(&aData.Value().GetAsBlob()); 1205 return SendBeaconInternal(aUrl, &body, eBeaconTypeBlob, aRv); 1206 } 1207 1208 if (aData.Value().IsFormData()) { 1209 BodyExtractor<const FormData> body(&aData.Value().GetAsFormData()); 1210 return SendBeaconInternal(aUrl, &body, eBeaconTypeOther, aRv); 1211 } 1212 1213 if (aData.Value().IsUSVString()) { 1214 BodyExtractor<const nsAString> body(&aData.Value().GetAsUSVString()); 1215 return SendBeaconInternal(aUrl, &body, eBeaconTypeOther, aRv); 1216 } 1217 1218 if (aData.Value().IsURLSearchParams()) { 1219 BodyExtractor<const URLSearchParams> body( 1220 &aData.Value().GetAsURLSearchParams()); 1221 return SendBeaconInternal(aUrl, &body, eBeaconTypeOther, aRv); 1222 } 1223 1224 MOZ_CRASH("Invalid data type."); 1225 return false; 1226 } 1227 1228 bool Navigator::SendBeaconInternal(const nsAString& aUrl, 1229 BodyExtractorBase* aBody, BeaconType aType, 1230 ErrorResult& aRv) { 1231 if (!mWindow) { 1232 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 1233 return false; 1234 } 1235 1236 nsCOMPtr<Document> doc = mWindow->GetDoc(); 1237 if (!doc) { 1238 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 1239 return false; 1240 } 1241 1242 nsIURI* documentURI = doc->GetDocumentURI(); 1243 if (!documentURI) { 1244 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 1245 return false; 1246 } 1247 1248 nsCOMPtr<nsIURI> uri; 1249 nsresult rv = nsContentUtils::NewURIWithDocumentCharset( 1250 getter_AddRefs(uri), aUrl, doc, doc->GetDocBaseURI()); 1251 if (NS_FAILED(rv)) { 1252 aRv.ThrowTypeError<MSG_INVALID_URL>(NS_ConvertUTF16toUTF8(aUrl)); 1253 return false; 1254 } 1255 1256 // Spec disallows any schemes save for HTTP/HTTPs 1257 if (!net::SchemeIsHttpOrHttps(uri)) { 1258 aRv.ThrowTypeError<MSG_INVALID_URL_SCHEME>("Beacon", 1259 uri->GetSpecOrDefault()); 1260 return false; 1261 } 1262 1263 nsCOMPtr<nsIInputStream> in; 1264 nsAutoCString contentTypeWithCharset; 1265 nsAutoCString charset; 1266 uint64_t length = 0; 1267 if (aBody) { 1268 aRv = aBody->GetAsStream(getter_AddRefs(in), &length, 1269 contentTypeWithCharset, charset); 1270 if (NS_WARN_IF(aRv.Failed())) { 1271 return false; 1272 } 1273 } 1274 1275 nsSecurityFlags securityFlags = nsILoadInfo::SEC_COOKIES_INCLUDE; 1276 // Ensure that only streams with content types that are safelisted ignore CORS 1277 // rules 1278 if (aBody && !contentTypeWithCharset.IsVoid() && 1279 !nsContentUtils::IsCORSSafelistedRequestHeader("content-type"_ns, 1280 contentTypeWithCharset)) { 1281 securityFlags |= nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT; 1282 } else { 1283 securityFlags |= nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT; 1284 } 1285 1286 nsCOMPtr<nsIChannel> channel; 1287 rv = NS_NewChannel(getter_AddRefs(channel), uri, doc, securityFlags, 1288 nsIContentPolicy::TYPE_BEACON); 1289 1290 if (NS_FAILED(rv)) { 1291 aRv.Throw(rv); 1292 return false; 1293 } 1294 1295 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel); 1296 if (!httpChannel) { 1297 // Beacon spec only supports HTTP requests at this time 1298 aRv.Throw(NS_ERROR_DOM_BAD_URI); 1299 return false; 1300 } 1301 1302 auto referrerInfo = MakeRefPtr<ReferrerInfo>(*doc); 1303 rv = httpChannel->SetReferrerInfoWithoutClone(referrerInfo); 1304 MOZ_ASSERT(NS_SUCCEEDED(rv)); 1305 1306 if (aBody) { 1307 nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(channel); 1308 if (!uploadChannel) { 1309 aRv.Throw(NS_ERROR_FAILURE); 1310 return false; 1311 } 1312 1313 uploadChannel->ExplicitSetUploadStream(in, contentTypeWithCharset, length, 1314 "POST"_ns, false); 1315 } else { 1316 rv = httpChannel->SetRequestMethod("POST"_ns); 1317 MOZ_ASSERT(NS_SUCCEEDED(rv)); 1318 } 1319 1320 nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(channel); 1321 if (p) { 1322 p->SetPriority(nsISupportsPriority::PRIORITY_LOWEST); 1323 } 1324 1325 nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel)); 1326 if (cos) { 1327 cos->AddClassFlags(nsIClassOfService::Background); 1328 } 1329 1330 // The channel needs to have a loadgroup associated with it, so that we can 1331 // cancel the channel and any redirected channels it may create. 1332 nsCOMPtr<nsILoadGroup> loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID); 1333 nsCOMPtr<nsIInterfaceRequestor> callbacks = 1334 do_QueryInterface(mWindow->GetDocShell()); 1335 loadGroup->SetNotificationCallbacks(callbacks); 1336 channel->SetLoadGroup(loadGroup); 1337 1338 RefPtr<BeaconStreamListener> beaconListener = new BeaconStreamListener(); 1339 rv = channel->AsyncOpen(beaconListener); 1340 // do not throw if security checks fail within asyncOpen 1341 NS_ENSURE_SUCCESS(rv, false); 1342 1343 // make the beaconListener hold a strong reference to the loadgroup 1344 // which is released in ::OnStartRequest 1345 beaconListener->SetLoadGroup(loadGroup); 1346 1347 return true; 1348 } 1349 1350 MediaDevices* Navigator::GetMediaDevices(ErrorResult& aRv) { 1351 if (!mMediaDevices) { 1352 if (!mWindow || !mWindow->GetOuterWindow() || 1353 mWindow->GetOuterWindow()->GetCurrentInnerWindow() != mWindow) { 1354 aRv.Throw(NS_ERROR_NOT_AVAILABLE); 1355 return nullptr; 1356 } 1357 mMediaDevices = new MediaDevices(mWindow); 1358 } 1359 return mMediaDevices; 1360 } 1361 1362 void Navigator::MozGetUserMedia(const MediaStreamConstraints& aConstraints, 1363 NavigatorUserMediaSuccessCallback& aOnSuccess, 1364 NavigatorUserMediaErrorCallback& aOnError, 1365 CallerType aCallerType, ErrorResult& aRv) { 1366 MOZ_ASSERT(NS_IsMainThread()); 1367 if (!mWindow || !mWindow->IsFullyActive()) { 1368 aRv.ThrowInvalidStateError("The document is not fully active."); 1369 return; 1370 } 1371 GetMediaDevices(aRv); 1372 if (aRv.Failed()) { 1373 return; 1374 } 1375 MOZ_ASSERT(mMediaDevices); 1376 RefPtr<MediaManager::StreamPromise> sp; 1377 if (!MediaManager::IsOn(aConstraints.mVideo) && 1378 !MediaManager::IsOn(aConstraints.mAudio)) { 1379 sp = MediaManager::StreamPromise::CreateAndReject( 1380 MakeRefPtr<MediaMgrError>(MediaMgrError::Name::TypeError, 1381 "audio and/or video is required"), 1382 __func__); 1383 } else { 1384 sp = mMediaDevices->GetUserMedia(mWindow, aConstraints, aCallerType); 1385 } 1386 RefPtr<NavigatorUserMediaSuccessCallback> onsuccess(&aOnSuccess); 1387 RefPtr<NavigatorUserMediaErrorCallback> onerror(&aOnError); 1388 1389 nsWeakPtr weakWindow = nsWeakPtr(do_GetWeakReference(mWindow)); 1390 sp->Then( 1391 GetMainThreadSerialEventTarget(), __func__, 1392 [weakWindow, onsuccess = std::move(onsuccess)]( 1393 const RefPtr<DOMMediaStream>& aStream) MOZ_CAN_RUN_SCRIPT { 1394 nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(weakWindow); 1395 if (!window || !window->GetOuterWindow() || 1396 window->GetOuterWindow()->GetCurrentInnerWindow() != window) { 1397 return; // Leave Promise pending after navigation by design. 1398 } 1399 MediaManager::CallOnSuccess(*onsuccess, *aStream); 1400 }, 1401 [weakWindow, onerror = std::move(onerror)]( 1402 const RefPtr<MediaMgrError>& aError) MOZ_CAN_RUN_SCRIPT { 1403 nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(weakWindow); 1404 if (!window || !window->GetOuterWindow() || 1405 window->GetOuterWindow()->GetCurrentInnerWindow() != window) { 1406 return; // Leave Promise pending after navigation by design. 1407 } 1408 auto error = MakeRefPtr<MediaStreamError>(window, *aError); 1409 MediaManager::CallOnError(*onerror, *error); 1410 }); 1411 } 1412 1413 //***************************************************************************** 1414 // Navigator::nsINavigatorBattery 1415 //***************************************************************************** 1416 1417 Promise* Navigator::GetBattery(ErrorResult& aRv) { 1418 if (mBatteryPromise) { 1419 return mBatteryPromise; 1420 } 1421 1422 if (!mWindow || !mWindow->GetDocShell()) { 1423 aRv.Throw(NS_ERROR_UNEXPECTED); 1424 return nullptr; 1425 } 1426 1427 RefPtr<Promise> batteryPromise = Promise::Create(mWindow->AsGlobal(), aRv); 1428 if (NS_WARN_IF(aRv.Failed())) { 1429 return nullptr; 1430 } 1431 mBatteryPromise = batteryPromise; 1432 1433 if (!mBatteryManager) { 1434 mBatteryManager = new battery::BatteryManager(mWindow); 1435 mBatteryManager->Init(); 1436 } 1437 1438 mBatteryPromise->MaybeResolve(mBatteryManager); 1439 1440 return mBatteryPromise; 1441 } 1442 1443 //***************************************************************************** 1444 // Navigator::Share() - Web Share API 1445 //***************************************************************************** 1446 1447 already_AddRefed<Promise> Navigator::Share(const ShareData& aData, 1448 ErrorResult& aRv) { 1449 if (!mWindow || !mWindow->IsFullyActive()) { 1450 aRv.ThrowInvalidStateError("The document is not fully active."); 1451 return nullptr; 1452 } 1453 1454 if (NS_WARN_IF(!mWindow->GetDocShell() || !mWindow->GetExtantDoc())) { 1455 aRv.Throw(NS_ERROR_UNEXPECTED); 1456 return nullptr; 1457 } 1458 1459 if (!FeaturePolicyUtils::IsFeatureAllowed(mWindow->GetExtantDoc(), 1460 u"web-share"_ns)) { 1461 aRv.ThrowNotAllowedError( 1462 "Document's Permissions Policy does not allow calling " 1463 "share() from this context."); 1464 return nullptr; 1465 } 1466 1467 if (mSharePromise) { 1468 NS_WARNING("Only one share picker at a time per navigator instance"); 1469 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); 1470 return nullptr; 1471 } 1472 1473 // null checked above 1474 Document* doc = mWindow->GetExtantDoc(); 1475 1476 if (StaticPrefs::dom_webshare_requireinteraction() && 1477 !doc->ConsumeTransientUserGestureActivation()) { 1478 aRv.ThrowNotAllowedError( 1479 "User activation was already consumed " 1480 "or share() was not activated by a user gesture."); 1481 return nullptr; 1482 } 1483 1484 ValidateShareData(aData, aRv); 1485 1486 if (aRv.Failed()) { 1487 return nullptr; 1488 } 1489 1490 // TODO: Process file member, which we don't currently support. 1491 1492 // If data's url member is present, try to resolve it... 1493 nsCOMPtr<nsIURI> url; 1494 if (aData.mUrl.WasPassed()) { 1495 auto result = doc->ResolveWithBaseURI(aData.mUrl.Value()); 1496 url = result.unwrap(); 1497 MOZ_ASSERT(url); 1498 } 1499 1500 // Process the title member... 1501 nsCString title; 1502 if (aData.mTitle.WasPassed()) { 1503 title.Assign(NS_ConvertUTF16toUTF8(aData.mTitle.Value())); 1504 } else { 1505 title.SetIsVoid(true); 1506 } 1507 1508 // Process the text member... 1509 nsCString text; 1510 if (aData.mText.WasPassed()) { 1511 text.Assign(NS_ConvertUTF16toUTF8(aData.mText.Value())); 1512 } else { 1513 text.SetIsVoid(true); 1514 } 1515 1516 // Let mSharePromise be a new promise. 1517 mSharePromise = Promise::Create(mWindow->AsGlobal(), aRv); 1518 if (aRv.Failed()) { 1519 return nullptr; 1520 } 1521 1522 IPCWebShareData data(title, text, url); 1523 auto wgc = mWindow->GetWindowGlobalChild(); 1524 if (!wgc) { 1525 aRv.Throw(NS_ERROR_FAILURE); 1526 return nullptr; 1527 } 1528 1529 // Do the share 1530 wgc->SendShare(data)->Then( 1531 GetCurrentSerialEventTarget(), __func__, 1532 [self = RefPtr{this}]( 1533 PWindowGlobalChild::SharePromise::ResolveOrRejectValue&& aResult) { 1534 if (aResult.IsResolve()) { 1535 if (NS_SUCCEEDED(aResult.ResolveValue())) { 1536 self->mSharePromise->MaybeResolveWithUndefined(); 1537 } else { 1538 self->mSharePromise->MaybeReject(aResult.ResolveValue()); 1539 } 1540 } else if (self->mSharePromise) { 1541 // IPC died 1542 self->mSharePromise->MaybeReject(NS_BINDING_ABORTED); 1543 } 1544 self->mSharePromise = nullptr; 1545 }); 1546 return do_AddRef(mSharePromise); 1547 } 1548 1549 //***************************************************************************** 1550 // Navigator::CanShare() - Web Share API 1551 //***************************************************************************** 1552 bool Navigator::CanShare(const ShareData& aData) { 1553 if (!mWindow || !mWindow->IsFullyActive()) { 1554 return false; 1555 } 1556 1557 if (!FeaturePolicyUtils::IsFeatureAllowed(mWindow->GetExtantDoc(), 1558 u"web-share"_ns)) { 1559 return false; 1560 } 1561 1562 IgnoredErrorResult rv; 1563 ValidateShareData(aData, rv); 1564 return !rv.Failed(); 1565 } 1566 1567 void Navigator::ValidateShareData(const ShareData& aData, ErrorResult& aRv) { 1568 // TODO: remove this check when we support files share. 1569 if (aData.mFiles.WasPassed() && !aData.mFiles.Value().IsEmpty()) { 1570 aRv.ThrowTypeError("Passing files is currently not supported."); 1571 return; 1572 } 1573 1574 bool titleTextOrUrlPassed = aData.mTitle.WasPassed() || 1575 aData.mText.WasPassed() || aData.mUrl.WasPassed(); 1576 1577 // At least one member must be present. 1578 if (!titleTextOrUrlPassed) { 1579 aRv.ThrowTypeError( 1580 "Must have a title, text, or url member in the ShareData dictionary"); 1581 return; 1582 } 1583 1584 // If data's url member is present, try to resolve it... 1585 nsCOMPtr<nsIURI> url; 1586 if (aData.mUrl.WasPassed()) { 1587 Document* doc = mWindow->GetExtantDoc(); 1588 Result<OwningNonNull<nsIURI>, nsresult> result = 1589 doc->ResolveWithBaseURI(aData.mUrl.Value()); 1590 if (NS_WARN_IF(result.isErr())) { 1591 aRv.ThrowTypeError<MSG_INVALID_URL>( 1592 NS_ConvertUTF16toUTF8(aData.mUrl.Value())); 1593 return; 1594 } 1595 url = result.unwrap(); 1596 // Check that we only share loadable URLs (e.g., http/https). 1597 // we also exclude blobs, as it doesn't make sense to share those outside 1598 // the context of the browser. 1599 const uint32_t flags = 1600 nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL | 1601 nsIScriptSecurityManager::DISALLOW_SCRIPT; 1602 if (NS_FAILED( 1603 nsContentUtils::GetSecurityManager()->CheckLoadURIWithPrincipal( 1604 doc->NodePrincipal(), url, flags, doc->InnerWindowID())) || 1605 url->SchemeIs("blob")) { 1606 aRv.ThrowTypeError<MSG_INVALID_URL_SCHEME>("Share", 1607 url->GetSpecOrDefault()); 1608 return; 1609 } 1610 } 1611 } 1612 1613 static bool ShouldResistFingerprinting(const Document* aDoc, 1614 RFPTarget aTarget) { 1615 return aDoc ? aDoc->ShouldResistFingerprinting(aTarget) 1616 : nsContentUtils::ShouldResistFingerprinting("Fallback", aTarget); 1617 } 1618 1619 already_AddRefed<LegacyMozTCPSocket> Navigator::MozTCPSocket() { 1620 RefPtr<LegacyMozTCPSocket> socket = new LegacyMozTCPSocket(GetWindow()); 1621 return socket.forget(); 1622 } 1623 1624 void Navigator::GetGamepads(nsTArray<RefPtr<Gamepad>>& aGamepads, 1625 ErrorResult& aRv) { 1626 if (!mWindow || !mWindow->IsFullyActive()) { 1627 return; 1628 } 1629 NS_ENSURE_TRUE_VOID(mWindow->GetDocShell()); 1630 nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow); 1631 1632 if (!FeaturePolicyUtils::IsFeatureAllowed(win->GetExtantDoc(), 1633 u"gamepad"_ns)) { 1634 aRv.ThrowSecurityError( 1635 "Document's Permission Policy does not allow calling " 1636 "getGamepads() from this context."); 1637 return; 1638 } 1639 1640 win->SetHasGamepadEventListener(true); 1641 win->GetGamepads(aGamepads); 1642 } 1643 1644 GamepadServiceTest* Navigator::RequestGamepadServiceTest(ErrorResult& aRv) { 1645 #ifdef FUZZING 1646 if (!StaticPrefs::fuzzing_enabled()) { 1647 aRv.Throw(NS_ERROR_UNEXPECTED); 1648 return nullptr; 1649 } 1650 #else 1651 if (!xpc::IsInAutomation()) { 1652 aRv.Throw(NS_ERROR_UNEXPECTED); 1653 return nullptr; 1654 } 1655 #endif 1656 1657 if (!mGamepadServiceTest) { 1658 mGamepadServiceTest = GamepadServiceTest::CreateTestService(mWindow); 1659 } 1660 return mGamepadServiceTest; 1661 } 1662 1663 already_AddRefed<Promise> Navigator::RequestAllGamepads(ErrorResult& aRv) { 1664 if (!mWindow || !mWindow->IsFullyActive()) { 1665 aRv.Throw(NS_ERROR_UNEXPECTED); 1666 return nullptr; 1667 } 1668 1669 nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow); 1670 1671 // We need to set the flag to trigger the parent process to start monitoring 1672 // gamepads. Otherwise, we cannot get any gamepad information. 1673 win->SetHasGamepadEventListener(true); 1674 return win->RequestAllGamepads(aRv); 1675 } 1676 1677 already_AddRefed<Promise> Navigator::GetVRDisplays(ErrorResult& aRv) { 1678 if (!mWindow || !mWindow->GetDocShell() || !mWindow->GetExtantDoc()) { 1679 aRv.Throw(NS_ERROR_UNEXPECTED); 1680 return nullptr; 1681 } 1682 1683 if (!FeaturePolicyUtils::IsFeatureAllowed(mWindow->GetExtantDoc(), 1684 u"vr"_ns)) { 1685 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); 1686 return nullptr; 1687 } 1688 1689 RefPtr<Promise> p = Promise::Create(mWindow->AsGlobal(), aRv); 1690 if (aRv.Failed()) { 1691 return nullptr; 1692 } 1693 1694 RefPtr<BrowserChild> browser(BrowserChild::GetFrom(mWindow)); 1695 if (!browser) { 1696 MOZ_ASSERT(XRE_IsParentProcess()); 1697 FinishGetVRDisplays(true, p); 1698 } else { 1699 RefPtr<Navigator> self(this); 1700 int browserID = browser->ChromeOuterWindowID(); 1701 1702 browser->SendIsWindowSupportingWebVR(browserID)->Then( 1703 GetCurrentSerialEventTarget(), __func__, 1704 [self, p](bool isSupported) { 1705 self->FinishGetVRDisplays(isSupported, p); 1706 }, 1707 [p](const mozilla::ipc::ResponseRejectReason) { 1708 p->MaybeRejectWithTypeError("Unable to start display enumeration"); 1709 }); 1710 } 1711 1712 return p.forget(); 1713 } 1714 1715 void Navigator::FinishGetVRDisplays(bool isWebVRSupportedInwindow, Promise* p) { 1716 if (!isWebVRSupportedInwindow) { 1717 // WebVR in this window is not supported, so resolve the promise 1718 // with no displays available 1719 nsTArray<RefPtr<VRDisplay>> vrDisplaysEmpty; 1720 p->MaybeResolve(vrDisplaysEmpty); 1721 return; 1722 } 1723 1724 // Since FinishGetVRDisplays can be called asynchronously after an IPC 1725 // response, it's possible that the Window can be torn down before this 1726 // call. In that case, the Window's cyclic references to VR objects are 1727 // also torn down and should not be recreated via 1728 // NotifyHasXRSession. 1729 nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow); 1730 if (win->IsDying()) { 1731 // The Window has been torn down, so there is no further work that can 1732 // be done. 1733 p->MaybeRejectWithTypeError( 1734 "Unable to return VRDisplays for a closed window."); 1735 return; 1736 } 1737 1738 mVRGetDisplaysPromises.AppendElement(p); 1739 win->RequestXRPermission(); 1740 } 1741 1742 void Navigator::OnXRPermissionRequestAllow() { 1743 // The permission request that results in this callback could have 1744 // been instantiated by WebVR, WebXR, or both. 1745 nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow); 1746 bool usingWebXR = false; 1747 1748 if (mXRSystem) { 1749 usingWebXR = mXRSystem->OnXRPermissionRequestAllow(); 1750 } 1751 1752 bool rejectWebVR = true; 1753 // If WebVR and WebXR both requested permission, only grant it to 1754 // WebXR, which takes priority. 1755 if (!usingWebXR) { 1756 // We pass mWindow's id to RefreshVRDisplays, so NotifyVRDisplaysUpdated 1757 // will be called asynchronously, resolving the promises in 1758 // mVRGetDisplaysPromises. 1759 rejectWebVR = !VRDisplay::RefreshVRDisplays(win->WindowID()); 1760 } 1761 // Even if WebXR took priority, reject requests for WebVR in case they were 1762 // made simultaneously and coelesced into a single permission prompt. 1763 if (rejectWebVR) { 1764 for (auto& p : mVRGetDisplaysPromises) { 1765 // Failed to refresh, reject the promise now 1766 p->MaybeRejectWithTypeError("Failed to find attached VR displays."); 1767 } 1768 mVRGetDisplaysPromises.Clear(); 1769 } 1770 } 1771 1772 void Navigator::OnXRPermissionRequestCancel() { 1773 if (mXRSystem) { 1774 mXRSystem->OnXRPermissionRequestCancel(); 1775 } 1776 1777 nsTArray<RefPtr<VRDisplay>> vrDisplays; 1778 for (auto& p : mVRGetDisplaysPromises) { 1779 // Resolve the promise with no vr displays when 1780 // the user blocks access. 1781 p->MaybeResolve(vrDisplays); 1782 } 1783 mVRGetDisplaysPromises.Clear(); 1784 } 1785 1786 void Navigator::GetActiveVRDisplays( 1787 nsTArray<RefPtr<VRDisplay>>& aDisplays) const { 1788 /** 1789 * Get only the active VR displays. 1790 * GetActiveVRDisplays should only enumerate displays that 1791 * are already active without causing any other hardware to be 1792 * activated. 1793 * We must not call nsGlobalWindowInner::NotifyHasXRSession here, 1794 * as that would cause enumeration and activation of other VR hardware. 1795 * Activating VR hardware is intrusive to the end user, as it may 1796 * involve physically powering on devices that the user did not 1797 * intend to use. 1798 */ 1799 if (!mWindow || !mWindow->GetDocShell()) { 1800 return; 1801 } 1802 nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow); 1803 nsTArray<RefPtr<VRDisplay>> displays; 1804 if (win->UpdateVRDisplays(displays)) { 1805 for (auto display : displays) { 1806 if (display->IsPresenting()) { 1807 aDisplays.AppendElement(display); 1808 } 1809 } 1810 } 1811 } 1812 1813 void Navigator::NotifyVRDisplaysUpdated() { 1814 // Synchronize the VR devices and resolve the promises in 1815 // mVRGetDisplaysPromises 1816 nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow); 1817 1818 nsTArray<RefPtr<VRDisplay>> vrDisplays; 1819 if (win->UpdateVRDisplays(vrDisplays)) { 1820 for (auto p : mVRGetDisplaysPromises) { 1821 p->MaybeResolve(vrDisplays); 1822 } 1823 } else { 1824 for (auto p : mVRGetDisplaysPromises) { 1825 p->MaybeReject(NS_ERROR_FAILURE); 1826 } 1827 } 1828 mVRGetDisplaysPromises.Clear(); 1829 } 1830 1831 void Navigator::NotifyActiveVRDisplaysChanged() { 1832 Navigator_Binding::ClearCachedActiveVRDisplaysValue(this); 1833 } 1834 1835 VRServiceTest* Navigator::RequestVRServiceTest(ErrorResult& aRv) { 1836 if (!xpc::IsInAutomation()) { 1837 aRv.Throw(NS_ERROR_UNEXPECTED); 1838 return nullptr; 1839 } 1840 1841 // Ensure that the Mock VR devices are not released prematurely 1842 nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow); 1843 win->NotifyHasXRSession(); 1844 1845 if (!mVRServiceTest) { 1846 mVRServiceTest = VRServiceTest::CreateTestService(mWindow); 1847 } 1848 return mVRServiceTest; 1849 } 1850 1851 XRSystem* Navigator::GetXr(ErrorResult& aRv) { 1852 if (!mWindow) { 1853 aRv.Throw(NS_ERROR_UNEXPECTED); 1854 return nullptr; 1855 } 1856 if (!mXRSystem) { 1857 mXRSystem = XRSystem::Create(mWindow); 1858 } 1859 return mXRSystem; 1860 } 1861 1862 bool Navigator::IsWebVRContentDetected() const { 1863 nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow); 1864 return win->IsVRContentDetected(); 1865 } 1866 1867 bool Navigator::IsWebVRContentPresenting() const { 1868 nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow); 1869 return win->IsVRContentPresenting(); 1870 } 1871 1872 void Navigator::RequestVRPresentation(VRDisplay& aDisplay) { 1873 nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow); 1874 win->DispatchVRDisplayActivate(aDisplay.DisplayId(), 1875 VRDisplayEventReason::Requested); 1876 } 1877 1878 already_AddRefed<Promise> Navigator::RequestMIDIAccess( 1879 const MIDIOptions& aOptions, ErrorResult& aRv) { 1880 if (!mWindow) { 1881 aRv.Throw(NS_ERROR_UNEXPECTED); 1882 return nullptr; 1883 } 1884 MIDIAccessManager* accessMgr = MIDIAccessManager::Get(); 1885 return accessMgr->RequestMIDIAccess(mWindow, aOptions, aRv); 1886 } 1887 1888 network::Connection* Navigator::GetConnection(ErrorResult& aRv) { 1889 if (!mConnection) { 1890 if (!mWindow) { 1891 aRv.Throw(NS_ERROR_UNEXPECTED); 1892 return nullptr; 1893 } 1894 mConnection = network::Connection::CreateForWindow( 1895 mWindow, nsGlobalWindowInner::Cast(mWindow)->ShouldResistFingerprinting( 1896 RFPTarget::NetworkConnection)); 1897 } 1898 1899 return mConnection; 1900 } 1901 1902 already_AddRefed<ServiceWorkerContainer> Navigator::ServiceWorker() { 1903 MOZ_ASSERT(mWindow); 1904 1905 if (!mServiceWorkerContainer) { 1906 mServiceWorkerContainer = 1907 ServiceWorkerContainer::Create(mWindow->AsGlobal()); 1908 } 1909 1910 RefPtr<ServiceWorkerContainer> ref = mServiceWorkerContainer; 1911 return ref.forget(); 1912 } 1913 1914 already_AddRefed<ServiceWorkerContainer> Navigator::ServiceWorkerJS() { 1915 if (mWindow->AsGlobal()->GetStorageAccess() == 1916 StorageAccess::ePrivateBrowsing) { 1917 SetUseCounter(mWindow->AsGlobal()->GetGlobalJSObject(), 1918 eUseCounter_custom_PrivateBrowsingNavigatorServiceWorker); 1919 } 1920 1921 return ServiceWorker(); 1922 } 1923 1924 size_t Navigator::SizeOfIncludingThis( 1925 mozilla::MallocSizeOf aMallocSizeOf) const { 1926 size_t n = aMallocSizeOf(this); 1927 1928 // TODO: add SizeOfIncludingThis() to nsMimeTypeArray, bug 674113. 1929 // TODO: add SizeOfIncludingThis() to nsPluginArray, bug 674114. 1930 // TODO: add SizeOfIncludingThis() to Geolocation, bug 674115. 1931 // TODO: add SizeOfIncludingThis() to DesktopNotificationCenter, bug 674116. 1932 1933 return n; 1934 } 1935 1936 void Navigator::OnNavigation() { 1937 if (!mWindow) { 1938 return; 1939 } 1940 1941 // If MediaManager is open let it inform any live streams or pending callbacks 1942 MediaManager* manager = MediaManager::GetIfExists(); 1943 if (manager) { 1944 manager->OnNavigation(mWindow->WindowID()); 1945 } 1946 } 1947 1948 JSObject* Navigator::WrapObject(JSContext* cx, 1949 JS::Handle<JSObject*> aGivenProto) { 1950 return Navigator_Binding::Wrap(cx, this, aGivenProto); 1951 } 1952 1953 /* static */ 1954 bool Navigator::HasUserMediaSupport(JSContext* cx, JSObject* obj) { 1955 // Make enabling peerconnection enable getUserMedia() as well. 1956 // Emulate [SecureContext] unless media.devices.insecure.enabled=true 1957 return (StaticPrefs::media_navigator_enabled() || 1958 StaticPrefs::media_peerconnection_enabled()) && 1959 (IsSecureContextOrObjectIsFromSecureContext(cx, obj) || 1960 StaticPrefs::media_devices_insecure_enabled()); 1961 } 1962 1963 /* static */ 1964 bool Navigator::MozGetUserMediaSupport(JSContext* aCx, JSObject* aObj) { 1965 return StaticPrefs::media_navigator_mozgetusermedia_enabled() && 1966 Navigator::HasUserMediaSupport(aCx, aObj); 1967 } 1968 1969 /* static */ 1970 bool Navigator::HasShareSupport(JSContext* cx, JSObject* obj) { 1971 if (!StaticPrefs::dom_webshare_enabled()) { 1972 return false; 1973 } 1974 #if defined(XP_WIN) && !defined(__MINGW32__) 1975 // The first public build that supports ShareCanceled API 1976 return IsWindows10BuildOrLater(18956); 1977 #else 1978 return true; 1979 #endif 1980 } 1981 1982 /* static */ 1983 bool Navigator::HasMidiSupport(JSContext* cx, JSObject* obj) { 1984 nsIPrincipal* principal = nsContentUtils::SubjectPrincipal(cx); 1985 1986 // Enable on secure contexts but exclude file schemes. 1987 return StaticPrefs::dom_webmidi_enabled() && 1988 IsSecureContextOrObjectIsFromSecureContext(cx, obj) && 1989 !principal->SchemeIs("file"); 1990 } 1991 1992 /* static */ 1993 already_AddRefed<nsPIDOMWindowInner> Navigator::GetWindowFromGlobal( 1994 JSObject* aGlobal) { 1995 nsCOMPtr<nsPIDOMWindowInner> win = xpc::WindowOrNull(aGlobal); 1996 return win.forget(); 1997 } 1998 1999 void Navigator::ClearPlatformCache() { 2000 Navigator_Binding::ClearCachedPlatformValue(this); 2001 } 2002 2003 void Navigator::ClearLanguageCache() { 2004 Navigator_Binding::ClearCachedLanguageValue(this); 2005 Navigator_Binding::ClearCachedLanguagesValue(this); 2006 } 2007 2008 nsresult Navigator::GetPlatform(nsAString& aPlatform, Document* aCallerDoc, 2009 bool aUsePrefOverriddenValue) { 2010 MOZ_ASSERT(NS_IsMainThread()); 2011 2012 // navigator.platform is the same for default and spoofed values. The 2013 // "general.platform.override" pref should override the default platform, 2014 // but the spoofed platform should override the pref. 2015 if (aUsePrefOverriddenValue && 2016 !ShouldResistFingerprinting(aCallerDoc, RFPTarget::NavigatorPlatform)) { 2017 nsAutoString override; 2018 nsresult rv = 2019 mozilla::Preferences::GetString("general.platform.override", override); 2020 2021 if (NS_SUCCEEDED(rv)) { 2022 aPlatform = override; 2023 return NS_OK; 2024 } 2025 } 2026 2027 #if defined(WIN32) 2028 aPlatform.AssignLiteral("Win32"); 2029 #elif defined(XP_MACOSX) 2030 // Always return "MacIntel", even on ARM64 macOS like Safari does. 2031 aPlatform.AssignLiteral("MacIntel"); 2032 #elif defined(ANDROID) 2033 aPlatform.AssignLiteral("Linux armv81"); 2034 #else 2035 aPlatform.AssignLiteral("Linux x86_64"); 2036 #endif 2037 2038 return NS_OK; 2039 } 2040 2041 /* static */ 2042 nsresult Navigator::GetAppVersion(nsAString& aAppVersion, Document* aCallerDoc, 2043 bool aUsePrefOverriddenValue) { 2044 MOZ_ASSERT(NS_IsMainThread()); 2045 2046 if (aUsePrefOverriddenValue) { 2047 // If fingerprinting resistance is on, we will spoof this value. See 2048 // nsRFPService.h for details about spoofed values. 2049 if (ShouldResistFingerprinting(aCallerDoc, 2050 RFPTarget::NavigatorAppVersion)) { 2051 aAppVersion.AssignLiteral(SPOOFED_APPVERSION); 2052 return NS_OK; 2053 } 2054 nsAutoString override; 2055 nsresult rv = mozilla::Preferences::GetString("general.appversion.override", 2056 override); 2057 2058 if (NS_SUCCEEDED(rv)) { 2059 aAppVersion = override; 2060 return NS_OK; 2061 } 2062 } 2063 2064 nsresult rv; 2065 2066 nsCOMPtr<nsIHttpProtocolHandler> service( 2067 do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv)); 2068 NS_ENSURE_SUCCESS(rv, rv); 2069 2070 nsAutoCString str; 2071 rv = service->GetAppVersion(str); 2072 CopyASCIItoUTF16(str, aAppVersion); 2073 NS_ENSURE_SUCCESS(rv, rv); 2074 2075 aAppVersion.AppendLiteral(" ("); 2076 2077 rv = service->GetPlatform(str); 2078 NS_ENSURE_SUCCESS(rv, rv); 2079 2080 AppendASCIItoUTF16(str, aAppVersion); 2081 aAppVersion.Append(char16_t(')')); 2082 2083 return rv; 2084 } 2085 2086 void Navigator::ClearUserAgentCache() { 2087 Navigator_Binding::ClearCachedUserAgentValue(this); 2088 } 2089 2090 nsresult Navigator::GetUserAgent(nsPIDOMWindowInner* aWindow, 2091 Document* aCallerDoc, 2092 Maybe<bool> aShouldResistFingerprinting, 2093 nsAString& aUserAgent) { 2094 MOZ_ASSERT(NS_IsMainThread()); 2095 2096 /* 2097 ResistFingerprinting is migrating to fine-grained control based off 2098 either a channel or Principal+OriginAttributes 2099 2100 This function can be called from Workers, Main Thread, and at least one 2101 other (unusual) case. 2102 2103 For Main Thread, we will generally have a window and an associated 2104 Document, for Workers we will not. 2105 2106 If aShouldResistFingerprinting is provided, we should respect it. 2107 If it is not provided, we will use aCallerDoc to determine our behavior. 2108 */ 2109 2110 bool shouldResistFingerprinting = 2111 aShouldResistFingerprinting.isSome() 2112 ? aShouldResistFingerprinting.value() 2113 : ShouldResistFingerprinting(aCallerDoc, 2114 RFPTarget::NavigatorUserAgent); 2115 2116 // We will skip the override and pass to httpHandler to get spoofed userAgent 2117 // when 'privacy.resistFingerprinting' is true. 2118 if (!shouldResistFingerprinting) { 2119 nsAutoString override; 2120 nsresult rv = 2121 mozilla::Preferences::GetString("general.useragent.override", override); 2122 2123 if (NS_SUCCEEDED(rv)) { 2124 aUserAgent = override; 2125 return NS_OK; 2126 } 2127 } 2128 2129 // When the caller is content and 'privacy.resistFingerprinting' is true, 2130 // return a spoofed userAgent which reveals the platform but not the 2131 // specific OS version, etc. 2132 if (shouldResistFingerprinting) { 2133 nsAutoCString spoofedUA; 2134 nsRFPService::GetSpoofedUserAgent(spoofedUA); 2135 CopyASCIItoUTF16(spoofedUA, aUserAgent); 2136 return NS_OK; 2137 } 2138 2139 nsresult rv; 2140 nsCOMPtr<nsIHttpProtocolHandler> service( 2141 do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv)); 2142 if (NS_WARN_IF(NS_FAILED(rv))) { 2143 return rv; 2144 } 2145 2146 nsAutoCString ua; 2147 rv = service->GetUserAgent(ua); 2148 if (NS_WARN_IF(NS_FAILED(rv))) { 2149 return rv; 2150 } 2151 2152 CopyASCIItoUTF16(ua, aUserAgent); 2153 2154 if (!aWindow) { 2155 return NS_OK; 2156 } 2157 2158 // Copy the User-Agent header from the document channel which has already been 2159 // subject to UA overrides. 2160 nsCOMPtr<Document> doc = aWindow->GetExtantDoc(); 2161 if (!doc) { 2162 return NS_OK; 2163 } 2164 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(doc->GetChannel()); 2165 if (httpChannel) { 2166 bool IsUserAgentHeaderOutdated; 2167 (void)httpChannel->GetIsUserAgentHeaderOutdated(&IsUserAgentHeaderOutdated); 2168 2169 // Do not return user agent from the request 2170 // if the user agent of the channel is outdated. 2171 if (!IsUserAgentHeaderOutdated) { 2172 nsAutoCString userAgent; 2173 rv = httpChannel->GetRequestHeader("User-Agent"_ns, userAgent); 2174 if (NS_WARN_IF(NS_FAILED(rv))) { 2175 return rv; 2176 } 2177 CopyASCIItoUTF16(userAgent, aUserAgent); 2178 } 2179 } 2180 return NS_OK; 2181 } 2182 2183 static nsCString RequestKeySystemAccessLogString( 2184 const nsAString& aKeySystem, 2185 const Sequence<MediaKeySystemConfiguration>& aConfigs, 2186 bool aIsSecureContext) { 2187 nsCString str; 2188 str.AppendPrintf( 2189 "Navigator::RequestMediaKeySystemAccess(keySystem='%s' options=", 2190 NS_ConvertUTF16toUTF8(aKeySystem).get()); 2191 str.Append(MediaKeySystemAccess::ToCString(aConfigs)); 2192 str.AppendLiteral(") secureContext="); 2193 str.AppendInt(aIsSecureContext); 2194 return str; 2195 } 2196 2197 already_AddRefed<Promise> Navigator::RequestMediaKeySystemAccess( 2198 const nsAString& aKeySystem, 2199 const Sequence<MediaKeySystemConfiguration>& aConfigs, ErrorResult& aRv) { 2200 EME_LOG("%s", RequestKeySystemAccessLogString(aKeySystem, aConfigs, 2201 mWindow->IsSecureContext()) 2202 .get()); 2203 2204 if (!mWindow->IsSecureContext()) { 2205 Document* doc = mWindow->GetExtantDoc(); 2206 AutoTArray<nsString, 1> params; 2207 nsString* uri = params.AppendElement(); 2208 if (doc) { 2209 (void)doc->GetDocumentURI(*uri); 2210 } 2211 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "Media"_ns, 2212 doc, nsContentUtils::eDOM_PROPERTIES, 2213 "MediaEMEInsecureContextDeprecatedWarning", 2214 params); 2215 } 2216 2217 Document* doc = mWindow->GetExtantDoc(); 2218 if (doc && 2219 !FeaturePolicyUtils::IsFeatureAllowed(doc, u"encrypted-media"_ns)) { 2220 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); 2221 return nullptr; 2222 } 2223 2224 RefPtr<DetailedPromise> promise = DetailedPromise::Create( 2225 mWindow->AsGlobal(), aRv, "navigator.requestMediaKeySystemAccess"_ns); 2226 if (aRv.Failed()) { 2227 return nullptr; 2228 } 2229 2230 GetOrCreateMediaKeySystemAccessManager()->Request(promise, aKeySystem, 2231 aConfigs); 2232 return promise.forget(); 2233 } 2234 2235 MediaKeySystemAccessManager* 2236 Navigator::GetOrCreateMediaKeySystemAccessManager() { 2237 if (!mMediaKeySystemAccessManager) { 2238 mMediaKeySystemAccessManager = new MediaKeySystemAccessManager(mWindow); 2239 } 2240 return mMediaKeySystemAccessManager; 2241 } 2242 2243 CredentialsContainer* Navigator::Credentials() { 2244 if (!mCredentials) { 2245 mCredentials = new CredentialsContainer(GetWindow()); 2246 } 2247 return mCredentials; 2248 } 2249 2250 dom::MediaCapabilities* Navigator::MediaCapabilities() { 2251 if (!mMediaCapabilities) { 2252 mMediaCapabilities = new dom::MediaCapabilities(GetWindow()->AsGlobal()); 2253 } 2254 return mMediaCapabilities; 2255 } 2256 2257 dom::MediaSession* Navigator::MediaSession() { 2258 if (!mMediaSession) { 2259 mMediaSession = new dom::MediaSession(GetWindow()); 2260 } 2261 return mMediaSession; 2262 } 2263 2264 bool Navigator::HasCreatedMediaSession() const { 2265 return mMediaSession != nullptr; 2266 } 2267 2268 Clipboard* Navigator::Clipboard() { 2269 if (!mClipboard) { 2270 mClipboard = new dom::Clipboard(GetWindow()); 2271 } 2272 return mClipboard; 2273 } 2274 2275 AddonManager* Navigator::GetMozAddonManager(ErrorResult& aRv) { 2276 if (!mAddonManager) { 2277 nsPIDOMWindowInner* win = GetWindow(); 2278 if (!win) { 2279 aRv.Throw(NS_ERROR_UNEXPECTED); 2280 return nullptr; 2281 } 2282 2283 mAddonManager = ConstructJSImplementation<AddonManager>( 2284 "@mozilla.org/addon-web-api/manager;1", win->AsGlobal(), aRv); 2285 if (aRv.Failed()) { 2286 return nullptr; 2287 } 2288 } 2289 2290 return mAddonManager; 2291 } 2292 2293 webgpu::Instance* Navigator::Gpu() { 2294 if (!mWebGpu) { 2295 mWebGpu = webgpu::Instance::Create(GetWindow()->AsGlobal()); 2296 } 2297 return mWebGpu; 2298 } 2299 2300 dom::LockManager* Navigator::Locks() { 2301 if (!mLocks) { 2302 mLocks = dom::LockManager::Create(*GetWindow()->AsGlobal()); 2303 } 2304 return mLocks; 2305 } 2306 2307 NavigatorLogin* Navigator::Login() { 2308 if (!mLogin) { 2309 mLogin = new NavigatorLogin(GetWindow()); 2310 } 2311 return mLogin; 2312 } 2313 2314 dom::PrivateAttribution* Navigator::PrivateAttribution() { 2315 if (!mPrivateAttribution) { 2316 mPrivateAttribution = new dom::PrivateAttribution(GetWindow()->AsGlobal()); 2317 } 2318 return mPrivateAttribution; 2319 } 2320 2321 /* static */ 2322 bool Navigator::Webdriver() { 2323 #ifdef ENABLE_WEBDRIVER 2324 nsCOMPtr<nsIMarionette> marionette = do_GetService(NS_MARIONETTE_CONTRACTID); 2325 if (marionette) { 2326 bool marionetteRunning = false; 2327 marionette->GetRunning(&marionetteRunning); 2328 if (marionetteRunning) { 2329 return true; 2330 } 2331 } 2332 2333 nsCOMPtr<nsIRemoteAgent> agent = do_GetService(NS_REMOTEAGENT_CONTRACTID); 2334 if (agent) { 2335 bool remoteAgentRunning = false; 2336 agent->GetRunning(&remoteAgentRunning); 2337 if (remoteAgentRunning) { 2338 return true; 2339 } 2340 } 2341 #endif 2342 2343 return false; 2344 } 2345 2346 AutoplayPolicy Navigator::GetAutoplayPolicy(AutoplayPolicyMediaType aType) { 2347 if (!mWindow) { 2348 return AutoplayPolicy::Disallowed; 2349 } 2350 nsCOMPtr<Document> doc = mWindow->GetExtantDoc(); 2351 if (!doc) { 2352 return AutoplayPolicy::Disallowed; 2353 } 2354 return media::AutoplayPolicy::GetAutoplayPolicy(aType, *doc); 2355 } 2356 2357 AutoplayPolicy Navigator::GetAutoplayPolicy(HTMLMediaElement& aElement) { 2358 return media::AutoplayPolicy::GetAutoplayPolicy(aElement); 2359 } 2360 2361 AutoplayPolicy Navigator::GetAutoplayPolicy(AudioContext& aContext) { 2362 return media::AutoplayPolicy::GetAutoplayPolicy(aContext); 2363 } 2364 2365 already_AddRefed<dom::UserActivation> Navigator::UserActivation() { 2366 if (!mUserActivation) { 2367 mUserActivation = new dom::UserActivation(GetWindow()); 2368 } 2369 return do_AddRef(mUserActivation); 2370 } 2371 2372 dom::WakeLockJS* Navigator::WakeLock() { 2373 if (!mWakeLock) { 2374 mWakeLock = new WakeLockJS(mWindow); 2375 } 2376 return mWakeLock; 2377 } 2378 2379 } // namespace mozilla::dom