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