Bug 1852740: add tests for the `fetchpriority` attribute in Link headers. r=necko...
[gecko.git] / dom / base / Navigator.cpp
blobb1a522b73d592aeb2b22a02e08fe8ec4ece6b8a7
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/. */
7 // Needs to be first.
8 #include "base/basictypes.h"
10 #include "Navigator.h"
11 #include "nsIXULAppInfo.h"
12 #include "nsPluginArray.h"
13 #include "nsMimeTypeArray.h"
14 #include "mozilla/Components.h"
15 #include "mozilla/ContentBlockingNotifier.h"
16 #include "mozilla/MemoryReporting.h"
17 #include "mozilla/dom/BodyExtractor.h"
18 #include "mozilla/dom/FetchBinding.h"
19 #include "mozilla/dom/File.h"
20 #include "Geolocation.h"
21 #include "nsIClassOfService.h"
22 #include "nsIHttpProtocolHandler.h"
23 #include "nsIContentPolicy.h"
24 #include "nsContentPolicyUtils.h"
25 #include "nsISupportsPriority.h"
26 #include "nsIWebProtocolHandlerRegistrar.h"
27 #include "nsCharSeparatedTokenizer.h"
28 #include "nsContentUtils.h"
29 #include "nsUnicharUtils.h"
30 #include "mozilla/Preferences.h"
31 #include "mozilla/StaticPrefs_dom.h"
32 #ifdef FUZZING
33 # include "mozilla/StaticPrefs_fuzzing.h"
34 #endif
35 #include "mozilla/StaticPrefs_media.h"
36 #include "mozilla/StaticPrefs_network.h"
37 #include "mozilla/StaticPrefs_pdfjs.h"
38 #include "mozilla/StaticPrefs_privacy.h"
39 #include "mozilla/StorageAccess.h"
40 #include "mozilla/Telemetry.h"
41 #include "BatteryManager.h"
42 #include "mozilla/dom/CredentialsContainer.h"
43 #include "mozilla/dom/Clipboard.h"
44 #include "mozilla/dom/FeaturePolicyUtils.h"
45 #include "mozilla/dom/GamepadServiceTest.h"
46 #include "mozilla/dom/MediaCapabilities.h"
47 #include "mozilla/dom/MediaSession.h"
48 #include "mozilla/dom/power/PowerManagerService.h"
49 #include "mozilla/dom/LockManager.h"
50 #include "mozilla/dom/MIDIAccessManager.h"
51 #include "mozilla/dom/MIDIOptionsBinding.h"
52 #include "mozilla/dom/Permissions.h"
53 #include "mozilla/dom/ServiceWorkerContainer.h"
54 #include "mozilla/dom/StorageManager.h"
55 #include "mozilla/dom/TCPSocket.h"
56 #include "mozilla/dom/URLSearchParams.h"
57 #include "mozilla/dom/UserActivation.h"
58 #include "mozilla/dom/VRDisplay.h"
59 #include "mozilla/dom/VRDisplayEvent.h"
60 #include "mozilla/dom/VRServiceTest.h"
61 #include "mozilla/dom/XRSystem.h"
62 #include "mozilla/dom/workerinternals/RuntimeService.h"
63 #include "mozilla/Hal.h"
64 #include "mozilla/ClearOnShutdown.h"
65 #include "mozilla/StaticPtr.h"
66 #include "Connection.h"
67 #include "mozilla/dom/Event.h" // for Event
68 #include "nsGlobalWindowInner.h"
69 #include "nsIPermissionManager.h"
70 #include "nsMimeTypes.h"
71 #include "nsNetUtil.h"
72 #include "nsRFPService.h"
73 #include "nsStringStream.h"
74 #include "nsComponentManagerUtils.h"
75 #include "nsICookieManager.h"
76 #include "nsICookieService.h"
77 #include "nsIHttpChannel.h"
78 #ifdef ENABLE_WEBDRIVER
79 # include "nsIMarionette.h"
80 # include "nsIRemoteAgent.h"
81 #endif
82 #include "nsStreamUtils.h"
83 #include "WidgetUtils.h"
84 #include "nsIScriptError.h"
85 #include "ReferrerInfo.h"
86 #include "mozilla/PermissionDelegateHandler.h"
88 #include "nsIExternalProtocolHandler.h"
89 #include "BrowserChild.h"
90 #include "mozilla/ipc/URIUtils.h"
92 #include "mozilla/dom/MediaDevices.h"
93 #include "MediaManager.h"
95 #include "nsJSUtils.h"
97 #include "mozilla/dom/Promise.h"
99 #include "nsIUploadChannel2.h"
100 #include "mozilla/dom/FormData.h"
101 #include "nsIDocShell.h"
103 #include "mozilla/dom/WorkerPrivate.h"
104 #include "mozilla/dom/WorkerRunnable.h"
106 #if defined(XP_WIN)
107 # include "mozilla/WindowsVersion.h"
108 #endif
110 #include "mozilla/EMEUtils.h"
111 #include "mozilla/DetailedPromise.h"
112 #include "mozilla/Unused.h"
114 #include "mozilla/webgpu/Instance.h"
115 #include "mozilla/dom/WindowGlobalChild.h"
117 #include "mozilla/intl/LocaleService.h"
118 #include "mozilla/dom/AudioContext.h"
119 #include "mozilla/dom/HTMLMediaElement.h"
120 #include "AutoplayPolicy.h"
122 namespace mozilla::dom {
124 static const nsLiteralCString kVibrationPermissionType = "vibration"_ns;
126 Navigator::Navigator(nsPIDOMWindowInner* aWindow) : mWindow(aWindow) {}
128 Navigator::~Navigator() { Invalidate(); }
130 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Navigator)
131 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
132 NS_INTERFACE_MAP_ENTRY(nsISupports)
133 NS_INTERFACE_MAP_END
135 NS_IMPL_CYCLE_COLLECTING_ADDREF(Navigator)
136 NS_IMPL_CYCLE_COLLECTING_RELEASE(Navigator)
138 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(Navigator)
140 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Navigator)
141 tmp->Invalidate();
142 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
143 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSharePromise)
144 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
145 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
147 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Navigator)
148 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPlugins)
149 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPermissions)
150 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGeolocation)
151 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBatteryManager)
152 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBatteryPromise)
153 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConnection)
154 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStorageManager)
155 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCredentials)
156 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaDevices)
157 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServiceWorkerContainer)
158 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaCapabilities)
159 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaSession)
160 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAddonManager)
161 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebGpu)
162 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocks)
163 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUserActivation)
165 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
166 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaKeySystemAccessManager)
167 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepadServiceTest)
168 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRGetDisplaysPromises)
169 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRServiceTest)
170 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSharePromise)
171 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXRSystem)
172 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
174 void Navigator::Invalidate() {
175 // Don't clear mWindow here so we know we've got a non-null mWindow
176 // until we're unlinked.
178 mPlugins = nullptr;
180 mPermissions = nullptr;
182 if (mStorageManager) {
183 mStorageManager->Shutdown();
184 mStorageManager = nullptr;
187 // If there is a page transition, make sure delete the geolocation object.
188 if (mGeolocation) {
189 mGeolocation->Shutdown();
190 mGeolocation = nullptr;
193 if (mBatteryManager) {
194 mBatteryManager->Shutdown();
195 mBatteryManager = nullptr;
198 mBatteryPromise = nullptr;
200 if (mConnection) {
201 mConnection->Shutdown();
202 mConnection = nullptr;
205 mMediaDevices = nullptr;
207 mServiceWorkerContainer = nullptr;
209 if (mMediaKeySystemAccessManager) {
210 mMediaKeySystemAccessManager->Shutdown();
211 mMediaKeySystemAccessManager = nullptr;
214 if (mGamepadServiceTest) {
215 mGamepadServiceTest->Shutdown();
216 mGamepadServiceTest = nullptr;
219 mVRGetDisplaysPromises.Clear();
221 if (mVRServiceTest) {
222 mVRServiceTest->Shutdown();
223 mVRServiceTest = nullptr;
226 if (mXRSystem) {
227 mXRSystem->Shutdown();
228 mXRSystem = nullptr;
231 mMediaCapabilities = nullptr;
233 if (mMediaSession) {
234 mMediaSession->Shutdown();
235 mMediaSession = nullptr;
238 mAddonManager = nullptr;
240 mWebGpu = nullptr;
242 if (mLocks) {
243 // Unloading a page does not immediately destruct the lock manager actor,
244 // but we want to abort the lock requests as soon as possible. Explicitly
245 // call Shutdown() to do that.
246 mLocks->Shutdown();
247 mLocks = nullptr;
250 mUserActivation = nullptr;
252 mSharePromise = nullptr;
255 void Navigator::GetUserAgent(nsAString& aUserAgent, CallerType aCallerType,
256 ErrorResult& aRv) const {
257 nsCOMPtr<nsPIDOMWindowInner> window;
259 if (mWindow) {
260 window = mWindow;
261 nsIDocShell* docshell = window->GetDocShell();
262 nsString customUserAgent;
263 if (docshell) {
264 docshell->GetBrowsingContext()->GetCustomUserAgent(customUserAgent);
266 if (!customUserAgent.IsEmpty()) {
267 aUserAgent = customUserAgent;
268 return;
273 nsCOMPtr<Document> doc = mWindow->GetExtantDoc();
274 nsresult rv = GetUserAgent(
275 mWindow, doc, aCallerType == CallerType::System ? Some(false) : Nothing(),
276 aUserAgent);
277 if (NS_WARN_IF(NS_FAILED(rv))) {
278 aRv.Throw(rv);
282 void Navigator::GetAppCodeName(nsAString& aAppCodeName, ErrorResult& aRv) {
283 nsresult rv;
285 nsCOMPtr<nsIHttpProtocolHandler> service(
286 do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
287 if (NS_WARN_IF(NS_FAILED(rv))) {
288 aRv.Throw(rv);
289 return;
292 nsAutoCString appName;
293 rv = service->GetAppName(appName);
294 if (NS_WARN_IF(NS_FAILED(rv))) {
295 aRv.Throw(rv);
296 return;
299 CopyASCIItoUTF16(appName, aAppCodeName);
302 void Navigator::GetAppVersion(nsAString& aAppVersion, CallerType aCallerType,
303 ErrorResult& aRv) const {
304 nsCOMPtr<Document> doc = mWindow->GetExtantDoc();
306 nsresult rv = GetAppVersion(
307 aAppVersion, doc,
308 /* aUsePrefOverriddenValue = */ aCallerType != CallerType::System);
309 if (NS_WARN_IF(NS_FAILED(rv))) {
310 aRv.Throw(rv);
314 void Navigator::GetAppName(nsAString& aAppName) const {
315 aAppName.AssignLiteral("Netscape");
319 * Returns the value of Accept-Languages (HTTP header) as a nsTArray of
320 * languages. The value is set in the preference by the user ("Content
321 * Languages").
323 * "en", "en-US" and "i-cherokee" and "" are valid languages tokens.
325 * If there is no valid language, the value of getWebExposedLocales is
326 * used to ensure that locale spoofing is honored and to reduce
327 * fingerprinting.
329 * See RFC 7231, Section 9.7 "Browser Fingerprinting" and
330 * RFC 2616, Section 15.1.4 "Privacy Issues Connected to Accept Headers"
331 * for more detail.
333 /* static */
334 void Navigator::GetAcceptLanguages(nsTArray<nsString>& aLanguages) {
335 MOZ_ASSERT(NS_IsMainThread());
337 aLanguages.Clear();
339 // E.g. "de-de, en-us,en".
340 nsAutoString acceptLang;
341 Preferences::GetLocalizedString("intl.accept_languages", acceptLang);
343 // Split values on commas.
344 for (nsDependentSubstring lang :
345 nsCharSeparatedTokenizer(acceptLang, ',').ToRange()) {
346 // Replace "_" with "-" to avoid POSIX/Windows "en_US" notation.
347 // NOTE: we should probably rely on the pref being set correctly.
348 if (lang.Length() > 2 && lang[2] == char16_t('_')) {
349 lang.Replace(2, 1, char16_t('-'));
352 // Use uppercase for country part, e.g. "en-US", not "en-us", see BCP47
353 // only uppercase 2-letter country codes, not "zh-Hant", "de-DE-x-goethe".
354 // NOTE: we should probably rely on the pref being set correctly.
355 if (lang.Length() > 2) {
356 int32_t pos = 0;
357 bool first = true;
358 for (const nsAString& code :
359 nsCharSeparatedTokenizer(lang, '-').ToRange()) {
360 if (code.Length() == 2 && !first) {
361 nsAutoString upper(code);
362 ToUpperCase(upper);
363 lang.Replace(pos, code.Length(), upper);
366 pos += code.Length() + 1; // 1 is the separator
367 first = false;
371 aLanguages.AppendElement(lang);
373 if (aLanguages.Length() == 0) {
374 nsTArray<nsCString> locales;
375 mozilla::intl::LocaleService::GetInstance()->GetWebExposedLocales(locales);
376 aLanguages.AppendElement(NS_ConvertUTF8toUTF16(locales[0]));
381 * Returns the first language from GetAcceptLanguages.
383 * Full details above in GetAcceptLanguages.
385 void Navigator::GetLanguage(nsAString& aLanguage) {
386 nsTArray<nsString> languages;
387 GetLanguages(languages);
388 MOZ_ASSERT(languages.Length() >= 1);
389 aLanguage.Assign(languages[0]);
392 void Navigator::GetLanguages(nsTArray<nsString>& aLanguages) {
393 GetAcceptLanguages(aLanguages);
395 // The returned value is cached by the binding code. The window listens to the
396 // accept languages change and will clear the cache when needed. It has to
397 // take care of dispatching the DOM event already and the invalidation and the
398 // event has to be timed correctly.
401 void Navigator::GetPlatform(nsAString& aPlatform, CallerType aCallerType,
402 ErrorResult& aRv) const {
403 if (mWindow) {
404 BrowsingContext* bc = mWindow->GetBrowsingContext();
405 nsString customPlatform;
406 if (bc) {
407 bc->GetCustomPlatform(customPlatform);
409 if (!customPlatform.IsEmpty()) {
410 aPlatform = customPlatform;
411 return;
416 nsCOMPtr<Document> doc = mWindow->GetExtantDoc();
418 nsresult rv = GetPlatform(
419 aPlatform, doc,
420 /* aUsePrefOverriddenValue = */ aCallerType != CallerType::System);
421 if (NS_WARN_IF(NS_FAILED(rv))) {
422 aRv.Throw(rv);
426 void Navigator::GetOscpu(nsAString& aOSCPU, CallerType aCallerType,
427 ErrorResult& aRv) const {
428 if (aCallerType != CallerType::System) {
429 // If fingerprinting resistance is on, we will spoof this value. See
430 // nsRFPService.h for details about spoofed values.
431 if (nsContentUtils::ShouldResistFingerprinting(GetDocShell(),
432 RFPTarget::NavigatorOscpu)) {
433 aOSCPU.AssignLiteral(SPOOFED_OSCPU);
434 return;
437 nsAutoString override;
438 nsresult rv = Preferences::GetString("general.oscpu.override", override);
439 if (NS_SUCCEEDED(rv)) {
440 aOSCPU = override;
441 return;
445 nsresult rv;
446 nsCOMPtr<nsIHttpProtocolHandler> service(
447 do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
448 if (NS_WARN_IF(NS_FAILED(rv))) {
449 aRv.Throw(rv);
450 return;
453 nsAutoCString oscpu;
454 rv = service->GetOscpu(oscpu);
455 if (NS_WARN_IF(NS_FAILED(rv))) {
456 aRv.Throw(rv);
457 return;
460 CopyASCIItoUTF16(oscpu, aOSCPU);
463 void Navigator::GetVendor(nsAString& aVendor) { aVendor.Truncate(); }
465 void Navigator::GetVendorSub(nsAString& aVendorSub) { aVendorSub.Truncate(); }
467 void Navigator::GetProduct(nsAString& aProduct) {
468 aProduct.AssignLiteral("Gecko");
471 void Navigator::GetProductSub(nsAString& aProductSub) {
472 // Legacy build date hardcoded for backward compatibility (bug 776376)
473 aProductSub.AssignLiteral(LEGACY_UA_GECKO_TRAIL);
476 nsMimeTypeArray* Navigator::GetMimeTypes(ErrorResult& aRv) {
477 auto* plugins = GetPlugins(aRv);
478 if (!plugins) {
479 return nullptr;
482 return plugins->MimeTypeArray();
485 nsPluginArray* Navigator::GetPlugins(ErrorResult& aRv) {
486 if (!mPlugins) {
487 if (!mWindow) {
488 aRv.Throw(NS_ERROR_UNEXPECTED);
489 return nullptr;
491 mPlugins = MakeRefPtr<nsPluginArray>(mWindow);
494 return mPlugins;
497 bool Navigator::PdfViewerEnabled() { return !StaticPrefs::pdfjs_disabled(); }
499 Permissions* Navigator::GetPermissions(ErrorResult& aRv) {
500 if (!mWindow) {
501 aRv.Throw(NS_ERROR_UNEXPECTED);
502 return nullptr;
505 if (!mPermissions) {
506 mPermissions = new Permissions(mWindow);
509 return mPermissions;
512 StorageManager* Navigator::Storage() {
513 MOZ_ASSERT(mWindow);
515 if (!mStorageManager) {
516 mStorageManager = new StorageManager(mWindow->AsGlobal());
519 return mStorageManager;
522 bool Navigator::CookieEnabled() {
523 // Check whether an exception overrides the global cookie behavior
524 // Note that the code for getting the URI here matches that in
525 // nsHTMLDocument::SetCookie.
526 if (!mWindow || !mWindow->GetDocShell()) {
527 return nsICookieManager::GetCookieBehavior(false) !=
528 nsICookieService::BEHAVIOR_REJECT;
531 nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(mWindow);
532 uint32_t cookieBehavior = loadContext
533 ? nsICookieManager::GetCookieBehavior(
534 loadContext->UsePrivateBrowsing())
535 : nsICookieManager::GetCookieBehavior(false);
536 bool cookieEnabled = cookieBehavior != nsICookieService::BEHAVIOR_REJECT;
538 nsCOMPtr<Document> doc = mWindow->GetExtantDoc();
539 if (!doc) {
540 return cookieEnabled;
543 uint32_t rejectedReason = 0;
544 bool granted = false;
545 nsresult rv = doc->NodePrincipal()->HasFirstpartyStorageAccess(
546 mWindow, &rejectedReason, &granted);
547 if (NS_FAILED(rv)) {
548 // Not a content, so technically can't set cookies, but let's
549 // just return the default value.
550 return cookieEnabled;
553 // We should return true if the cookie is partitioned because the cookie is
554 // still available in this case.
555 if (!granted &&
556 StoragePartitioningEnabled(rejectedReason, doc->CookieJarSettings())) {
557 granted = true;
560 ContentBlockingNotifier::OnDecision(
561 mWindow,
562 granted ? ContentBlockingNotifier::BlockingDecision::eAllow
563 : ContentBlockingNotifier::BlockingDecision::eBlock,
564 rejectedReason);
565 return granted;
568 bool Navigator::OnLine() {
569 if (mWindow) {
570 // Check if this tab is set to be offline.
571 BrowsingContext* bc = mWindow->GetBrowsingContext();
572 if (bc && bc->Top()->GetForceOffline()) {
573 return false;
576 // Return the default browser value
577 return !NS_IsOffline();
580 void Navigator::GetBuildID(nsAString& aBuildID, CallerType aCallerType,
581 ErrorResult& aRv) const {
582 if (aCallerType != CallerType::System) {
583 // If fingerprinting resistance is on, we will spoof this value. See
584 // nsRFPService.h for details about spoofed values.
585 if (nsContentUtils::ShouldResistFingerprinting(
586 GetDocShell(), RFPTarget::NavigatorBuildID)) {
587 aBuildID.AssignLiteral(LEGACY_BUILD_ID);
588 return;
591 nsAutoString override;
592 nsresult rv = Preferences::GetString("general.buildID.override", override);
593 if (NS_SUCCEEDED(rv)) {
594 aBuildID = override;
595 return;
598 nsAutoCString host;
599 bool isHTTPS = false;
600 if (mWindow) {
601 nsCOMPtr<Document> doc = mWindow->GetDoc();
602 if (doc) {
603 nsIURI* uri = doc->GetDocumentURI();
604 if (uri) {
605 isHTTPS = uri->SchemeIs("https");
606 if (isHTTPS) {
607 MOZ_ALWAYS_SUCCEEDS(uri->GetHost(host));
613 // Spoof the buildID on pages not loaded from "https://*.mozilla.org".
614 if (!isHTTPS || !StringEndsWith(host, ".mozilla.org"_ns)) {
615 aBuildID.AssignLiteral(LEGACY_BUILD_ID);
616 return;
620 nsCOMPtr<nsIXULAppInfo> appInfo =
621 do_GetService("@mozilla.org/xre/app-info;1");
622 if (!appInfo) {
623 aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
624 return;
627 nsAutoCString buildID;
628 nsresult rv = appInfo->GetAppBuildID(buildID);
629 if (NS_WARN_IF(NS_FAILED(rv))) {
630 aRv.Throw(rv);
631 return;
634 aBuildID.Truncate();
635 AppendASCIItoUTF16(buildID, aBuildID);
638 void Navigator::GetDoNotTrack(nsAString& aResult) {
639 bool doNotTrack = StaticPrefs::privacy_donottrackheader_enabled();
640 if (!doNotTrack) {
641 nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(mWindow);
642 doNotTrack = loadContext && loadContext->UseTrackingProtection();
645 if (doNotTrack) {
646 aResult.AssignLiteral("1");
647 } else {
648 aResult.AssignLiteral("unspecified");
652 bool Navigator::GlobalPrivacyControl() {
653 return StaticPrefs::privacy_globalprivacycontrol_enabled() &&
654 StaticPrefs::privacy_globalprivacycontrol_functionality_enabled();
657 uint64_t Navigator::HardwareConcurrency() {
658 workerinternals::RuntimeService* rts =
659 workerinternals::RuntimeService::GetOrCreateService();
660 if (!rts) {
661 return 1;
664 return rts->ClampedHardwareConcurrency(
665 nsGlobalWindowInner::Cast(mWindow)->ShouldResistFingerprinting(
666 RFPTarget::NavigatorHWConcurrency));
669 namespace {
671 class VibrateWindowListener : public nsIDOMEventListener {
672 public:
673 VibrateWindowListener(nsPIDOMWindowInner* aWindow, Document* aDocument) {
674 mWindow = do_GetWeakReference(aWindow);
675 mDocument = do_GetWeakReference(aDocument);
677 constexpr auto visibilitychange = u"visibilitychange"_ns;
678 aDocument->AddSystemEventListener(visibilitychange, this, /* listener */
679 true, /* use capture */
680 false /* wants untrusted */);
683 void RemoveListener();
685 NS_DECL_ISUPPORTS
686 NS_DECL_NSIDOMEVENTLISTENER
688 private:
689 virtual ~VibrateWindowListener() = default;
691 nsWeakPtr mWindow;
692 nsWeakPtr mDocument;
695 NS_IMPL_ISUPPORTS(VibrateWindowListener, nsIDOMEventListener)
697 StaticRefPtr<VibrateWindowListener> gVibrateWindowListener;
699 static bool MayVibrate(Document* doc) {
700 // Hidden documents cannot start or stop a vibration.
701 return (doc && !doc->Hidden());
704 NS_IMETHODIMP
705 VibrateWindowListener::HandleEvent(Event* aEvent) {
706 nsCOMPtr<Document> doc = do_QueryInterface(aEvent->GetTarget());
708 if (!MayVibrate(doc)) {
709 // It's important that we call CancelVibrate(), not Vibrate() with an
710 // empty list, because Vibrate() will fail if we're no longer focused, but
711 // CancelVibrate() will succeed, so long as nobody else has started a new
712 // vibration pattern.
713 nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mWindow);
714 hal::CancelVibrate(window);
715 RemoveListener();
716 gVibrateWindowListener = nullptr;
717 // Careful: The line above might have deleted |this|!
720 return NS_OK;
723 void VibrateWindowListener::RemoveListener() {
724 nsCOMPtr<EventTarget> target = do_QueryReferent(mDocument);
725 if (!target) {
726 return;
728 constexpr auto visibilitychange = u"visibilitychange"_ns;
729 target->RemoveSystemEventListener(visibilitychange, this,
730 true /* use capture */);
733 } // namespace
735 void Navigator::SetVibrationPermission(bool aPermitted, bool aPersistent) {
736 MOZ_ASSERT(NS_IsMainThread());
738 nsTArray<uint32_t> pattern = std::move(mRequestedVibrationPattern);
740 if (!mWindow) {
741 return;
744 nsCOMPtr<Document> doc = mWindow->GetExtantDoc();
746 if (!MayVibrate(doc)) {
747 return;
750 if (aPermitted) {
751 // Add a listener to cancel the vibration if the document becomes hidden,
752 // and remove the old visibility listener, if there was one.
753 if (!gVibrateWindowListener) {
754 // If gVibrateWindowListener is null, this is the first time we've
755 // vibrated, and we need to register a listener to clear
756 // gVibrateWindowListener on shutdown.
757 ClearOnShutdown(&gVibrateWindowListener);
758 } else {
759 gVibrateWindowListener->RemoveListener();
761 gVibrateWindowListener = new VibrateWindowListener(mWindow, doc);
762 hal::Vibrate(pattern, mWindow);
765 if (aPersistent) {
766 nsCOMPtr<nsIPermissionManager> permMgr =
767 components::PermissionManager::Service();
768 if (!permMgr) {
769 return;
771 permMgr->AddFromPrincipal(doc->NodePrincipal(), kVibrationPermissionType,
772 aPermitted ? nsIPermissionManager::ALLOW_ACTION
773 : nsIPermissionManager::DENY_ACTION,
774 nsIPermissionManager::EXPIRE_SESSION, 0);
778 bool Navigator::Vibrate(uint32_t aDuration) {
779 AutoTArray<uint32_t, 1> pattern;
780 pattern.AppendElement(aDuration);
781 return Vibrate(pattern);
784 nsTArray<uint32_t> SanitizeVibratePattern(const nsTArray<uint32_t>& aPattern) {
785 nsTArray<uint32_t> pattern(aPattern.Clone());
787 if (pattern.Length() > StaticPrefs::dom_vibrator_max_vibrate_list_len()) {
788 pattern.SetLength(StaticPrefs::dom_vibrator_max_vibrate_list_len());
791 for (size_t i = 0; i < pattern.Length(); ++i) {
792 pattern[i] =
793 std::min(StaticPrefs::dom_vibrator_max_vibrate_ms(), pattern[i]);
796 return pattern;
799 bool Navigator::Vibrate(const nsTArray<uint32_t>& aPattern) {
800 MOZ_ASSERT(NS_IsMainThread());
802 if (!mWindow) {
803 return false;
806 nsCOMPtr<Document> doc = mWindow->GetExtantDoc();
808 if (!MayVibrate(doc)) {
809 return false;
812 nsTArray<uint32_t> pattern = SanitizeVibratePattern(aPattern);
814 // The spec says we check dom.vibrator.enabled after we've done the sanity
815 // checking on the pattern.
816 if (!StaticPrefs::dom_vibrator_enabled()) {
817 return true;
820 mRequestedVibrationPattern = std::move(pattern);
822 PermissionDelegateHandler* permissionHandler =
823 doc->GetPermissionDelegateHandler();
824 if (NS_WARN_IF(!permissionHandler)) {
825 return false;
828 uint32_t permission = nsIPermissionManager::UNKNOWN_ACTION;
830 permissionHandler->GetPermission(kVibrationPermissionType, &permission,
831 false);
833 if (permission == nsIPermissionManager::DENY_ACTION) {
834 // Abort without observer service or on denied session permission.
835 SetVibrationPermission(false /* permitted */, false /* persistent */);
836 return false;
839 if (permission == nsIPermissionManager::ALLOW_ACTION ||
840 mRequestedVibrationPattern.IsEmpty() ||
841 (mRequestedVibrationPattern.Length() == 1 &&
842 mRequestedVibrationPattern[0] == 0)) {
843 // Always allow cancelling vibration and respect session permissions.
844 SetVibrationPermission(true /* permitted */, false /* persistent */);
845 return true;
848 // Request user permission.
849 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
850 if (!obs) {
851 return true;
854 obs->NotifyObservers(ToSupports(this), "Vibration:Request", nullptr);
856 return true;
859 //*****************************************************************************
860 // Pointer Events interface
861 //*****************************************************************************
863 uint32_t Navigator::MaxTouchPoints(CallerType aCallerType) {
864 nsIDocShell* docshell = GetDocShell();
865 BrowsingContext* bc = docshell ? docshell->GetBrowsingContext() : nullptr;
867 // Responsive Design Mode overrides the maxTouchPoints property when
868 // touch simulation is enabled.
869 if (bc && bc->InRDMPane()) {
870 return bc->GetMaxTouchPointsOverride();
873 // The maxTouchPoints is going to reveal the detail of users' hardware. So,
874 // we will spoof it into 0 if fingerprinting resistance is on.
875 if (aCallerType != CallerType::System &&
876 nsContentUtils::ShouldResistFingerprinting(GetDocShell(),
877 RFPTarget::PointerEvents)) {
878 return 0;
881 nsCOMPtr<nsIWidget> widget =
882 widget::WidgetUtils::DOMWindowToWidget(mWindow->GetOuterWindow());
884 NS_ENSURE_TRUE(widget, 0);
885 return widget->GetMaxTouchPoints();
888 //*****************************************************************************
889 // Navigator::nsIDOMClientInformation
890 //*****************************************************************************
892 // This list should be kept up-to-date with the spec:
893 // https://html.spec.whatwg.org/multipage/system-state.html#custom-handlers
894 // If you change this list, please also update the copy in E10SUtils.sys.mjs.
895 static const char* const kSafeSchemes[] = {
896 // clang-format off
897 "bitcoin",
898 "ftp",
899 "ftps",
900 "geo",
901 "im",
902 "irc",
903 "ircs",
904 "magnet",
905 "mailto",
906 "matrix",
907 "mms",
908 "news",
909 "nntp",
910 "openpgp4fpr",
911 "sftp",
912 "sip",
913 "sms",
914 "smsto",
915 "ssh",
916 "tel",
917 "urn",
918 "webcal",
919 "wtai",
920 "xmpp",
921 // clang-format on
924 void Navigator::CheckProtocolHandlerAllowed(const nsAString& aScheme,
925 nsIURI* aHandlerURI,
926 nsIURI* aDocumentURI,
927 ErrorResult& aRv) {
928 auto raisePermissionDeniedHandler = [&] {
929 nsAutoCString spec;
930 aHandlerURI->GetSpec(spec);
931 nsPrintfCString message("Permission denied to add %s as a protocol handler",
932 spec.get());
933 aRv.ThrowSecurityError(message);
936 auto raisePermissionDeniedScheme = [&] {
937 nsPrintfCString message(
938 "Permission denied to add a protocol handler for %s",
939 NS_ConvertUTF16toUTF8(aScheme).get());
940 aRv.ThrowSecurityError(message);
943 if (!aDocumentURI || !aHandlerURI) {
944 aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
945 return;
948 nsCString spec;
949 aHandlerURI->GetSpec(spec);
950 // If the uri doesn't contain '%s', it won't be a good handler - the %s
951 // gets replaced with the handled URI.
952 if (!FindInReadable("%s"_ns, spec)) {
953 aRv.ThrowSyntaxError("Handler URI does not contain \"%s\".");
954 return;
957 // For security reasons we reject non-http(s) urls (see bug 354316),
958 nsAutoCString docScheme;
959 nsAutoCString handlerScheme;
960 aDocumentURI->GetScheme(docScheme);
961 aHandlerURI->GetScheme(handlerScheme);
962 if ((!docScheme.EqualsLiteral("https") && !docScheme.EqualsLiteral("http")) ||
963 (!handlerScheme.EqualsLiteral("https") &&
964 !handlerScheme.EqualsLiteral("http"))) {
965 raisePermissionDeniedHandler();
966 return;
969 // Should be same-origin:
970 nsAutoCString handlerHost;
971 aHandlerURI->GetHostPort(handlerHost);
972 nsAutoCString documentHost;
973 aDocumentURI->GetHostPort(documentHost);
974 if (!handlerHost.Equals(documentHost) || !handlerScheme.Equals(docScheme)) {
975 raisePermissionDeniedHandler();
976 return;
979 // Having checked the handler URI, check the scheme:
980 nsAutoCString scheme;
981 ToLowerCase(NS_ConvertUTF16toUTF8(aScheme), scheme);
982 if (StringBeginsWith(scheme, "web+"_ns)) {
983 // Check for non-ascii
984 nsReadingIterator<char> iter;
985 nsReadingIterator<char> iterEnd;
986 auto remainingScheme = Substring(scheme, 4 /* web+ */);
987 remainingScheme.BeginReading(iter);
988 remainingScheme.EndReading(iterEnd);
989 // Scheme suffix must be non-empty
990 if (iter == iterEnd) {
991 raisePermissionDeniedScheme();
992 return;
994 for (; iter != iterEnd; iter++) {
995 if (*iter < 'a' || *iter > 'z') {
996 raisePermissionDeniedScheme();
997 return;
1000 } else {
1001 bool matches = false;
1002 for (const char* safeScheme : kSafeSchemes) {
1003 if (scheme.Equals(safeScheme)) {
1004 matches = true;
1005 break;
1008 if (!matches) {
1009 raisePermissionDeniedScheme();
1010 return;
1014 nsCOMPtr<nsIProtocolHandler> handler;
1015 nsCOMPtr<nsIIOService> io = components::IO::Service();
1016 if (NS_FAILED(
1017 io->GetProtocolHandler(scheme.get(), getter_AddRefs(handler)))) {
1018 raisePermissionDeniedScheme();
1019 return;
1022 // check if we have prefs set saying not to add this.
1023 bool defaultExternal =
1024 Preferences::GetBool("network.protocol-handler.external-default");
1025 nsPrintfCString specificPref("network.protocol-handler.external.%s",
1026 scheme.get());
1027 if (!Preferences::GetBool(specificPref.get(), defaultExternal)) {
1028 raisePermissionDeniedScheme();
1029 return;
1032 // Check to make sure this isn't already handled internally (we don't
1033 // want to let them take over, say "chrome"). In theory, the checks above
1034 // should have already taken care of this.
1035 nsCOMPtr<nsIExternalProtocolHandler> externalHandler =
1036 do_QueryInterface(handler);
1037 MOZ_RELEASE_ASSERT(
1038 externalHandler,
1039 "We should never allow overriding a builtin protocol handler");
1042 void Navigator::RegisterProtocolHandler(const nsAString& aScheme,
1043 const nsAString& aURI,
1044 ErrorResult& aRv) {
1045 if (!mWindow || !mWindow->GetOuterWindow() || !mWindow->GetDocShell() ||
1046 !mWindow->GetDoc()) {
1047 return;
1049 nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(mWindow);
1050 if (loadContext->UsePrivateBrowsing()) {
1051 // If we're a private window, don't alert the user or webpage. We log to the
1052 // console so that web developers have some way to tell what's going wrong.
1053 nsContentUtils::ReportToConsole(
1054 nsIScriptError::warningFlag, "DOM"_ns, mWindow->GetDoc(),
1055 nsContentUtils::eDOM_PROPERTIES,
1056 "RegisterProtocolHandlerPrivateBrowsingWarning");
1057 return;
1060 nsCOMPtr<Document> doc = mWindow->GetDoc();
1062 // Determine if doc is allowed to assign this handler
1063 nsIURI* docURI = doc->GetDocumentURIObject();
1064 nsCOMPtr<nsIURI> handlerURI;
1065 NS_NewURI(getter_AddRefs(handlerURI), NS_ConvertUTF16toUTF8(aURI),
1066 doc->GetDocumentCharacterSet(), docURI);
1067 CheckProtocolHandlerAllowed(aScheme, handlerURI, docURI, aRv);
1068 if (aRv.Failed()) {
1069 return;
1072 // Determine a title from the document URI.
1073 nsAutoCString docDisplayHostPort;
1074 docURI->GetDisplayHostPort(docDisplayHostPort);
1075 NS_ConvertASCIItoUTF16 title(docDisplayHostPort);
1077 if (XRE_IsContentProcess()) {
1078 nsAutoString scheme(aScheme);
1079 RefPtr<BrowserChild> browserChild = BrowserChild::GetFrom(mWindow);
1080 browserChild->SendRegisterProtocolHandler(scheme, handlerURI, title,
1081 docURI);
1082 return;
1085 nsCOMPtr<nsIWebProtocolHandlerRegistrar> registrar =
1086 do_GetService(NS_WEBPROTOCOLHANDLERREGISTRAR_CONTRACTID);
1087 if (registrar) {
1088 aRv = registrar->RegisterProtocolHandler(aScheme, handlerURI, title, docURI,
1089 mWindow->GetOuterWindow());
1093 Geolocation* Navigator::GetGeolocation(ErrorResult& aRv) {
1094 if (mGeolocation) {
1095 return mGeolocation;
1098 if (!mWindow || !mWindow->GetOuterWindow() || !mWindow->GetDocShell()) {
1099 aRv.Throw(NS_ERROR_FAILURE);
1100 return nullptr;
1103 mGeolocation = new Geolocation();
1104 if (NS_FAILED(mGeolocation->Init(mWindow))) {
1105 mGeolocation = nullptr;
1106 aRv.Throw(NS_ERROR_FAILURE);
1107 return nullptr;
1110 return mGeolocation;
1113 class BeaconStreamListener final : public nsIStreamListener {
1114 ~BeaconStreamListener() = default;
1116 public:
1117 BeaconStreamListener() : mLoadGroup(nullptr) {}
1119 void SetLoadGroup(nsILoadGroup* aLoadGroup) { mLoadGroup = aLoadGroup; }
1121 NS_DECL_ISUPPORTS
1122 NS_DECL_NSISTREAMLISTENER
1123 NS_DECL_NSIREQUESTOBSERVER
1125 private:
1126 nsCOMPtr<nsILoadGroup> mLoadGroup;
1129 NS_IMPL_ISUPPORTS(BeaconStreamListener, nsIStreamListener, nsIRequestObserver)
1131 NS_IMETHODIMP
1132 BeaconStreamListener::OnStartRequest(nsIRequest* aRequest) {
1133 // release the loadgroup first
1134 mLoadGroup = nullptr;
1136 return NS_ERROR_ABORT;
1139 NS_IMETHODIMP
1140 BeaconStreamListener::OnStopRequest(nsIRequest* aRequest, nsresult aStatus) {
1141 return NS_OK;
1144 NS_IMETHODIMP
1145 BeaconStreamListener::OnDataAvailable(nsIRequest* aRequest,
1146 nsIInputStream* inStr,
1147 uint64_t sourceOffset, uint32_t count) {
1148 MOZ_ASSERT(false);
1149 return NS_OK;
1152 bool Navigator::SendBeacon(const nsAString& aUrl,
1153 const Nullable<fetch::BodyInit>& aData,
1154 ErrorResult& aRv) {
1155 if (aData.IsNull()) {
1156 return SendBeaconInternal(aUrl, nullptr, eBeaconTypeOther, aRv);
1159 if (aData.Value().IsArrayBuffer()) {
1160 BodyExtractor<const ArrayBuffer> body(&aData.Value().GetAsArrayBuffer());
1161 return SendBeaconInternal(aUrl, &body, eBeaconTypeArrayBuffer, aRv);
1164 if (aData.Value().IsArrayBufferView()) {
1165 BodyExtractor<const ArrayBufferView> body(
1166 &aData.Value().GetAsArrayBufferView());
1167 return SendBeaconInternal(aUrl, &body, eBeaconTypeArrayBuffer, aRv);
1170 if (aData.Value().IsBlob()) {
1171 BodyExtractor<const Blob> body(&aData.Value().GetAsBlob());
1172 return SendBeaconInternal(aUrl, &body, eBeaconTypeBlob, aRv);
1175 if (aData.Value().IsFormData()) {
1176 BodyExtractor<const FormData> body(&aData.Value().GetAsFormData());
1177 return SendBeaconInternal(aUrl, &body, eBeaconTypeOther, aRv);
1180 if (aData.Value().IsUSVString()) {
1181 BodyExtractor<const nsAString> body(&aData.Value().GetAsUSVString());
1182 return SendBeaconInternal(aUrl, &body, eBeaconTypeOther, aRv);
1185 if (aData.Value().IsURLSearchParams()) {
1186 BodyExtractor<const URLSearchParams> body(
1187 &aData.Value().GetAsURLSearchParams());
1188 return SendBeaconInternal(aUrl, &body, eBeaconTypeOther, aRv);
1191 MOZ_CRASH("Invalid data type.");
1192 return false;
1195 bool Navigator::SendBeaconInternal(const nsAString& aUrl,
1196 BodyExtractorBase* aBody, BeaconType aType,
1197 ErrorResult& aRv) {
1198 if (!mWindow) {
1199 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1200 return false;
1203 nsCOMPtr<Document> doc = mWindow->GetDoc();
1204 if (!doc) {
1205 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1206 return false;
1209 nsIURI* documentURI = doc->GetDocumentURI();
1210 if (!documentURI) {
1211 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1212 return false;
1215 nsCOMPtr<nsIURI> uri;
1216 nsresult rv = nsContentUtils::NewURIWithDocumentCharset(
1217 getter_AddRefs(uri), aUrl, doc, doc->GetDocBaseURI());
1218 if (NS_FAILED(rv)) {
1219 aRv.ThrowTypeError<MSG_INVALID_URL>(NS_ConvertUTF16toUTF8(aUrl));
1220 return false;
1223 // Spec disallows any schemes save for HTTP/HTTPs
1224 if (!uri->SchemeIs("http") && !uri->SchemeIs("https")) {
1225 aRv.ThrowTypeError<MSG_INVALID_URL_SCHEME>("Beacon",
1226 uri->GetSpecOrDefault());
1227 return false;
1230 nsCOMPtr<nsIInputStream> in;
1231 nsAutoCString contentTypeWithCharset;
1232 nsAutoCString charset;
1233 uint64_t length = 0;
1234 if (aBody) {
1235 aRv = aBody->GetAsStream(getter_AddRefs(in), &length,
1236 contentTypeWithCharset, charset);
1237 if (NS_WARN_IF(aRv.Failed())) {
1238 return false;
1242 nsSecurityFlags securityFlags = nsILoadInfo::SEC_COOKIES_INCLUDE;
1243 // Ensure that only streams with content types that are safelisted ignore CORS
1244 // rules
1245 if (aBody && !contentTypeWithCharset.IsVoid() &&
1246 !nsContentUtils::IsCORSSafelistedRequestHeader("content-type"_ns,
1247 contentTypeWithCharset)) {
1248 securityFlags |= nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT;
1249 } else {
1250 securityFlags |= nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT;
1253 nsCOMPtr<nsIChannel> channel;
1254 rv = NS_NewChannel(getter_AddRefs(channel), uri, doc, securityFlags,
1255 nsIContentPolicy::TYPE_BEACON);
1257 if (NS_FAILED(rv)) {
1258 aRv.Throw(rv);
1259 return false;
1262 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
1263 if (!httpChannel) {
1264 // Beacon spec only supports HTTP requests at this time
1265 aRv.Throw(NS_ERROR_DOM_BAD_URI);
1266 return false;
1269 auto referrerInfo = MakeRefPtr<ReferrerInfo>(*doc);
1270 rv = httpChannel->SetReferrerInfoWithoutClone(referrerInfo);
1271 MOZ_ASSERT(NS_SUCCEEDED(rv));
1273 if (aBody) {
1274 nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(channel);
1275 if (!uploadChannel) {
1276 aRv.Throw(NS_ERROR_FAILURE);
1277 return false;
1280 uploadChannel->ExplicitSetUploadStream(in, contentTypeWithCharset, length,
1281 "POST"_ns, false);
1282 } else {
1283 rv = httpChannel->SetRequestMethod("POST"_ns);
1284 MOZ_ASSERT(NS_SUCCEEDED(rv));
1287 nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(channel);
1288 if (p) {
1289 p->SetPriority(nsISupportsPriority::PRIORITY_LOWEST);
1292 nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel));
1293 if (cos) {
1294 cos->AddClassFlags(nsIClassOfService::Background);
1297 // The channel needs to have a loadgroup associated with it, so that we can
1298 // cancel the channel and any redirected channels it may create.
1299 nsCOMPtr<nsILoadGroup> loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
1300 nsCOMPtr<nsIInterfaceRequestor> callbacks =
1301 do_QueryInterface(mWindow->GetDocShell());
1302 loadGroup->SetNotificationCallbacks(callbacks);
1303 channel->SetLoadGroup(loadGroup);
1305 RefPtr<BeaconStreamListener> beaconListener = new BeaconStreamListener();
1306 rv = channel->AsyncOpen(beaconListener);
1307 // do not throw if security checks fail within asyncOpen
1308 NS_ENSURE_SUCCESS(rv, false);
1310 // make the beaconListener hold a strong reference to the loadgroup
1311 // which is released in ::OnStartRequest
1312 beaconListener->SetLoadGroup(loadGroup);
1314 return true;
1317 MediaDevices* Navigator::GetMediaDevices(ErrorResult& aRv) {
1318 if (!mMediaDevices) {
1319 if (!mWindow || !mWindow->GetOuterWindow() ||
1320 mWindow->GetOuterWindow()->GetCurrentInnerWindow() != mWindow) {
1321 aRv.Throw(NS_ERROR_NOT_AVAILABLE);
1322 return nullptr;
1324 mMediaDevices = new MediaDevices(mWindow);
1326 return mMediaDevices;
1329 void Navigator::MozGetUserMedia(const MediaStreamConstraints& aConstraints,
1330 NavigatorUserMediaSuccessCallback& aOnSuccess,
1331 NavigatorUserMediaErrorCallback& aOnError,
1332 CallerType aCallerType, ErrorResult& aRv) {
1333 MOZ_ASSERT(NS_IsMainThread());
1334 if (!mWindow || !mWindow->IsFullyActive()) {
1335 aRv.ThrowInvalidStateError("The document is not fully active.");
1336 return;
1338 GetMediaDevices(aRv);
1339 if (aRv.Failed()) {
1340 return;
1342 MOZ_ASSERT(mMediaDevices);
1343 if (Document* doc = mWindow->GetExtantDoc()) {
1344 if (!mWindow->IsSecureContext()) {
1345 doc->SetUseCounter(eUseCounter_custom_MozGetUserMediaInsec);
1348 RefPtr<MediaManager::StreamPromise> sp;
1349 if (!MediaManager::IsOn(aConstraints.mVideo) &&
1350 !MediaManager::IsOn(aConstraints.mAudio)) {
1351 sp = MediaManager::StreamPromise::CreateAndReject(
1352 MakeRefPtr<MediaMgrError>(MediaMgrError::Name::TypeError,
1353 "audio and/or video is required"),
1354 __func__);
1355 } else {
1356 sp = mMediaDevices->GetUserMedia(mWindow, aConstraints, aCallerType);
1358 RefPtr<NavigatorUserMediaSuccessCallback> onsuccess(&aOnSuccess);
1359 RefPtr<NavigatorUserMediaErrorCallback> onerror(&aOnError);
1361 nsWeakPtr weakWindow = nsWeakPtr(do_GetWeakReference(mWindow));
1362 sp->Then(
1363 GetMainThreadSerialEventTarget(), __func__,
1364 [weakWindow, onsuccess = std::move(onsuccess)](
1365 const RefPtr<DOMMediaStream>& aStream) MOZ_CAN_RUN_SCRIPT {
1366 nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(weakWindow);
1367 if (!window || !window->GetOuterWindow() ||
1368 window->GetOuterWindow()->GetCurrentInnerWindow() != window) {
1369 return; // Leave Promise pending after navigation by design.
1371 MediaManager::CallOnSuccess(*onsuccess, *aStream);
1373 [weakWindow, onerror = std::move(onerror)](
1374 const RefPtr<MediaMgrError>& aError) MOZ_CAN_RUN_SCRIPT {
1375 nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(weakWindow);
1376 if (!window || !window->GetOuterWindow() ||
1377 window->GetOuterWindow()->GetCurrentInnerWindow() != window) {
1378 return; // Leave Promise pending after navigation by design.
1380 auto error = MakeRefPtr<MediaStreamError>(window, *aError);
1381 MediaManager::CallOnError(*onerror, *error);
1385 //*****************************************************************************
1386 // Navigator::nsINavigatorBattery
1387 //*****************************************************************************
1389 Promise* Navigator::GetBattery(ErrorResult& aRv) {
1390 if (mBatteryPromise) {
1391 return mBatteryPromise;
1394 if (!mWindow || !mWindow->GetDocShell()) {
1395 aRv.Throw(NS_ERROR_UNEXPECTED);
1396 return nullptr;
1399 RefPtr<Promise> batteryPromise = Promise::Create(mWindow->AsGlobal(), aRv);
1400 if (NS_WARN_IF(aRv.Failed())) {
1401 return nullptr;
1403 mBatteryPromise = batteryPromise;
1405 if (!mBatteryManager) {
1406 mBatteryManager = new battery::BatteryManager(mWindow);
1407 mBatteryManager->Init();
1410 mBatteryPromise->MaybeResolve(mBatteryManager);
1412 return mBatteryPromise;
1415 //*****************************************************************************
1416 // Navigator::Share() - Web Share API
1417 //*****************************************************************************
1419 already_AddRefed<Promise> Navigator::Share(const ShareData& aData,
1420 ErrorResult& aRv) {
1421 if (!mWindow || !mWindow->IsFullyActive()) {
1422 aRv.ThrowInvalidStateError("The document is not fully active.");
1423 return nullptr;
1426 if (NS_WARN_IF(!mWindow->GetDocShell() || !mWindow->GetExtantDoc())) {
1427 aRv.Throw(NS_ERROR_UNEXPECTED);
1428 return nullptr;
1431 if (!FeaturePolicyUtils::IsFeatureAllowed(mWindow->GetExtantDoc(),
1432 u"web-share"_ns)) {
1433 aRv.ThrowNotAllowedError(
1434 "Document's Permissions Policy does not allow calling "
1435 "share() from this context.");
1436 return nullptr;
1439 if (mSharePromise) {
1440 NS_WARNING("Only one share picker at a time per navigator instance");
1441 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1442 return nullptr;
1445 // null checked above
1446 Document* doc = mWindow->GetExtantDoc();
1448 if (StaticPrefs::dom_webshare_requireinteraction() &&
1449 !doc->ConsumeTransientUserGestureActivation()) {
1450 aRv.ThrowNotAllowedError(
1451 "User activation was already consumed "
1452 "or share() was not activated by a user gesture.");
1453 return nullptr;
1456 ValidateShareData(aData, aRv);
1458 if (aRv.Failed()) {
1459 return nullptr;
1462 // TODO: Process file member, which we don't currently support.
1464 // If data's url member is present, try to resolve it...
1465 nsCOMPtr<nsIURI> url;
1466 if (aData.mUrl.WasPassed()) {
1467 auto result = doc->ResolveWithBaseURI(aData.mUrl.Value());
1468 url = result.unwrap();
1469 MOZ_ASSERT(url);
1472 // Process the title member...
1473 nsCString title;
1474 if (aData.mTitle.WasPassed()) {
1475 title.Assign(NS_ConvertUTF16toUTF8(aData.mTitle.Value()));
1476 } else {
1477 title.SetIsVoid(true);
1480 // Process the text member...
1481 nsCString text;
1482 if (aData.mText.WasPassed()) {
1483 text.Assign(NS_ConvertUTF16toUTF8(aData.mText.Value()));
1484 } else {
1485 text.SetIsVoid(true);
1488 // Let mSharePromise be a new promise.
1489 mSharePromise = Promise::Create(mWindow->AsGlobal(), aRv);
1490 if (aRv.Failed()) {
1491 return nullptr;
1494 IPCWebShareData data(title, text, url);
1495 auto wgc = mWindow->GetWindowGlobalChild();
1496 if (!wgc) {
1497 aRv.Throw(NS_ERROR_FAILURE);
1498 return nullptr;
1501 // Do the share
1502 wgc->SendShare(data)->Then(
1503 GetCurrentSerialEventTarget(), __func__,
1504 [self = RefPtr{this}](
1505 PWindowGlobalChild::SharePromise::ResolveOrRejectValue&& aResult) {
1506 if (aResult.IsResolve()) {
1507 if (NS_SUCCEEDED(aResult.ResolveValue())) {
1508 self->mSharePromise->MaybeResolveWithUndefined();
1509 } else {
1510 self->mSharePromise->MaybeReject(aResult.ResolveValue());
1512 } else if (self->mSharePromise) {
1513 // IPC died
1514 self->mSharePromise->MaybeReject(NS_BINDING_ABORTED);
1516 self->mSharePromise = nullptr;
1518 return do_AddRef(mSharePromise);
1521 //*****************************************************************************
1522 // Navigator::CanShare() - Web Share API
1523 //*****************************************************************************
1524 bool Navigator::CanShare(const ShareData& aData) {
1525 if (!mWindow || !mWindow->IsFullyActive()) {
1526 return false;
1529 if (!FeaturePolicyUtils::IsFeatureAllowed(mWindow->GetExtantDoc(),
1530 u"web-share"_ns)) {
1531 return false;
1534 IgnoredErrorResult rv;
1535 ValidateShareData(aData, rv);
1536 return !rv.Failed();
1539 void Navigator::ValidateShareData(const ShareData& aData, ErrorResult& aRv) {
1540 // TODO: remove this check when we support files share.
1541 if (aData.mFiles.WasPassed() && !aData.mFiles.Value().IsEmpty()) {
1542 aRv.ThrowTypeError("Passing files is currently not supported.");
1543 return;
1546 bool titleTextOrUrlPassed = aData.mTitle.WasPassed() ||
1547 aData.mText.WasPassed() || aData.mUrl.WasPassed();
1549 // At least one member must be present.
1550 if (!titleTextOrUrlPassed) {
1551 aRv.ThrowTypeError(
1552 "Must have a title, text, or url member in the ShareData dictionary");
1553 return;
1556 // If data's url member is present, try to resolve it...
1557 nsCOMPtr<nsIURI> url;
1558 if (aData.mUrl.WasPassed()) {
1559 Document* doc = mWindow->GetExtantDoc();
1560 Result<OwningNonNull<nsIURI>, nsresult> result =
1561 doc->ResolveWithBaseURI(aData.mUrl.Value());
1562 if (NS_WARN_IF(result.isErr())) {
1563 aRv.ThrowTypeError<MSG_INVALID_URL>(
1564 NS_ConvertUTF16toUTF8(aData.mUrl.Value()));
1565 return;
1567 url = result.unwrap();
1568 // Check that we only share loadable URLs (e.g., http/https).
1569 // we also exclude blobs, as it doesn't make sense to share those outside
1570 // the context of the browser.
1571 const uint32_t flags =
1572 nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL |
1573 nsIScriptSecurityManager::DISALLOW_SCRIPT;
1574 if (NS_FAILED(
1575 nsContentUtils::GetSecurityManager()->CheckLoadURIWithPrincipal(
1576 doc->NodePrincipal(), url, flags, doc->InnerWindowID())) ||
1577 url->SchemeIs("blob")) {
1578 aRv.ThrowTypeError<MSG_INVALID_URL_SCHEME>("Share",
1579 url->GetSpecOrDefault());
1580 return;
1585 static bool ShouldResistFingerprinting(const Document* aDoc,
1586 RFPTarget aTarget) {
1587 return aDoc ? aDoc->ShouldResistFingerprinting(aTarget)
1588 : nsContentUtils::ShouldResistFingerprinting("Fallback", aTarget);
1591 already_AddRefed<LegacyMozTCPSocket> Navigator::MozTCPSocket() {
1592 RefPtr<LegacyMozTCPSocket> socket = new LegacyMozTCPSocket(GetWindow());
1593 return socket.forget();
1596 void Navigator::GetGamepads(nsTArray<RefPtr<Gamepad>>& aGamepads,
1597 ErrorResult& aRv) {
1598 if (!mWindow || !mWindow->IsFullyActive()) {
1599 return;
1601 NS_ENSURE_TRUE_VOID(mWindow->GetDocShell());
1602 nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow);
1604 if (!FeaturePolicyUtils::IsFeatureAllowed(win->GetExtantDoc(),
1605 u"gamepad"_ns)) {
1606 aRv.ThrowSecurityError(
1607 "Document's Permission Policy does not allow calling "
1608 "getGamepads() from this context.");
1609 return;
1612 win->SetHasGamepadEventListener(true);
1613 win->GetGamepads(aGamepads);
1616 GamepadServiceTest* Navigator::RequestGamepadServiceTest(ErrorResult& aRv) {
1617 #ifdef FUZZING
1618 if (!StaticPrefs::fuzzing_enabled()) {
1619 aRv.Throw(NS_ERROR_UNEXPECTED);
1620 return nullptr;
1622 #else
1623 if (!xpc::IsInAutomation()) {
1624 aRv.Throw(NS_ERROR_UNEXPECTED);
1625 return nullptr;
1627 #endif
1629 if (!mGamepadServiceTest) {
1630 mGamepadServiceTest = GamepadServiceTest::CreateTestService(mWindow);
1632 return mGamepadServiceTest;
1635 already_AddRefed<Promise> Navigator::GetVRDisplays(ErrorResult& aRv) {
1636 if (!mWindow || !mWindow->GetDocShell() || !mWindow->GetExtantDoc()) {
1637 aRv.Throw(NS_ERROR_UNEXPECTED);
1638 return nullptr;
1641 if (!FeaturePolicyUtils::IsFeatureAllowed(mWindow->GetExtantDoc(),
1642 u"vr"_ns)) {
1643 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
1644 return nullptr;
1647 RefPtr<Promise> p = Promise::Create(mWindow->AsGlobal(), aRv);
1648 if (aRv.Failed()) {
1649 return nullptr;
1652 RefPtr<BrowserChild> browser(BrowserChild::GetFrom(mWindow));
1653 if (!browser) {
1654 MOZ_ASSERT(XRE_IsParentProcess());
1655 FinishGetVRDisplays(true, p);
1656 } else {
1657 RefPtr<Navigator> self(this);
1658 int browserID = browser->ChromeOuterWindowID();
1660 browser->SendIsWindowSupportingWebVR(browserID)->Then(
1661 GetCurrentSerialEventTarget(), __func__,
1662 [self, p](bool isSupported) {
1663 self->FinishGetVRDisplays(isSupported, p);
1665 [p](const mozilla::ipc::ResponseRejectReason) {
1666 p->MaybeRejectWithTypeError("Unable to start display enumeration");
1670 return p.forget();
1673 void Navigator::FinishGetVRDisplays(bool isWebVRSupportedInwindow, Promise* p) {
1674 if (!isWebVRSupportedInwindow) {
1675 // WebVR in this window is not supported, so resolve the promise
1676 // with no displays available
1677 nsTArray<RefPtr<VRDisplay>> vrDisplaysEmpty;
1678 p->MaybeResolve(vrDisplaysEmpty);
1679 return;
1682 // Since FinishGetVRDisplays can be called asynchronously after an IPC
1683 // response, it's possible that the Window can be torn down before this
1684 // call. In that case, the Window's cyclic references to VR objects are
1685 // also torn down and should not be recreated via
1686 // NotifyHasXRSession.
1687 nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow);
1688 if (win->IsDying()) {
1689 // The Window has been torn down, so there is no further work that can
1690 // be done.
1691 p->MaybeRejectWithTypeError(
1692 "Unable to return VRDisplays for a closed window.");
1693 return;
1696 mVRGetDisplaysPromises.AppendElement(p);
1697 win->RequestXRPermission();
1700 void Navigator::OnXRPermissionRequestAllow() {
1701 // The permission request that results in this callback could have
1702 // been instantiated by WebVR, WebXR, or both.
1703 nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow);
1704 bool usingWebXR = false;
1706 if (mXRSystem) {
1707 usingWebXR = mXRSystem->OnXRPermissionRequestAllow();
1710 bool rejectWebVR = true;
1711 // If WebVR and WebXR both requested permission, only grant it to
1712 // WebXR, which takes priority.
1713 if (!usingWebXR) {
1714 // We pass mWindow's id to RefreshVRDisplays, so NotifyVRDisplaysUpdated
1715 // will be called asynchronously, resolving the promises in
1716 // mVRGetDisplaysPromises.
1717 rejectWebVR = !VRDisplay::RefreshVRDisplays(win->WindowID());
1719 // Even if WebXR took priority, reject requests for WebVR in case they were
1720 // made simultaneously and coelesced into a single permission prompt.
1721 if (rejectWebVR) {
1722 for (auto& p : mVRGetDisplaysPromises) {
1723 // Failed to refresh, reject the promise now
1724 p->MaybeRejectWithTypeError("Failed to find attached VR displays.");
1726 mVRGetDisplaysPromises.Clear();
1730 void Navigator::OnXRPermissionRequestCancel() {
1731 if (mXRSystem) {
1732 mXRSystem->OnXRPermissionRequestCancel();
1735 nsTArray<RefPtr<VRDisplay>> vrDisplays;
1736 for (auto& p : mVRGetDisplaysPromises) {
1737 // Resolve the promise with no vr displays when
1738 // the user blocks access.
1739 p->MaybeResolve(vrDisplays);
1741 mVRGetDisplaysPromises.Clear();
1744 void Navigator::GetActiveVRDisplays(
1745 nsTArray<RefPtr<VRDisplay>>& aDisplays) const {
1747 * Get only the active VR displays.
1748 * GetActiveVRDisplays should only enumerate displays that
1749 * are already active without causing any other hardware to be
1750 * activated.
1751 * We must not call nsGlobalWindowInner::NotifyHasXRSession here,
1752 * as that would cause enumeration and activation of other VR hardware.
1753 * Activating VR hardware is intrusive to the end user, as it may
1754 * involve physically powering on devices that the user did not
1755 * intend to use.
1757 if (!mWindow || !mWindow->GetDocShell()) {
1758 return;
1760 nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow);
1761 nsTArray<RefPtr<VRDisplay>> displays;
1762 if (win->UpdateVRDisplays(displays)) {
1763 for (auto display : displays) {
1764 if (display->IsPresenting()) {
1765 aDisplays.AppendElement(display);
1771 void Navigator::NotifyVRDisplaysUpdated() {
1772 // Synchronize the VR devices and resolve the promises in
1773 // mVRGetDisplaysPromises
1774 nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow);
1776 nsTArray<RefPtr<VRDisplay>> vrDisplays;
1777 if (win->UpdateVRDisplays(vrDisplays)) {
1778 for (auto p : mVRGetDisplaysPromises) {
1779 p->MaybeResolve(vrDisplays);
1781 } else {
1782 for (auto p : mVRGetDisplaysPromises) {
1783 p->MaybeReject(NS_ERROR_FAILURE);
1786 mVRGetDisplaysPromises.Clear();
1789 void Navigator::NotifyActiveVRDisplaysChanged() {
1790 Navigator_Binding::ClearCachedActiveVRDisplaysValue(this);
1793 VRServiceTest* Navigator::RequestVRServiceTest(ErrorResult& aRv) {
1794 if (!xpc::IsInAutomation()) {
1795 aRv.Throw(NS_ERROR_UNEXPECTED);
1796 return nullptr;
1799 // Ensure that the Mock VR devices are not released prematurely
1800 nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow);
1801 win->NotifyHasXRSession();
1803 if (!mVRServiceTest) {
1804 mVRServiceTest = VRServiceTest::CreateTestService(mWindow);
1806 return mVRServiceTest;
1809 XRSystem* Navigator::GetXr(ErrorResult& aRv) {
1810 if (!mWindow) {
1811 aRv.Throw(NS_ERROR_UNEXPECTED);
1812 return nullptr;
1814 if (!mXRSystem) {
1815 mXRSystem = XRSystem::Create(mWindow);
1817 return mXRSystem;
1820 bool Navigator::IsWebVRContentDetected() const {
1821 nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow);
1822 return win->IsVRContentDetected();
1825 bool Navigator::IsWebVRContentPresenting() const {
1826 nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow);
1827 return win->IsVRContentPresenting();
1830 void Navigator::RequestVRPresentation(VRDisplay& aDisplay) {
1831 nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow);
1832 win->DispatchVRDisplayActivate(aDisplay.DisplayId(),
1833 VRDisplayEventReason::Requested);
1836 already_AddRefed<Promise> Navigator::RequestMIDIAccess(
1837 const MIDIOptions& aOptions, ErrorResult& aRv) {
1838 if (!mWindow) {
1839 aRv.Throw(NS_ERROR_UNEXPECTED);
1840 return nullptr;
1842 MIDIAccessManager* accessMgr = MIDIAccessManager::Get();
1843 return accessMgr->RequestMIDIAccess(mWindow, aOptions, aRv);
1846 network::Connection* Navigator::GetConnection(ErrorResult& aRv) {
1847 if (!mConnection) {
1848 if (!mWindow) {
1849 aRv.Throw(NS_ERROR_UNEXPECTED);
1850 return nullptr;
1852 mConnection = network::Connection::CreateForWindow(
1853 mWindow, nsGlobalWindowInner::Cast(mWindow)->ShouldResistFingerprinting(
1854 RFPTarget::NavigatorConnection));
1857 return mConnection;
1860 already_AddRefed<ServiceWorkerContainer> Navigator::ServiceWorker() {
1861 MOZ_ASSERT(mWindow);
1863 if (!mServiceWorkerContainer) {
1864 mServiceWorkerContainer =
1865 ServiceWorkerContainer::Create(mWindow->AsGlobal());
1868 RefPtr<ServiceWorkerContainer> ref = mServiceWorkerContainer;
1869 return ref.forget();
1872 already_AddRefed<ServiceWorkerContainer> Navigator::ServiceWorkerJS() {
1873 if (mWindow->AsGlobal()->GetStorageAccess() ==
1874 StorageAccess::ePrivateBrowsing) {
1875 SetUseCounter(mWindow->AsGlobal()->GetGlobalJSObject(),
1876 eUseCounter_custom_PrivateBrowsingNavigatorServiceWorker);
1879 return ServiceWorker();
1882 size_t Navigator::SizeOfIncludingThis(
1883 mozilla::MallocSizeOf aMallocSizeOf) const {
1884 size_t n = aMallocSizeOf(this);
1886 // TODO: add SizeOfIncludingThis() to nsMimeTypeArray, bug 674113.
1887 // TODO: add SizeOfIncludingThis() to nsPluginArray, bug 674114.
1888 // TODO: add SizeOfIncludingThis() to Geolocation, bug 674115.
1889 // TODO: add SizeOfIncludingThis() to DesktopNotificationCenter, bug 674116.
1891 return n;
1894 void Navigator::OnNavigation() {
1895 if (!mWindow) {
1896 return;
1899 // If MediaManager is open let it inform any live streams or pending callbacks
1900 MediaManager* manager = MediaManager::GetIfExists();
1901 if (manager) {
1902 manager->OnNavigation(mWindow->WindowID());
1906 JSObject* Navigator::WrapObject(JSContext* cx,
1907 JS::Handle<JSObject*> aGivenProto) {
1908 return Navigator_Binding::Wrap(cx, this, aGivenProto);
1911 /* static */
1912 bool Navigator::HasUserMediaSupport(JSContext* cx, JSObject* obj) {
1913 // Make enabling peerconnection enable getUserMedia() as well.
1914 // Emulate [SecureContext] unless media.devices.insecure.enabled=true
1915 return (StaticPrefs::media_navigator_enabled() ||
1916 StaticPrefs::media_peerconnection_enabled()) &&
1917 (IsSecureContextOrObjectIsFromSecureContext(cx, obj) ||
1918 StaticPrefs::media_devices_insecure_enabled());
1921 /* static */
1922 bool Navigator::HasShareSupport(JSContext* cx, JSObject* obj) {
1923 if (!StaticPrefs::dom_webshare_enabled()) {
1924 return false;
1926 #if defined(XP_WIN) && !defined(__MINGW32__)
1927 // The first public build that supports ShareCanceled API
1928 return IsWindows10BuildOrLater(18956);
1929 #else
1930 return true;
1931 #endif
1934 /* static */
1935 bool Navigator::HasMidiSupport(JSContext* cx, JSObject* obj) {
1936 nsIPrincipal* principal = nsContentUtils::SubjectPrincipal(cx);
1938 // Enable on secure contexts but exclude file schemes.
1939 return StaticPrefs::dom_webmidi_enabled() &&
1940 IsSecureContextOrObjectIsFromSecureContext(cx, obj) &&
1941 !principal->SchemeIs("file");
1944 /* static */
1945 already_AddRefed<nsPIDOMWindowInner> Navigator::GetWindowFromGlobal(
1946 JSObject* aGlobal) {
1947 nsCOMPtr<nsPIDOMWindowInner> win = xpc::WindowOrNull(aGlobal);
1948 return win.forget();
1951 void Navigator::ClearPlatformCache() {
1952 Navigator_Binding::ClearCachedPlatformValue(this);
1955 nsresult Navigator::GetPlatform(nsAString& aPlatform, Document* aCallerDoc,
1956 bool aUsePrefOverriddenValue) {
1957 MOZ_ASSERT(NS_IsMainThread());
1959 if (aUsePrefOverriddenValue) {
1960 // If fingerprinting resistance is on, we will spoof this value. See
1961 // nsRFPService.h for details about spoofed values.
1962 if (ShouldResistFingerprinting(aCallerDoc, RFPTarget::NavigatorPlatform)) {
1963 aPlatform.AssignLiteral(SPOOFED_PLATFORM);
1964 return NS_OK;
1966 nsAutoString override;
1967 nsresult rv =
1968 mozilla::Preferences::GetString("general.platform.override", override);
1970 if (NS_SUCCEEDED(rv)) {
1971 aPlatform = override;
1972 return NS_OK;
1976 #if defined(WIN32)
1977 aPlatform.AssignLiteral("Win32");
1978 #elif defined(XP_MACOSX)
1979 // Always return "MacIntel", even on ARM64 macOS like Safari does.
1980 aPlatform.AssignLiteral("MacIntel");
1981 #else
1982 nsresult rv;
1983 nsCOMPtr<nsIHttpProtocolHandler> service(
1984 do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
1985 NS_ENSURE_SUCCESS(rv, rv);
1987 nsAutoCString plat;
1988 rv = service->GetOscpu(plat);
1989 NS_ENSURE_SUCCESS(rv, rv);
1991 CopyASCIItoUTF16(plat, aPlatform);
1992 #endif
1994 return NS_OK;
1997 /* static */
1998 nsresult Navigator::GetAppVersion(nsAString& aAppVersion, Document* aCallerDoc,
1999 bool aUsePrefOverriddenValue) {
2000 MOZ_ASSERT(NS_IsMainThread());
2002 if (aUsePrefOverriddenValue) {
2003 // If fingerprinting resistance is on, we will spoof this value. See
2004 // nsRFPService.h for details about spoofed values.
2005 if (ShouldResistFingerprinting(aCallerDoc,
2006 RFPTarget::NavigatorAppVersion)) {
2007 aAppVersion.AssignLiteral(SPOOFED_APPVERSION);
2008 return NS_OK;
2010 nsAutoString override;
2011 nsresult rv = mozilla::Preferences::GetString("general.appversion.override",
2012 override);
2014 if (NS_SUCCEEDED(rv)) {
2015 aAppVersion = override;
2016 return NS_OK;
2020 nsresult rv;
2022 nsCOMPtr<nsIHttpProtocolHandler> service(
2023 do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
2024 NS_ENSURE_SUCCESS(rv, rv);
2026 nsAutoCString str;
2027 rv = service->GetAppVersion(str);
2028 CopyASCIItoUTF16(str, aAppVersion);
2029 NS_ENSURE_SUCCESS(rv, rv);
2031 aAppVersion.AppendLiteral(" (");
2033 rv = service->GetPlatform(str);
2034 NS_ENSURE_SUCCESS(rv, rv);
2036 AppendASCIItoUTF16(str, aAppVersion);
2037 aAppVersion.Append(char16_t(')'));
2039 return rv;
2042 void Navigator::ClearUserAgentCache() {
2043 Navigator_Binding::ClearCachedUserAgentValue(this);
2046 nsresult Navigator::GetUserAgent(nsPIDOMWindowInner* aWindow,
2047 Document* aCallerDoc,
2048 Maybe<bool> aShouldResistFingerprinting,
2049 nsAString& aUserAgent) {
2050 MOZ_ASSERT(NS_IsMainThread());
2053 ResistFingerprinting is migrating to fine-grained control based off
2054 either a channel or Principal+OriginAttributes
2056 This function can be called from Workers, Main Thread, and at least one
2057 other (unusual) case.
2059 For Main Thread, we will generally have a window and an associated
2060 Document, for Workers we will not.
2062 If aShouldResistFingerprinting is provided, we should respect it.
2063 If it is not provided, we will use aCallerDoc to determine our behavior.
2066 bool shouldResistFingerprinting =
2067 aShouldResistFingerprinting.isSome()
2068 ? aShouldResistFingerprinting.value()
2069 : ShouldResistFingerprinting(aCallerDoc,
2070 RFPTarget::NavigatorUserAgent);
2072 // We will skip the override and pass to httpHandler to get spoofed userAgent
2073 // when 'privacy.resistFingerprinting' is true.
2074 if (!shouldResistFingerprinting) {
2075 nsAutoString override;
2076 nsresult rv =
2077 mozilla::Preferences::GetString("general.useragent.override", override);
2079 if (NS_SUCCEEDED(rv)) {
2080 aUserAgent = override;
2081 return NS_OK;
2085 // When the caller is content and 'privacy.resistFingerprinting' is true,
2086 // return a spoofed userAgent which reveals the platform but not the
2087 // specific OS version, etc.
2088 if (shouldResistFingerprinting) {
2089 nsAutoCString spoofedUA;
2090 nsRFPService::GetSpoofedUserAgent(spoofedUA, false);
2091 CopyASCIItoUTF16(spoofedUA, aUserAgent);
2092 return NS_OK;
2095 nsresult rv;
2096 nsCOMPtr<nsIHttpProtocolHandler> service(
2097 do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
2098 if (NS_WARN_IF(NS_FAILED(rv))) {
2099 return rv;
2102 nsAutoCString ua;
2103 rv = service->GetUserAgent(ua);
2104 if (NS_WARN_IF(NS_FAILED(rv))) {
2105 return rv;
2108 CopyASCIItoUTF16(ua, aUserAgent);
2110 if (!aWindow) {
2111 return NS_OK;
2114 // Copy the User-Agent header from the document channel which has already been
2115 // subject to UA overrides.
2116 nsCOMPtr<Document> doc = aWindow->GetExtantDoc();
2117 if (!doc) {
2118 return NS_OK;
2120 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(doc->GetChannel());
2121 if (httpChannel) {
2122 nsAutoCString userAgent;
2123 rv = httpChannel->GetRequestHeader("User-Agent"_ns, userAgent);
2124 if (NS_WARN_IF(NS_FAILED(rv))) {
2125 return rv;
2127 CopyASCIItoUTF16(userAgent, aUserAgent);
2129 return NS_OK;
2132 static nsCString RequestKeySystemAccessLogString(
2133 const nsAString& aKeySystem,
2134 const Sequence<MediaKeySystemConfiguration>& aConfigs,
2135 bool aIsSecureContext) {
2136 nsCString str;
2137 str.AppendPrintf(
2138 "Navigator::RequestMediaKeySystemAccess(keySystem='%s' options=",
2139 NS_ConvertUTF16toUTF8(aKeySystem).get());
2140 str.Append(MediaKeySystemAccess::ToCString(aConfigs));
2141 str.AppendLiteral(") secureContext=");
2142 str.AppendInt(aIsSecureContext);
2143 return str;
2146 already_AddRefed<Promise> Navigator::RequestMediaKeySystemAccess(
2147 const nsAString& aKeySystem,
2148 const Sequence<MediaKeySystemConfiguration>& aConfigs, ErrorResult& aRv) {
2149 EME_LOG("%s", RequestKeySystemAccessLogString(aKeySystem, aConfigs,
2150 mWindow->IsSecureContext())
2151 .get());
2153 if (!mWindow->IsSecureContext()) {
2154 Document* doc = mWindow->GetExtantDoc();
2155 AutoTArray<nsString, 1> params;
2156 nsString* uri = params.AppendElement();
2157 if (doc) {
2158 Unused << doc->GetDocumentURI(*uri);
2160 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "Media"_ns,
2161 doc, nsContentUtils::eDOM_PROPERTIES,
2162 "MediaEMEInsecureContextDeprecatedWarning",
2163 params);
2166 Document* doc = mWindow->GetExtantDoc();
2167 if (doc &&
2168 !FeaturePolicyUtils::IsFeatureAllowed(doc, u"encrypted-media"_ns)) {
2169 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
2170 return nullptr;
2173 RefPtr<DetailedPromise> promise = DetailedPromise::Create(
2174 mWindow->AsGlobal(), aRv, "navigator.requestMediaKeySystemAccess"_ns);
2175 if (aRv.Failed()) {
2176 return nullptr;
2179 if (!mMediaKeySystemAccessManager) {
2180 mMediaKeySystemAccessManager = new MediaKeySystemAccessManager(mWindow);
2183 mMediaKeySystemAccessManager->Request(promise, aKeySystem, aConfigs);
2184 return promise.forget();
2187 CredentialsContainer* Navigator::Credentials() {
2188 if (!mCredentials) {
2189 mCredentials = new CredentialsContainer(GetWindow());
2191 return mCredentials;
2194 dom::MediaCapabilities* Navigator::MediaCapabilities() {
2195 if (!mMediaCapabilities) {
2196 mMediaCapabilities = new dom::MediaCapabilities(GetWindow()->AsGlobal());
2198 return mMediaCapabilities;
2201 dom::MediaSession* Navigator::MediaSession() {
2202 if (!mMediaSession) {
2203 mMediaSession = new dom::MediaSession(GetWindow());
2205 return mMediaSession;
2208 bool Navigator::HasCreatedMediaSession() const {
2209 return mMediaSession != nullptr;
2212 Clipboard* Navigator::Clipboard() {
2213 if (!mClipboard) {
2214 mClipboard = new dom::Clipboard(GetWindow());
2216 return mClipboard;
2219 AddonManager* Navigator::GetMozAddonManager(ErrorResult& aRv) {
2220 if (!mAddonManager) {
2221 nsPIDOMWindowInner* win = GetWindow();
2222 if (!win) {
2223 aRv.Throw(NS_ERROR_UNEXPECTED);
2224 return nullptr;
2227 mAddonManager = ConstructJSImplementation<AddonManager>(
2228 "@mozilla.org/addon-web-api/manager;1", win->AsGlobal(), aRv);
2229 if (aRv.Failed()) {
2230 return nullptr;
2234 return mAddonManager;
2237 webgpu::Instance* Navigator::Gpu() {
2238 if (!mWebGpu) {
2239 mWebGpu = webgpu::Instance::Create(GetWindow()->AsGlobal());
2241 return mWebGpu;
2244 dom::LockManager* Navigator::Locks() {
2245 if (!mLocks) {
2246 mLocks = dom::LockManager::Create(*GetWindow()->AsGlobal());
2248 return mLocks;
2251 /* static */
2252 bool Navigator::Webdriver() {
2253 #ifdef ENABLE_WEBDRIVER
2254 nsCOMPtr<nsIMarionette> marionette = do_GetService(NS_MARIONETTE_CONTRACTID);
2255 if (marionette) {
2256 bool marionetteRunning = false;
2257 marionette->GetRunning(&marionetteRunning);
2258 if (marionetteRunning) {
2259 return true;
2263 nsCOMPtr<nsIRemoteAgent> agent = do_GetService(NS_REMOTEAGENT_CONTRACTID);
2264 if (agent) {
2265 bool remoteAgentRunning = false;
2266 agent->GetRunning(&remoteAgentRunning);
2267 if (remoteAgentRunning) {
2268 return true;
2271 #endif
2273 return false;
2276 AutoplayPolicy Navigator::GetAutoplayPolicy(AutoplayPolicyMediaType aType) {
2277 if (!mWindow) {
2278 return AutoplayPolicy::Disallowed;
2280 nsCOMPtr<Document> doc = mWindow->GetExtantDoc();
2281 if (!doc) {
2282 return AutoplayPolicy::Disallowed;
2284 return media::AutoplayPolicy::GetAutoplayPolicy(aType, *doc);
2287 AutoplayPolicy Navigator::GetAutoplayPolicy(HTMLMediaElement& aElement) {
2288 return media::AutoplayPolicy::GetAutoplayPolicy(aElement);
2291 AutoplayPolicy Navigator::GetAutoplayPolicy(AudioContext& aContext) {
2292 return media::AutoplayPolicy::GetAutoplayPolicy(aContext);
2295 already_AddRefed<dom::UserActivation> Navigator::UserActivation() {
2296 if (!mUserActivation) {
2297 mUserActivation = new dom::UserActivation(GetWindow());
2299 return do_AddRef(mUserActivation);
2302 } // namespace mozilla::dom