Navigator.cpp 59.9 KB
Newer Older
1
2
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
4
5
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
8
9
10
11
12
13

// Needs to be first.
#include "base/basictypes.h"

#include "Navigator.h"
#include "nsIXULAppInfo.h"
#include "nsPluginArray.h"
#include "nsMimeTypeArray.h"
14
#include "mozilla/AntiTrackingCommon.h"
15
#include "mozilla/MemoryReporting.h"
16
17
#include "mozilla/dom/BodyExtractor.h"
#include "mozilla/dom/FetchBinding.h"
18
#include "mozilla/dom/File.h"
19
#include "nsGeolocation.h"
20
#include "nsIClassOfService.h"
21
#include "nsIHttpProtocolHandler.h"
22
23
24
25
#include "nsIContentPolicy.h"
#include "nsIContentSecurityPolicy.h"
#include "nsContentPolicyUtils.h"
#include "nsISupportsPriority.h"
26
#include "nsICachingChannel.h"
27
#include "nsIWebProtocolHandlerRegistrar.h"
28
29
30
31
32
33
#include "nsICookiePermission.h"
#include "nsIScriptSecurityManager.h"
#include "nsCharSeparatedTokenizer.h"
#include "nsContentUtils.h"
#include "nsUnicharUtils.h"
#include "mozilla/Preferences.h"
34
35
36
#include "mozilla/StaticPrefs_media.h"
#include "mozilla/StaticPrefs_network.h"
#include "mozilla/StaticPrefs_privacy.h"
37
#include "mozilla/Telemetry.h"
38
#include "BatteryManager.h"
39
#include "mozilla/dom/CredentialsContainer.h"
40
#include "mozilla/dom/Clipboard.h"
41
#include "mozilla/dom/FeaturePolicyUtils.h"
42
#include "mozilla/dom/GamepadServiceTest.h"
43
#include "mozilla/dom/MediaCapabilities.h"
44
45
#include "mozilla/dom/WakeLock.h"
#include "mozilla/dom/power/PowerManagerService.h"
46
47
#include "mozilla/dom/MIDIAccessManager.h"
#include "mozilla/dom/MIDIOptionsBinding.h"
48
#include "mozilla/dom/Permissions.h"
49
#include "mozilla/dom/Presentation.h"
50
#include "mozilla/dom/ServiceWorkerContainer.h"
51
#include "mozilla/dom/StorageManager.h"
52
#include "mozilla/dom/TCPSocket.h"
53
#include "mozilla/dom/URLSearchParams.h"
54
#include "mozilla/dom/VRDisplay.h"
55
#include "mozilla/dom/VRDisplayEvent.h"
56
#include "mozilla/dom/VRServiceTest.h"
57
#include "mozilla/dom/workerinternals/RuntimeService.h"
58
59
#include "mozilla/Hal.h"
#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
69
#include "nsStringStream.h"
#include "nsComponentManagerUtils.h"
70
#include "nsICookieService.h"
71
#include "nsIStringStream.h"
72
#include "nsIHttpChannel.h"
73
#include "nsIHttpChannelInternal.h"
74
#include "nsStreamUtils.h"
75
#include "WidgetUtils.h"
76
#include "nsIPresentationService.h"
77
#include "nsIScriptError.h"
78
#include "ReferrerInfo.h"
79

80
#include "nsIExternalProtocolHandler.h"
81
#include "BrowserChild.h"
82
83
#include "URIUtils.h"

84
#include "mozilla/dom/MediaDevices.h"
85
#include "MediaManager.h"
86

87
#include "nsIDOMGlobalPropertyInitializer.h"
88
89
90
#include "nsJSUtils.h"

#include "mozilla/dom/NavigatorBinding.h"
91
#include "mozilla/dom/Promise.h"
92

93
#include "nsIUploadChannel2.h"
94
#include "mozilla/dom/FormData.h"
95
#include "nsIDocShell.h"
96

97
98
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/WorkerRunnable.h"
99

100
#if defined(XP_LINUX)
101
#  include "mozilla/Hal.h"
102
103
#endif

104
#include "mozilla/EMEUtils.h"
105
#include "mozilla/DetailedPromise.h"
106
#include "mozilla/Unused.h"
107

108
#include "mozilla/webgpu/Instance.h"
109
#include "mozilla/dom/WindowGlobalChild.h"
110

111
112
113
namespace mozilla {
namespace dom {

114
115
static bool sVibratorEnabled = false;
static uint32_t sMaxVibrateMS = 0;
116
static uint32_t sMaxVibrateListLen = 0;
117
118
static const nsLiteralCString kVibrationPermissionType =
    NS_LITERAL_CSTRING("vibration");
119
120

/* static */
121
122
123
124
void Navigator::Init() {
  Preferences::AddBoolVarCache(&sVibratorEnabled, "dom.vibrator.enabled", true);
  Preferences::AddUintVarCache(&sMaxVibrateMS, "dom.vibrator.max_vibrate_ms",
                               10000);
125
126
  Preferences::AddUintVarCache(&sMaxVibrateListLen,
                               "dom.vibrator.max_vibrate_list_len", 128);
127
128
}

129
Navigator::Navigator(nsPIDOMWindowInner* aWindow) : mWindow(aWindow) {}
130

131
Navigator::~Navigator() { Invalidate(); }
132

133
134
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Navigator)
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
135
  NS_INTERFACE_MAP_ENTRY(nsISupports)
136
137
NS_INTERFACE_MAP_END

138
139
140
NS_IMPL_CYCLE_COLLECTING_ADDREF(Navigator)
NS_IMPL_CYCLE_COLLECTING_RELEASE(Navigator)

141
142
NS_IMPL_CYCLE_COLLECTION_CLASS(Navigator)

143
144
145
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Navigator)
  tmp->Invalidate();
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
146
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSharePromise)
147
148
149
150
  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END

NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Navigator)
151
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMimeTypes)
152
153
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPlugins)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPermissions)
154
155
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGeolocation)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBatteryManager)
156
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBatteryPromise)
157
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConnection)
158
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStorageManager)
159
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCredentials)
160
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaDevices)
161
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServiceWorkerContainer)
162
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaCapabilities)
163
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAddonManager)
164
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebGpu)
165
166

  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
167
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaKeySystemAccessManager)
168
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPresentation)
169
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepadServiceTest)
170
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRGetDisplaysPromises)
171
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRServiceTest)
172
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSharePromise)
173
174
175
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END

NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(Navigator)
176

177
void Navigator::Invalidate() {
178
179
  // Don't clear mWindow here so we know we've got a non-null mWindow
  // until we're unlinked.
180

181
182
  mMimeTypes = nullptr;

183
  if (mPlugins) {
184
    mPlugins->Invalidate();
185
    mPlugins = nullptr;
186
187
  }

188
  mPermissions = nullptr;
189

190
191
  mStorageManager = nullptr;

192
193
194
  // If there is a page transition, make sure delete the geolocation object.
  if (mGeolocation) {
    mGeolocation->Shutdown();
195
    mGeolocation = nullptr;
196
197
  }

198
199
  if (mBatteryManager) {
    mBatteryManager->Shutdown();
200
    mBatteryManager = nullptr;
201
  }
202

203
204
  mBatteryPromise = nullptr;

205
206
  if (mConnection) {
    mConnection->Shutdown();
207
    mConnection = nullptr;
208
  }
209

210
  mMediaDevices = nullptr;
211

212
213
214
215
  if (mPresentation) {
    mPresentation = nullptr;
  }

216
  mServiceWorkerContainer = nullptr;
217
218
219
220
221

  if (mMediaKeySystemAccessManager) {
    mMediaKeySystemAccessManager->Shutdown();
    mMediaKeySystemAccessManager = nullptr;
  }
222

223
224
225
226
227
  if (mGamepadServiceTest) {
    mGamepadServiceTest->Shutdown();
    mGamepadServiceTest = nullptr;
  }

228
  mVRGetDisplaysPromises.Clear();
229
230
231
232
233

  if (mVRServiceTest) {
    mVRServiceTest->Shutdown();
    mVRServiceTest = nullptr;
  }
234
235

  mMediaCapabilities = nullptr;
236

237
  mAddonManager = nullptr;
238
239

  mWebGpu = nullptr;
240
241

  mSharePromise = nullptr;
242
243
}

244
245
void Navigator::GetUserAgent(nsAString& aUserAgent, CallerType aCallerType,
                             ErrorResult& aRv) const {
246
  nsCOMPtr<nsPIDOMWindowInner> window;
247

248
  if (mWindow) {
249
    window = mWindow;
250
251
252
253
254
255
256
    nsIDocShell* docshell = window->GetDocShell();
    nsString customUserAgent;
    if (docshell) {
      docshell->GetCustomUserAgent(customUserAgent);

      if (!customUserAgent.IsEmpty()) {
        aUserAgent = customUserAgent;
257
        return;
258
      }
259
260
    }
  }
261

262
263
264
265
  nsCOMPtr<Document> doc = mWindow->GetExtantDoc();

  nsresult rv = GetUserAgent(window, doc ? doc->NodePrincipal() : nullptr,
                             aCallerType == CallerType::System, aUserAgent);
266
267
268
  if (NS_WARN_IF(NS_FAILED(rv))) {
    aRv.Throw(rv);
  }
269
270
}

271
void Navigator::GetAppCodeName(nsAString& aAppCodeName, ErrorResult& aRv) {
272
273
  nsresult rv;

274
275
  nsCOMPtr<nsIHttpProtocolHandler> service(
      do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
276
277
278
279
  if (NS_WARN_IF(NS_FAILED(rv))) {
    aRv.Throw(rv);
    return;
  }
280

281
  nsAutoCString appName;
282
  rv = service->GetAppName(appName);
283
284
285
286
  if (NS_WARN_IF(NS_FAILED(rv))) {
    aRv.Throw(rv);
    return;
  }
287

288
  CopyASCIItoUTF16(appName, aAppCodeName);
289
290
}

291
292
void Navigator::GetAppVersion(nsAString& aAppVersion, CallerType aCallerType,
                              ErrorResult& aRv) const {
293
294
  nsCOMPtr<Document> doc = mWindow->GetExtantDoc();

295
  nsresult rv = GetAppVersion(
296
      aAppVersion, doc ? doc->NodePrincipal() : nullptr,
297
      /* aUsePrefOverriddenValue = */ aCallerType != CallerType::System);
298
299
300
  if (NS_WARN_IF(NS_FAILED(rv))) {
    aRv.Throw(rv);
  }
301
302
}

303
void Navigator::GetAppName(nsAString& aAppName, CallerType aCallerType) const {
304
305
306
  nsCOMPtr<Document> doc = mWindow->GetExtantDoc();

  AppName(aAppName, doc ? doc->NodePrincipal() : nullptr,
307
          /* aUsePrefOverriddenValue = */ aCallerType != CallerType::System);
308
309
310
}

/**
311
312
313
 * Returns the value of Accept-Languages (HTTP header) as a nsTArray of
 * languages. The value is set in the preference by the user ("Content
 * Languages").
314
 *
315
 * "en", "en-US" and "i-cherokee" and "" are valid languages tokens.
316
 *
317
 * An empty array will be returned if there is no valid languages.
318
 */
319
320
/* static */
void Navigator::GetAcceptLanguages(nsTArray<nsString>& aLanguages) {
321
322
  MOZ_ASSERT(NS_IsMainThread());

323
324
  aLanguages.Clear();

325
  // E.g. "de-de, en-us,en".
326
327
  nsAutoString acceptLang;
  Preferences::GetLocalizedString("intl.accept_languages", acceptLang);
328

329
  // Split values on commas.
330
  nsCharSeparatedTokenizer langTokenizer(acceptLang, ',');
331
332
  while (langTokenizer.hasMoreTokens()) {
    nsDependentSubstring lang = langTokenizer.nextToken();
333

334
335
336
337
338
    // Replace "_" with "-" to avoid POSIX/Windows "en_US" notation.
    // NOTE: we should probably rely on the pref being set correctly.
    if (lang.Length() > 2 && lang[2] == char16_t('_')) {
      lang.Replace(2, 1, char16_t('-'));
    }
339

340
341
342
343
344
345
346
347
    // Use uppercase for country part, e.g. "en-US", not "en-us", see BCP47
    // only uppercase 2-letter country codes, not "zh-Hant", "de-DE-x-goethe".
    // NOTE: we should probably rely on the pref being set correctly.
    if (lang.Length() > 2) {
      nsCharSeparatedTokenizer localeTokenizer(lang, '-');
      int32_t pos = 0;
      bool first = true;
      while (localeTokenizer.hasMoreTokens()) {
348
        const nsAString& code = localeTokenizer.nextToken();
349
350
351
352
353
354
355

        if (code.Length() == 2 && !first) {
          nsAutoString upper(code);
          ToUpperCase(upper);
          lang.Replace(pos, code.Length(), upper);
        }

356
        pos += code.Length() + 1;  // 1 is the separator
357
358
        first = false;
      }
359
360
    }

361
    aLanguages.AppendElement(lang);
362
  }
363
}
364

365
366
367
368
369
370
371
/**
 * Do not use UI language (chosen app locale) here but the first value set in
 * the Accept Languages header, see ::GetAcceptLanguages().
 *
 * See RFC 2616, Section 15.1.4 "Privacy Issues Connected to Accept Headers" for
 * the reasons why.
 */
372
void Navigator::GetLanguage(nsAString& aLanguage) {
373
374
375
376
377
378
379
380
381
  nsTArray<nsString> languages;
  GetLanguages(languages);
  if (languages.Length() >= 1) {
    aLanguage.Assign(languages[0]);
  } else {
    aLanguage.Truncate();
  }
}

382
void Navigator::GetLanguages(nsTArray<nsString>& aLanguages) {
383
384
385
386
387
388
  GetAcceptLanguages(aLanguages);

  // The returned value is cached by the binding code. The window listen to the
  // accept languages change and will clear the cache when needed. It has to
  // take care of dispatching the DOM event already and the invalidation and the
  // event has to be timed correctly.
389
390
}

391
392
void Navigator::GetPlatform(nsAString& aPlatform, CallerType aCallerType,
                            ErrorResult& aRv) const {
393
394
  nsCOMPtr<Document> doc = mWindow->GetExtantDoc();

395
  nsresult rv = GetPlatform(
396
      aPlatform, doc ? doc->NodePrincipal() : nullptr,
397
      /* aUsePrefOverriddenValue = */ aCallerType != CallerType::System);
398
399
400
  if (NS_WARN_IF(NS_FAILED(rv))) {
    aRv.Throw(rv);
  }
401
402
}

403
404
void Navigator::GetOscpu(nsAString& aOSCPU, CallerType aCallerType,
                         ErrorResult& aRv) const {
405
  if (aCallerType != CallerType::System) {
406
407
    // If fingerprinting resistance is on, we will spoof this value. See
    // nsRFPService.h for details about spoofed values.
408
    if (nsContentUtils::ShouldResistFingerprinting(GetDocShell())) {
409
410
411
412
      aOSCPU.AssignLiteral(SPOOFED_OSCPU);
      return;
    }

413
414
415
    nsAutoString override;
    nsresult rv = Preferences::GetString("general.oscpu.override", override);
    if (NS_SUCCEEDED(rv)) {
416
      aOSCPU = override;
417
      return;
418
419
420
421
    }
  }

  nsresult rv;
422
423
  nsCOMPtr<nsIHttpProtocolHandler> service(
      do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
424
425
426
427
  if (NS_WARN_IF(NS_FAILED(rv))) {
    aRv.Throw(rv);
    return;
  }
428

429
  nsAutoCString oscpu;
430
  rv = service->GetOscpu(oscpu);
431
432
433
434
  if (NS_WARN_IF(NS_FAILED(rv))) {
    aRv.Throw(rv);
    return;
  }
435

436
  CopyASCIItoUTF16(oscpu, aOSCPU);
437
438
}

439
void Navigator::GetVendor(nsAString& aVendor) { aVendor.Truncate(); }
440

441
void Navigator::GetVendorSub(nsAString& aVendorSub) { aVendorSub.Truncate(); }
442

443
void Navigator::GetProduct(nsAString& aProduct) {
444
  aProduct.AssignLiteral("Gecko");
445
446
}

447
void Navigator::GetProductSub(nsAString& aProductSub) {
448
449
  // Legacy build date hardcoded for backward compatibility (bug 776376)
  aProductSub.AssignLiteral(LEGACY_UA_GECKO_TRAIL);
450
451
}

452
nsMimeTypeArray* Navigator::GetMimeTypes(ErrorResult& aRv) {
453
  if (!mMimeTypes) {
454
455
456
457
    if (!mWindow) {
      aRv.Throw(NS_ERROR_UNEXPECTED);
      return nullptr;
    }
458
    mMimeTypes = new nsMimeTypeArray(mWindow);
459
460
  }

461
  return mMimeTypes;
462
463
}

464
nsPluginArray* Navigator::GetPlugins(ErrorResult& aRv) {
465
  if (!mPlugins) {
466
467
468
469
    if (!mWindow) {
      aRv.Throw(NS_ERROR_UNEXPECTED);
      return nullptr;
    }
470
    mPlugins = new nsPluginArray(mWindow);
471
    mPlugins->Init();
472
473
  }

474
  return mPlugins;
475
476
}

477
Permissions* Navigator::GetPermissions(ErrorResult& aRv) {
478
479
480
481
482
483
484
485
486
487
488
489
  if (!mWindow) {
    aRv.Throw(NS_ERROR_UNEXPECTED);
    return nullptr;
  }

  if (!mPermissions) {
    mPermissions = new Permissions(mWindow);
  }

  return mPermissions;
}

490
StorageManager* Navigator::Storage() {
491
492
  MOZ_ASSERT(mWindow);

493
  if (!mStorageManager) {
494
    mStorageManager = new StorageManager(mWindow->AsGlobal());
495
496
497
498
499
  }

  return mStorageManager;
}

500
bool Navigator::CookieEnabled() {
501
  bool cookieEnabled = (StaticPrefs::network_cookie_cookieBehavior() !=
502
                        nsICookieService::BEHAVIOR_REJECT);
503
504
505
506

  // Check whether an exception overrides the global cookie behavior
  // Note that the code for getting the URI here matches that in
  // nsHTMLDocument::SetCookie.
507
  if (!mWindow || !mWindow->GetDocShell()) {
508
    return cookieEnabled;
509
510
  }

511
  nsCOMPtr<Document> doc = mWindow->GetExtantDoc();
512
  if (!doc) {
513
    return cookieEnabled;
514
515
  }

516
517
  nsCOMPtr<nsIURI> contentURI;
  doc->NodePrincipal()->GetURI(getter_AddRefs(contentURI));
518

519
520
  if (!contentURI) {
    // Not a content, so technically can't set cookies, but let's
521
    // just return the default value.
522
    return cookieEnabled;
523
524
  }

525
  uint32_t rejectedReason = 0;
526
  bool granted = AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(
527
      mWindow, contentURI, &rejectedReason);
528
529
530
531
532
533

  AntiTrackingCommon::NotifyBlockingDecision(
      mWindow,
      granted ? AntiTrackingCommon::BlockingDecision::eAllow
              : AntiTrackingCommon::BlockingDecision::eBlock,
      rejectedReason);
534
  return granted;
535
536
}

537
bool Navigator::OnLine() { return !NS_IsOffline(); }
538

539
540
void Navigator::GetBuildID(nsAString& aBuildID, CallerType aCallerType,
                           ErrorResult& aRv) const {
541
  if (aCallerType != CallerType::System) {
542
543
    // If fingerprinting resistance is on, we will spoof this value. See
    // nsRFPService.h for details about spoofed values.
544
    if (nsContentUtils::ShouldResistFingerprinting(GetDocShell())) {
545
546
547
      aBuildID.AssignLiteral(LEGACY_BUILD_ID);
      return;
    }
548

549
550
551
    nsAutoString override;
    nsresult rv = Preferences::GetString("general.buildID.override", override);
    if (NS_SUCCEEDED(rv)) {
552
      aBuildID = override;
553
      return;
554
    }
555
556
557
558

    nsAutoCString host;
    bool isHTTPS = false;
    if (mWindow) {
559
      nsCOMPtr<Document> doc = mWindow->GetDoc();
560
561
562
      if (doc) {
        nsIURI* uri = doc->GetDocumentURI();
        if (uri) {
563
          isHTTPS = uri->SchemeIs("https");
564
565
566
567
568
569
570
571
          if (isHTTPS) {
            MOZ_ALWAYS_SUCCEEDS(uri->GetHost(host));
          }
        }
      }
    }

    // Spoof the buildID on pages not loaded from "https://*.mozilla.org".
572
    if (!isHTTPS || !StringEndsWith(host, NS_LITERAL_CSTRING(".mozilla.org"))) {
573
574
575
      aBuildID.AssignLiteral(LEGACY_BUILD_ID);
      return;
    }
576
577
578
  }

  nsCOMPtr<nsIXULAppInfo> appInfo =
579
      do_GetService("@mozilla.org/xre/app-info;1");
580
  if (!appInfo) {
581
582
    aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
    return;
583
584
  }

585
  nsAutoCString buildID;
586
  nsresult rv = appInfo->GetAppBuildID(buildID);
587
588
589
  if (NS_WARN_IF(NS_FAILED(rv))) {
    aRv.Throw(rv);
    return;
590
591
592
593
594
595
  }

  aBuildID.Truncate();
  AppendASCIItoUTF16(buildID, aBuildID);
}

596
void Navigator::GetDoNotTrack(nsAString& aResult) {
597
  bool doNotTrack = StaticPrefs::privacy_donottrackheader_enabled();
598
599
600
601
602
603
  if (!doNotTrack) {
    nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(mWindow);
    doNotTrack = loadContext && loadContext->UseTrackingProtection();
  }

  if (doNotTrack) {
604
    aResult.AssignLiteral("1");
605
606
607
608
609
  } else {
    aResult.AssignLiteral("unspecified");
  }
}

610
uint64_t Navigator::HardwareConcurrency() {
611
  workerinternals::RuntimeService* rts =
612
      workerinternals::RuntimeService::GetOrCreateService();
613
614
615
616
617
618
619
  if (!rts) {
    return 1;
  }

  return rts->ClampedHardwareConcurrency();
}

620
void Navigator::RefreshMIMEArray() {
621
  if (mMimeTypes) {
622
    mMimeTypes->Refresh();
623
624
625
  }
}

626
627
namespace {

628
629
class VibrateWindowListener : public nsIDOMEventListener {
 public:
630
  VibrateWindowListener(nsPIDOMWindowInner* aWindow, Document* aDocument) {
631
632
633
    mWindow = do_GetWeakReference(aWindow);
    mDocument = do_GetWeakReference(aDocument);

634
    NS_NAMED_LITERAL_STRING(visibilitychange, "visibilitychange");
635
636
    aDocument->AddSystemEventListener(visibilitychange, this, /* listener */
                                      true,                   /* use capture */
637
                                      false /* wants untrusted */);
638
639
640
641
642
643
644
  }

  void RemoveListener();

  NS_DECL_ISUPPORTS
  NS_DECL_NSIDOMEVENTLISTENER

645
646
 private:
  virtual ~VibrateWindowListener() {}
647

648
649
650
651
  nsWeakPtr mWindow;
  nsWeakPtr mDocument;
};

652
NS_IMPL_ISUPPORTS(VibrateWindowListener, nsIDOMEventListener)
653

654
StaticRefPtr<VibrateWindowListener> gVibrateWindowListener;
655

656
static bool MayVibrate(Document* doc) {
657
658
659
660
  // Hidden documents cannot start or stop a vibration.
  return (doc && !doc->Hidden());
}

661
NS_IMETHODIMP
662
VibrateWindowListener::HandleEvent(Event* aEvent) {
663
  nsCOMPtr<Document> doc = do_QueryInterface(aEvent->GetTarget());
664

665
  if (!MayVibrate(doc)) {
666
667
668
669
    // It's important that we call CancelVibrate(), not Vibrate() with an
    // empty list, because Vibrate() will fail if we're no longer focused, but
    // CancelVibrate() will succeed, so long as nobody else has started a new
    // vibration pattern.
670
    nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mWindow);
671
672
    hal::CancelVibrate(window);
    RemoveListener();
673
    gVibrateWindowListener = nullptr;
674
675
676
677
678
679
    // Careful: The line above might have deleted |this|!
  }

  return NS_OK;
}

680
void VibrateWindowListener::RemoveListener() {
681
  nsCOMPtr<EventTarget> target = do_QueryReferent(mDocument);
682
683
684
  if (!target) {
    return;
  }
685
  NS_NAMED_LITERAL_STRING(visibilitychange, "visibilitychange");
686
687
688
689
  target->RemoveSystemEventListener(visibilitychange, this,
                                    true /* use capture */);
}

690
}  // namespace
691

692
void Navigator::SetVibrationPermission(bool aPermitted, bool aPersistent) {
693
694
695
696
697
698
699
700
701
  MOZ_ASSERT(NS_IsMainThread());

  nsTArray<uint32_t> pattern;
  pattern.SwapElements(mRequestedVibrationPattern);

  if (!mWindow) {
    return;
  }

702
  nsCOMPtr<Document> doc = mWindow->GetExtantDoc();
703
704
705
706
707
708
709
710
711

  if (!MayVibrate(doc)) {
    return;
  }

  if (aPermitted) {
    // Add a listener to cancel the vibration if the document becomes hidden,
    // and remove the old visibility listener, if there was one.
    if (!gVibrateWindowListener) {
712
713
714
      // If gVibrateWindowListener is null, this is the first time we've
      // vibrated, and we need to register a listener to clear
      // gVibrateWindowListener on shutdown.
715
716
717
718
719
720
721
722
723
      ClearOnShutdown(&gVibrateWindowListener);
    } else {
      gVibrateWindowListener->RemoveListener();
    }
    gVibrateWindowListener = new VibrateWindowListener(mWindow, doc);
    hal::Vibrate(pattern, mWindow);
  }

  if (aPersistent) {
724
725
726
727
728
    nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
    if (!permMgr) {
      return;
    }
    permMgr->AddFromPrincipal(doc->NodePrincipal(), kVibrationPermissionType,
729
730
                              aPermitted ? nsIPermissionManager::ALLOW_ACTION
                                         : nsIPermissionManager::DENY_ACTION,
731
                              nsIPermissionManager::EXPIRE_SESSION, 0);
732
733
734
  }
}

735
bool Navigator::Vibrate(uint32_t aDuration) {
736
  AutoTArray<uint32_t, 1> pattern;
737
  pattern.AppendElement(aDuration);
738
  return Vibrate(pattern);
739
740
}

741
bool Navigator::Vibrate(const nsTArray<uint32_t>& aPattern) {
742
743
  MOZ_ASSERT(NS_IsMainThread());

744
  if (!mWindow) {
745
    return false;
746
  }
747

748
  nsCOMPtr<Document> doc = mWindow->GetExtantDoc();
749

750
  if (!MayVibrate(doc)) {
751
    return false;
752
753
  }

754
755
756
  nsTArray<uint32_t> pattern(aPattern);

  if (pattern.Length() > sMaxVibrateListLen) {
757
    pattern.SetLength(sMaxVibrateListLen);
758
759
  }

760
  for (size_t i = 0; i < pattern.Length(); ++i) {
761
    pattern[i] = std::min(sMaxVibrateMS, pattern[i]);
762
763
  }

764
765
  // The spec says we check sVibratorEnabled after we've done the sanity
  // checking on the pattern.
766
  if (!sVibratorEnabled) {
767
    return true;
768
769
  }

770
  mRequestedVibrationPattern.SwapElements(pattern);
771
772
773
774
775
776
777
  nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
  if (!permMgr) {
    return false;
  }

  uint32_t permission = nsIPermissionManager::UNKNOWN_ACTION;

778
779
  permMgr->TestPermissionFromPrincipal(doc->NodePrincipal(),
                                       kVibrationPermissionType, &permission);
780

781
782
783
784
785
786
787
  if (permission == nsIPermissionManager::ALLOW_ACTION ||
      mRequestedVibrationPattern.IsEmpty() ||
      (mRequestedVibrationPattern.Length() == 1 &&
       mRequestedVibrationPattern[0] == 0)) {
    // Always allow cancelling vibration and respect session permissions.
    SetVibrationPermission(true /* permitted */, false /* persistent */);
    return true;
788
  }
789
790
791
792
793
794

  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
  if (!obs || permission == nsIPermissionManager::DENY_ACTION) {
    // Abort without observer service or on denied session permission.
    SetVibrationPermission(false /* permitted */, false /* persistent */);
    return true;
795
796
  }

797
798
799
  // Request user permission.
  obs->NotifyObservers(ToSupports(this), "Vibration:Request", nullptr);

800
  return true;
801
802
}

803
804
805
806
//*****************************************************************************
//  Pointer Events interface
//*****************************************************************************

807
uint32_t Navigator::MaxTouchPoints(CallerType aCallerType) {
808
809
810
  // The maxTouchPoints is going to reveal the detail of users' hardware. So,
  // we will spoof it into 0 if fingerprinting resistance is on.
  if (aCallerType != CallerType::System &&
811
      nsContentUtils::ShouldResistFingerprinting(GetDocShell())) {
812
813
814
    return 0;
  }

815
816
  nsCOMPtr<nsIWidget> widget =
      widget::WidgetUtils::DOMWindowToWidget(mWindow->GetOuterWindow());
817
818
819
820
821

  NS_ENSURE_TRUE(widget, 0);
  return widget->GetMaxTouchPoints();
}

822
823
824
825
//*****************************************************************************
//    Navigator::nsIDOMClientInformation
//*****************************************************************************

826
827
828
829
void Navigator::RegisterContentHandler(const nsAString& aMIMEType,
                                       const nsAString& aURI,
                                       const nsAString& aTitle,
                                       ErrorResult& aRv) {}
830

831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
// This list should be kept up-to-date with the spec:
// https://html.spec.whatwg.org/multipage/system-state.html#custom-handlers
static const char* const kSafeSchemes[] = {
    "bitcoin", "geo",  "im",   "irc",         "ircs", "magnet", "mailto",
    "mms",     "news", "nntp", "openpgp4fpr", "sip",  "sms",    "smsto",
    "ssh",     "tel",  "urn",  "webcal",      "wtai", "xmpp"};

void Navigator::CheckProtocolHandlerAllowed(const nsAString& aScheme,
                                            nsIURI* aHandlerURI,
                                            nsIURI* aDocumentURI,
                                            ErrorResult& aRv) {
  auto raisePermissionDeniedHandler = [&] {
    nsAutoCString spec;
    aHandlerURI->GetSpec(spec);
    nsPrintfCString message("Permission denied to add %s as a protocol handler",
                            spec.get());
    aRv.ThrowDOMException(NS_ERROR_DOM_SECURITY_ERR, message);
  };

  auto raisePermissionDeniedScheme = [&] {
    nsPrintfCString message(
        "Permission denied to add a protocol handler for %s",
        NS_ConvertUTF16toUTF8(aScheme).get());
    aRv.ThrowDOMException(NS_ERROR_DOM_SECURITY_ERR, message);
  };

  if (!aDocumentURI || !aHandlerURI) {
    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
    return;
  }

  nsCString spec;
  aHandlerURI->GetSpec(spec);
  // If the uri doesn't contain '%s', it won't be a good handler - the %s
  // gets replaced with the handled URI.
  if (!FindInReadable(NS_LITERAL_CSTRING("%s"), spec)) {
867
868
    aRv.ThrowDOMException(NS_ERROR_DOM_SYNTAX_ERR,
                          "Handler URI does not contain \"%s\".");
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
    return;
  }

  // For security reasons we reject non-http(s) urls (see bug 354316),
  nsAutoCString docScheme;
  nsAutoCString handlerScheme;
  aDocumentURI->GetScheme(docScheme);
  aHandlerURI->GetScheme(handlerScheme);
  if ((!docScheme.EqualsLiteral("https") && !docScheme.EqualsLiteral("http")) ||
      (!handlerScheme.EqualsLiteral("https") &&
       !handlerScheme.EqualsLiteral("http"))) {
    raisePermissionDeniedHandler();
    return;
  }

  // Should be same-origin:
  nsAutoCString handlerHost;
  aHandlerURI->GetHostPort(handlerHost);
  nsAutoCString documentHost;
  aDocumentURI->GetHostPort(documentHost);
  if (!handlerHost.Equals(documentHost) || !handlerScheme.Equals(docScheme)) {
    raisePermissionDeniedHandler();
    return;
  }

  // Having checked the handler URI, check the scheme:
  nsAutoCString scheme;
  ToLowerCase(NS_ConvertUTF16toUTF8(aScheme), scheme);
  if (StringBeginsWith(scheme, NS_LITERAL_CSTRING("web+"))) {
    // Check for non-ascii