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