gecko-dev/dom/base/Navigator.cpp
Nicholas Nethercote 18fae65f38 Bug 1563139 - Remove StaticPrefs.h. r=glandium
This requires replacing inclusions of it with inclusions of more specific prefs
files.

The exception is that StaticPrefsAll.h, which is equivalent to StaticPrefs.h,
and is used in `Codegen.py` because doing something smarter is tricky and
suitable for a follow-up. As a result, any change to StaticPrefList.yaml will
still trigger recompilation of all the generated DOM bindings files, but that's
still a big improvement over trigger recompilation of every file that uses
static prefs.

Most of the changes in this commit are very boring. The only changes that are
not boring are modules/libpref/*, Codegen.py, and ServoBindings.toml.

Differential Revision: https://phabricator.services.mozilla.com/D39138

--HG--
extra : moz-landing-system : lando
2019-07-26 01:10:23 +00:00

1844 lines
56 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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/. */
// Needs to be first.
#include "base/basictypes.h"
#include "Navigator.h"
#include "nsIXULAppInfo.h"
#include "nsPluginArray.h"
#include "nsMimeTypeArray.h"
#include "mozilla/AntiTrackingCommon.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/dom/BodyExtractor.h"
#include "mozilla/dom/FetchBinding.h"
#include "mozilla/dom/File.h"
#include "nsGeolocation.h"
#include "nsIClassOfService.h"
#include "nsIHttpProtocolHandler.h"
#include "nsIContentPolicy.h"
#include "nsIContentSecurityPolicy.h"
#include "nsContentPolicyUtils.h"
#include "nsISupportsPriority.h"
#include "nsICachingChannel.h"
#include "nsIWebProtocolHandlerRegistrar.h"
#include "nsICookiePermission.h"
#include "nsIScriptSecurityManager.h"
#include "nsCharSeparatedTokenizer.h"
#include "nsContentUtils.h"
#include "nsUnicharUtils.h"
#include "mozilla/Preferences.h"
#include "mozilla/StaticPrefs_media.h"
#include "mozilla/StaticPrefs_network.h"
#include "mozilla/StaticPrefs_privacy.h"
#include "mozilla/Telemetry.h"
#include "BatteryManager.h"
#include "mozilla/dom/CredentialsContainer.h"
#include "mozilla/dom/Clipboard.h"
#include "mozilla/dom/FeaturePolicyUtils.h"
#include "mozilla/dom/GamepadServiceTest.h"
#include "mozilla/dom/MediaCapabilities.h"
#include "mozilla/dom/WakeLock.h"
#include "mozilla/dom/power/PowerManagerService.h"
#include "mozilla/dom/MIDIAccessManager.h"
#include "mozilla/dom/MIDIOptionsBinding.h"
#include "mozilla/dom/Permissions.h"
#include "mozilla/dom/Presentation.h"
#include "mozilla/dom/ServiceWorkerContainer.h"
#include "mozilla/dom/StorageManager.h"
#include "mozilla/dom/TCPSocket.h"
#include "mozilla/dom/URLSearchParams.h"
#include "mozilla/dom/VRDisplay.h"
#include "mozilla/dom/VRDisplayEvent.h"
#include "mozilla/dom/VRServiceTest.h"
#include "mozilla/dom/workerinternals/RuntimeService.h"
#include "mozilla/Hal.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/StaticPtr.h"
#include "Connection.h"
#include "mozilla/dom/Event.h" // for Event
#include "nsGlobalWindow.h"
#include "nsIPermissionManager.h"
#include "nsMimeTypes.h"
#include "nsNetUtil.h"
#include "nsRFPService.h"
#include "nsStringStream.h"
#include "nsComponentManagerUtils.h"
#include "nsICookieService.h"
#include "nsIStringStream.h"
#include "nsIHttpChannel.h"
#include "nsIHttpChannelInternal.h"
#include "nsStreamUtils.h"
#include "WidgetUtils.h"
#include "nsIPresentationService.h"
#include "nsIScriptError.h"
#include "ReferrerInfo.h"
#include "nsIExternalProtocolHandler.h"
#include "BrowserChild.h"
#include "URIUtils.h"
#include "mozilla/dom/MediaDevices.h"
#include "MediaManager.h"
#include "nsIDOMGlobalPropertyInitializer.h"
#include "nsJSUtils.h"
#include "mozilla/dom/NavigatorBinding.h"
#include "mozilla/dom/Promise.h"
#include "nsIUploadChannel2.h"
#include "mozilla/dom/FormData.h"
#include "nsIDocShell.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/WorkerRunnable.h"
#if defined(XP_LINUX)
# include "mozilla/Hal.h"
#endif
#include "mozilla/EMEUtils.h"
#include "mozilla/DetailedPromise.h"
#include "mozilla/Unused.h"
namespace mozilla {
namespace dom {
static bool sVibratorEnabled = false;
static uint32_t sMaxVibrateMS = 0;
static uint32_t sMaxVibrateListLen = 0;
static const nsLiteralCString kVibrationPermissionType =
NS_LITERAL_CSTRING("vibration");
/* static */
void Navigator::Init() {
Preferences::AddBoolVarCache(&sVibratorEnabled, "dom.vibrator.enabled", true);
Preferences::AddUintVarCache(&sMaxVibrateMS, "dom.vibrator.max_vibrate_ms",
10000);
Preferences::AddUintVarCache(&sMaxVibrateListLen,
"dom.vibrator.max_vibrate_list_len", 128);
}
Navigator::Navigator(nsPIDOMWindowInner* aWindow) : mWindow(aWindow) {}
Navigator::~Navigator() { Invalidate(); }
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Navigator)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(Navigator)
NS_IMPL_CYCLE_COLLECTING_RELEASE(Navigator)
NS_IMPL_CYCLE_COLLECTION_CLASS(Navigator)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Navigator)
tmp->Invalidate();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Navigator)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMimeTypes)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPlugins)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPermissions)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGeolocation)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBatteryManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBatteryPromise)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConnection)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStorageManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCredentials)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaDevices)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServiceWorkerContainer)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaCapabilities)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaKeySystemAccessManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPresentation)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepadServiceTest)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRGetDisplaysPromises)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRServiceTest)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(Navigator)
void Navigator::Invalidate() {
// Don't clear mWindow here so we know we've got a non-null mWindow
// until we're unlinked.
mMimeTypes = nullptr;
if (mPlugins) {
mPlugins->Invalidate();
mPlugins = nullptr;
}
mPermissions = nullptr;
mStorageManager = nullptr;
// If there is a page transition, make sure delete the geolocation object.
if (mGeolocation) {
mGeolocation->Shutdown();
mGeolocation = nullptr;
}
if (mBatteryManager) {
mBatteryManager->Shutdown();
mBatteryManager = nullptr;
}
mBatteryPromise = nullptr;
if (mConnection) {
mConnection->Shutdown();
mConnection = nullptr;
}
mMediaDevices = nullptr;
if (mPresentation) {
mPresentation = nullptr;
}
mServiceWorkerContainer = nullptr;
if (mMediaKeySystemAccessManager) {
mMediaKeySystemAccessManager->Shutdown();
mMediaKeySystemAccessManager = nullptr;
}
if (mGamepadServiceTest) {
mGamepadServiceTest->Shutdown();
mGamepadServiceTest = nullptr;
}
mVRGetDisplaysPromises.Clear();
if (mVRServiceTest) {
mVRServiceTest->Shutdown();
mVRServiceTest = nullptr;
}
mMediaCapabilities = nullptr;
}
void Navigator::GetUserAgent(nsAString& aUserAgent, CallerType aCallerType,
ErrorResult& aRv) const {
nsCOMPtr<nsPIDOMWindowInner> window;
if (mWindow) {
window = mWindow;
nsIDocShell* docshell = window->GetDocShell();
nsString customUserAgent;
if (docshell) {
docshell->GetCustomUserAgent(customUserAgent);
if (!customUserAgent.IsEmpty()) {
aUserAgent = customUserAgent;
return;
}
}
}
nsCOMPtr<Document> doc = mWindow->GetExtantDoc();
nsresult rv = GetUserAgent(window, doc ? doc->NodePrincipal() : nullptr,
aCallerType == CallerType::System, aUserAgent);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
}
}
void Navigator::GetAppCodeName(nsAString& aAppCodeName, ErrorResult& aRv) {
nsresult rv;
nsCOMPtr<nsIHttpProtocolHandler> service(
do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return;
}
nsAutoCString appName;
rv = service->GetAppName(appName);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return;
}
CopyASCIItoUTF16(appName, aAppCodeName);
}
void Navigator::GetAppVersion(nsAString& aAppVersion, CallerType aCallerType,
ErrorResult& aRv) const {
nsCOMPtr<Document> doc = mWindow->GetExtantDoc();
nsresult rv = GetAppVersion(
aAppVersion, doc ? doc->NodePrincipal() : nullptr,
/* aUsePrefOverriddenValue = */ aCallerType != CallerType::System);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
}
}
void Navigator::GetAppName(nsAString& aAppName, CallerType aCallerType) const {
nsCOMPtr<Document> doc = mWindow->GetExtantDoc();
AppName(aAppName, doc ? doc->NodePrincipal() : nullptr,
/* aUsePrefOverriddenValue = */ aCallerType != CallerType::System);
}
/**
* 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").
*
* "en", "en-US" and "i-cherokee" and "" are valid languages tokens.
*
* An empty array will be returned if there is no valid languages.
*/
/* static */
void Navigator::GetAcceptLanguages(nsTArray<nsString>& aLanguages) {
MOZ_ASSERT(NS_IsMainThread());
aLanguages.Clear();
// E.g. "de-de, en-us,en".
nsAutoString acceptLang;
Preferences::GetLocalizedString("intl.accept_languages", acceptLang);
// Split values on commas.
nsCharSeparatedTokenizer langTokenizer(acceptLang, ',');
while (langTokenizer.hasMoreTokens()) {
nsDependentSubstring lang = langTokenizer.nextToken();
// 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('-'));
}
// 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()) {
const nsAString& code = localeTokenizer.nextToken();
if (code.Length() == 2 && !first) {
nsAutoString upper(code);
ToUpperCase(upper);
lang.Replace(pos, code.Length(), upper);
}
pos += code.Length() + 1; // 1 is the separator
first = false;
}
}
aLanguages.AppendElement(lang);
}
}
/**
* 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.
*/
void Navigator::GetLanguage(nsAString& aLanguage) {
nsTArray<nsString> languages;
GetLanguages(languages);
if (languages.Length() >= 1) {
aLanguage.Assign(languages[0]);
} else {
aLanguage.Truncate();
}
}
void Navigator::GetLanguages(nsTArray<nsString>& aLanguages) {
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.
}
void Navigator::GetPlatform(nsAString& aPlatform, CallerType aCallerType,
ErrorResult& aRv) const {
nsCOMPtr<Document> doc = mWindow->GetExtantDoc();
nsresult rv = GetPlatform(
aPlatform, doc ? doc->NodePrincipal() : nullptr,
/* aUsePrefOverriddenValue = */ aCallerType != CallerType::System);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
}
}
void Navigator::GetOscpu(nsAString& aOSCPU, CallerType aCallerType,
ErrorResult& aRv) const {
if (aCallerType != CallerType::System) {
// If fingerprinting resistance is on, we will spoof this value. See
// nsRFPService.h for details about spoofed values.
if (nsContentUtils::ShouldResistFingerprinting(GetDocShell())) {
aOSCPU.AssignLiteral(SPOOFED_OSCPU);
return;
}
nsAutoString override;
nsresult rv = Preferences::GetString("general.oscpu.override", override);
if (NS_SUCCEEDED(rv)) {
aOSCPU = override;
return;
}
}
nsresult rv;
nsCOMPtr<nsIHttpProtocolHandler> service(
do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return;
}
nsAutoCString oscpu;
rv = service->GetOscpu(oscpu);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return;
}
CopyASCIItoUTF16(oscpu, aOSCPU);
}
void Navigator::GetVendor(nsAString& aVendor) { aVendor.Truncate(); }
void Navigator::GetVendorSub(nsAString& aVendorSub) { aVendorSub.Truncate(); }
void Navigator::GetProduct(nsAString& aProduct) {
aProduct.AssignLiteral("Gecko");
}
void Navigator::GetProductSub(nsAString& aProductSub) {
// Legacy build date hardcoded for backward compatibility (bug 776376)
aProductSub.AssignLiteral(LEGACY_UA_GECKO_TRAIL);
}
nsMimeTypeArray* Navigator::GetMimeTypes(ErrorResult& aRv) {
if (!mMimeTypes) {
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
mMimeTypes = new nsMimeTypeArray(mWindow);
}
return mMimeTypes;
}
nsPluginArray* Navigator::GetPlugins(ErrorResult& aRv) {
if (!mPlugins) {
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
mPlugins = new nsPluginArray(mWindow);
mPlugins->Init();
}
return mPlugins;
}
Permissions* Navigator::GetPermissions(ErrorResult& aRv) {
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
if (!mPermissions) {
mPermissions = new Permissions(mWindow);
}
return mPermissions;
}
StorageManager* Navigator::Storage() {
MOZ_ASSERT(mWindow);
if (!mStorageManager) {
mStorageManager = new StorageManager(mWindow->AsGlobal());
}
return mStorageManager;
}
bool Navigator::CookieEnabled() {
bool cookieEnabled = (StaticPrefs::network_cookie_cookieBehavior() !=
nsICookieService::BEHAVIOR_REJECT);
// Check whether an exception overrides the global cookie behavior
// Note that the code for getting the URI here matches that in
// nsHTMLDocument::SetCookie.
if (!mWindow || !mWindow->GetDocShell()) {
return cookieEnabled;
}
nsCOMPtr<Document> doc = mWindow->GetExtantDoc();
if (!doc) {
return cookieEnabled;
}
nsCOMPtr<nsIURI> contentURI;
doc->NodePrincipal()->GetURI(getter_AddRefs(contentURI));
if (!contentURI) {
// Not a content, so technically can't set cookies, but let's
// just return the default value.
return cookieEnabled;
}
uint32_t rejectedReason = 0;
bool granted = AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(
mWindow, contentURI, &rejectedReason);
AntiTrackingCommon::NotifyBlockingDecision(
mWindow,
granted ? AntiTrackingCommon::BlockingDecision::eAllow
: AntiTrackingCommon::BlockingDecision::eBlock,
rejectedReason);
return granted;
}
bool Navigator::OnLine() { return !NS_IsOffline(); }
void Navigator::GetBuildID(nsAString& aBuildID, CallerType aCallerType,
ErrorResult& aRv) const {
if (aCallerType != CallerType::System) {
// If fingerprinting resistance is on, we will spoof this value. See
// nsRFPService.h for details about spoofed values.
if (nsContentUtils::ShouldResistFingerprinting(GetDocShell())) {
aBuildID.AssignLiteral(LEGACY_BUILD_ID);
return;
}
nsAutoString override;
nsresult rv = Preferences::GetString("general.buildID.override", override);
if (NS_SUCCEEDED(rv)) {
aBuildID = override;
return;
}
nsAutoCString host;
bool isHTTPS = false;
if (mWindow) {
nsCOMPtr<Document> doc = mWindow->GetDoc();
if (doc) {
nsIURI* uri = doc->GetDocumentURI();
if (uri) {
MOZ_ALWAYS_SUCCEEDS(uri->SchemeIs("https", &isHTTPS));
if (isHTTPS) {
MOZ_ALWAYS_SUCCEEDS(uri->GetHost(host));
}
}
}
}
// Spoof the buildID on pages not loaded from "https://*.mozilla.org".
if (!isHTTPS || !StringEndsWith(host, NS_LITERAL_CSTRING(".mozilla.org"))) {
aBuildID.AssignLiteral(LEGACY_BUILD_ID);
return;
}
}
nsCOMPtr<nsIXULAppInfo> appInfo =
do_GetService("@mozilla.org/xre/app-info;1");
if (!appInfo) {
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
return;
}
nsAutoCString buildID;
nsresult rv = appInfo->GetAppBuildID(buildID);
if (NS_WARN_IF(NS_FAILED(rv))) {
aRv.Throw(rv);
return;
}
aBuildID.Truncate();
AppendASCIItoUTF16(buildID, aBuildID);
}
void Navigator::GetDoNotTrack(nsAString& aResult) {
bool doNotTrack = StaticPrefs::privacy_donottrackheader_enabled();
if (!doNotTrack) {
nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(mWindow);
doNotTrack = loadContext && loadContext->UseTrackingProtection();
}
if (doNotTrack) {
aResult.AssignLiteral("1");
} else {
aResult.AssignLiteral("unspecified");
}
}
uint64_t Navigator::HardwareConcurrency() {
workerinternals::RuntimeService* rts =
workerinternals::RuntimeService::GetOrCreateService();
if (!rts) {
return 1;
}
return rts->ClampedHardwareConcurrency();
}
void Navigator::RefreshMIMEArray() {
if (mMimeTypes) {
mMimeTypes->Refresh();
}
}
namespace {
class VibrateWindowListener : public nsIDOMEventListener {
public:
VibrateWindowListener(nsPIDOMWindowInner* aWindow, Document* aDocument) {
mWindow = do_GetWeakReference(aWindow);
mDocument = do_GetWeakReference(aDocument);
NS_NAMED_LITERAL_STRING(visibilitychange, "visibilitychange");
aDocument->AddSystemEventListener(visibilitychange, this, /* listener */
true, /* use capture */
false /* wants untrusted */);
}
void RemoveListener();
NS_DECL_ISUPPORTS
NS_DECL_NSIDOMEVENTLISTENER
private:
virtual ~VibrateWindowListener() {}
nsWeakPtr mWindow;
nsWeakPtr mDocument;
};
NS_IMPL_ISUPPORTS(VibrateWindowListener, nsIDOMEventListener)
StaticRefPtr<VibrateWindowListener> gVibrateWindowListener;
static bool MayVibrate(Document* doc) {
// Hidden documents cannot start or stop a vibration.
return (doc && !doc->Hidden());
}
NS_IMETHODIMP
VibrateWindowListener::HandleEvent(Event* aEvent) {
nsCOMPtr<Document> doc = do_QueryInterface(aEvent->GetTarget());
if (!MayVibrate(doc)) {
// 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.
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mWindow);
hal::CancelVibrate(window);
RemoveListener();
gVibrateWindowListener = nullptr;
// Careful: The line above might have deleted |this|!
}
return NS_OK;
}
void VibrateWindowListener::RemoveListener() {
nsCOMPtr<EventTarget> target = do_QueryReferent(mDocument);
if (!target) {
return;
}
NS_NAMED_LITERAL_STRING(visibilitychange, "visibilitychange");
target->RemoveSystemEventListener(visibilitychange, this,
true /* use capture */);
}
} // namespace
void Navigator::SetVibrationPermission(bool aPermitted, bool aPersistent) {
MOZ_ASSERT(NS_IsMainThread());
nsTArray<uint32_t> pattern;
pattern.SwapElements(mRequestedVibrationPattern);
if (!mWindow) {
return;
}
nsCOMPtr<Document> doc = mWindow->GetExtantDoc();
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) {
// If gVibrateWindowListener is null, this is the first time we've
// vibrated, and we need to register a listener to clear
// gVibrateWindowListener on shutdown.
ClearOnShutdown(&gVibrateWindowListener);
} else {
gVibrateWindowListener->RemoveListener();
}
gVibrateWindowListener = new VibrateWindowListener(mWindow, doc);
hal::Vibrate(pattern, mWindow);
}
if (aPersistent) {
nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
if (!permMgr) {
return;
}
permMgr->AddFromPrincipal(doc->NodePrincipal(), kVibrationPermissionType,
aPermitted ? nsIPermissionManager::ALLOW_ACTION
: nsIPermissionManager::DENY_ACTION,
nsIPermissionManager::EXPIRE_SESSION, 0);
}
}
bool Navigator::Vibrate(uint32_t aDuration) {
AutoTArray<uint32_t, 1> pattern;
pattern.AppendElement(aDuration);
return Vibrate(pattern);
}
bool Navigator::Vibrate(const nsTArray<uint32_t>& aPattern) {
MOZ_ASSERT(NS_IsMainThread());
if (!mWindow) {
return false;
}
nsCOMPtr<Document> doc = mWindow->GetExtantDoc();
if (!MayVibrate(doc)) {
return false;
}
nsTArray<uint32_t> pattern(aPattern);
if (pattern.Length() > sMaxVibrateListLen) {
pattern.SetLength(sMaxVibrateListLen);
}
for (size_t i = 0; i < pattern.Length(); ++i) {
pattern[i] = std::min(sMaxVibrateMS, pattern[i]);
}
// The spec says we check sVibratorEnabled after we've done the sanity
// checking on the pattern.
if (!sVibratorEnabled) {
return true;
}
mRequestedVibrationPattern.SwapElements(pattern);
nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
if (!permMgr) {
return false;
}
uint32_t permission = nsIPermissionManager::UNKNOWN_ACTION;
permMgr->TestPermissionFromPrincipal(doc->NodePrincipal(),
kVibrationPermissionType, &permission);
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;
}
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;
}
// Request user permission.
obs->NotifyObservers(ToSupports(this), "Vibration:Request", nullptr);
return true;
}
//*****************************************************************************
// Pointer Events interface
//*****************************************************************************
uint32_t Navigator::MaxTouchPoints(CallerType aCallerType) {
// 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 &&
nsContentUtils::ShouldResistFingerprinting(GetDocShell())) {
return 0;
}
nsCOMPtr<nsIWidget> widget =
widget::WidgetUtils::DOMWindowToWidget(mWindow->GetOuterWindow());
NS_ENSURE_TRUE(widget, 0);
return widget->GetMaxTouchPoints();
}
//*****************************************************************************
// Navigator::nsIDOMClientInformation
//*****************************************************************************
void Navigator::RegisterContentHandler(const nsAString& aMIMEType,
const nsAString& aURI,
const nsAString& aTitle,
ErrorResult& aRv) {}
// 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)) {
aRv.ThrowDOMException(NS_ERROR_DOM_SYNTAX_ERR);
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
nsReadingIterator<char> iter;
nsReadingIterator<char> iterEnd;
auto remainingScheme = Substring(scheme, 4 /* web+ */);
remainingScheme.BeginReading(iter);
remainingScheme.EndReading(iterEnd);
// Scheme suffix must be non-empty
if (iter == iterEnd) {
raisePermissionDeniedScheme();
return;
}
for (; iter != iterEnd; iter++) {
if (*iter < 'a' || *iter > 'z') {
raisePermissionDeniedScheme();
return;
}
}
} else {
bool matches = false;
for (const char* safeScheme : kSafeSchemes) {
if (scheme.Equals(safeScheme)) {
matches = true;
break;
}
}
if (!matches) {
raisePermissionDeniedScheme();
return;
}
}
nsCOMPtr<nsIProtocolHandler> handler;
nsCOMPtr<nsIIOService> io = services::GetIOService();
if (NS_FAILED(
io->GetProtocolHandler(scheme.get(), getter_AddRefs(handler)))) {
raisePermissionDeniedScheme();
return;
}
// Check to make sure this isn't already handled internally (we don't
// want to let them take over, say "chrome"). In theory, the checks above
// should have already taken care of this.
nsCOMPtr<nsIExternalProtocolHandler> externalHandler =
do_QueryInterface(handler);
MOZ_RELEASE_ASSERT(
externalHandler,
"We should never allow overriding a builtin protocol handler");
// check if we have prefs set saying not to add this.
bool defaultExternal =
Preferences::GetBool("network.protocol-handler.external-default");
nsPrintfCString specificPref("network.protocol-handler.external.%s",
scheme.get());
if (!Preferences::GetBool(specificPref.get(), defaultExternal)) {
raisePermissionDeniedScheme();
return;
}
}
void Navigator::RegisterProtocolHandler(const nsAString& aScheme,
const nsAString& aURI,
const nsAString& aTitle,
ErrorResult& aRv) {
if (!mWindow || !mWindow->GetOuterWindow() || !mWindow->GetDocShell() ||
!mWindow->GetDoc()) {
return;
}
nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(mWindow);
if (loadContext->UsePrivateBrowsing()) {
// If we're a private window, don't alert the user or webpage. We log to the
// console so that web developers have some way to tell what's going wrong.
nsContentUtils::ReportToConsole(
nsIScriptError::warningFlag, NS_LITERAL_CSTRING("DOM"),
mWindow->GetDoc(), nsContentUtils::eDOM_PROPERTIES,
"RegisterProtocolHandlerPrivateBrowsingWarning");
return;
}
nsCOMPtr<Document> doc = mWindow->GetDoc();
// Determine if doc is allowed to assign this handler
nsIURI* docURI = doc->GetDocumentURIObject();
nsCOMPtr<nsIURI> handlerURI;
NS_NewURI(getter_AddRefs(handlerURI), NS_ConvertUTF16toUTF8(aURI),
doc->GetDocumentCharacterSet(), docURI);
CheckProtocolHandlerAllowed(aScheme, handlerURI, docURI, aRv);
if (aRv.Failed()) {
return;
}
if (XRE_IsContentProcess()) {
nsAutoString scheme(aScheme);
nsAutoString title(aTitle);
RefPtr<BrowserChild> browserChild = BrowserChild::GetFrom(mWindow);
browserChild->SendRegisterProtocolHandler(scheme, handlerURI, title,
docURI);
return;
}
nsCOMPtr<nsIWebProtocolHandlerRegistrar> registrar =
do_GetService(NS_WEBPROTOCOLHANDLERREGISTRAR_CONTRACTID);
if (registrar) {
aRv = registrar->RegisterProtocolHandler(aScheme, handlerURI, aTitle,
docURI, mWindow->GetOuterWindow());
}
}
Geolocation* Navigator::GetGeolocation(ErrorResult& aRv) {
if (mGeolocation) {
return mGeolocation;
}
if (!mWindow || !mWindow->GetOuterWindow() || !mWindow->GetDocShell()) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
mGeolocation = new Geolocation();
if (NS_FAILED(mGeolocation->Init(mWindow))) {
mGeolocation = nullptr;
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
return mGeolocation;
}
class BeaconStreamListener final : public nsIStreamListener {
~BeaconStreamListener() {}
public:
BeaconStreamListener() : mLoadGroup(nullptr) {}
void SetLoadGroup(nsILoadGroup* aLoadGroup) { mLoadGroup = aLoadGroup; }
NS_DECL_ISUPPORTS
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSIREQUESTOBSERVER
private:
nsCOMPtr<nsILoadGroup> mLoadGroup;
};
NS_IMPL_ISUPPORTS(BeaconStreamListener, nsIStreamListener, nsIRequestObserver)
NS_IMETHODIMP
BeaconStreamListener::OnStartRequest(nsIRequest* aRequest) {
// release the loadgroup first
mLoadGroup = nullptr;
aRequest->Cancel(NS_ERROR_NET_INTERRUPT);
return NS_BINDING_ABORTED;
}
NS_IMETHODIMP
BeaconStreamListener::OnStopRequest(nsIRequest* aRequest, nsresult aStatus) {
return NS_OK;
}
NS_IMETHODIMP
BeaconStreamListener::OnDataAvailable(nsIRequest* aRequest,
nsIInputStream* inStr,
uint64_t sourceOffset, uint32_t count) {
MOZ_ASSERT(false);
return NS_OK;
}
bool Navigator::SendBeacon(const nsAString& aUrl,
const Nullable<fetch::BodyInit>& aData,
ErrorResult& aRv) {
if (aData.IsNull()) {
return SendBeaconInternal(aUrl, nullptr, eBeaconTypeOther, aRv);
}
if (aData.Value().IsArrayBuffer()) {
BodyExtractor<const ArrayBuffer> body(&aData.Value().GetAsArrayBuffer());
return SendBeaconInternal(aUrl, &body, eBeaconTypeArrayBuffer, aRv);
}
if (aData.Value().IsArrayBufferView()) {
BodyExtractor<const ArrayBufferView> body(
&aData.Value().GetAsArrayBufferView());
return SendBeaconInternal(aUrl, &body, eBeaconTypeArrayBuffer, aRv);
}
if (aData.Value().IsBlob()) {
BodyExtractor<const Blob> body(&aData.Value().GetAsBlob());
return SendBeaconInternal(aUrl, &body, eBeaconTypeBlob, aRv);
}
if (aData.Value().IsFormData()) {
BodyExtractor<const FormData> body(&aData.Value().GetAsFormData());
return SendBeaconInternal(aUrl, &body, eBeaconTypeOther, aRv);
}
if (aData.Value().IsUSVString()) {
BodyExtractor<const nsAString> body(&aData.Value().GetAsUSVString());
return SendBeaconInternal(aUrl, &body, eBeaconTypeOther, aRv);
}
if (aData.Value().IsURLSearchParams()) {
BodyExtractor<const URLSearchParams> body(
&aData.Value().GetAsURLSearchParams());
return SendBeaconInternal(aUrl, &body, eBeaconTypeOther, aRv);
}
MOZ_CRASH("Invalid data type.");
return false;
}
bool Navigator::SendBeaconInternal(const nsAString& aUrl,
BodyExtractorBase* aBody, BeaconType aType,
ErrorResult& aRv) {
if (!mWindow) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return false;
}
nsCOMPtr<Document> doc = mWindow->GetDoc();
if (!doc) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return false;
}
nsIURI* documentURI = doc->GetDocumentURI();
if (!documentURI) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return false;
}
nsCOMPtr<nsIURI> uri;
nsresult rv = nsContentUtils::NewURIWithDocumentCharset(
getter_AddRefs(uri), aUrl, doc, doc->GetDocBaseURI());
if (NS_FAILED(rv)) {
aRv.ThrowTypeError<MSG_INVALID_URL>(aUrl);
return false;
}
// Spec disallows any schemes save for HTTP/HTTPs
bool isValidScheme;
if (!(NS_SUCCEEDED(uri->SchemeIs("http", &isValidScheme)) && isValidScheme) &&
!(NS_SUCCEEDED(uri->SchemeIs("https", &isValidScheme)) &&
isValidScheme)) {
aRv.ThrowTypeError<MSG_INVALID_URL_SCHEME>(NS_LITERAL_STRING("Beacon"),
aUrl);
return false;
}
// No need to use CORS for sendBeacon unless it's a BLOB
nsSecurityFlags securityFlags =
aType == eBeaconTypeBlob
? nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS
: nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
nsCOMPtr<nsIChannel> channel;
rv = NS_NewChannel(getter_AddRefs(channel), uri, doc, securityFlags,
nsIContentPolicy::TYPE_BEACON);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return false;
}
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
if (!httpChannel) {
// Beacon spec only supports HTTP requests at this time
aRv.Throw(NS_ERROR_DOM_BAD_URI);
return false;
}
nsCOMPtr<nsIReferrerInfo> referrerInfo = new ReferrerInfo();
referrerInfo->InitWithDocument(doc);
rv = httpChannel->SetReferrerInfoWithoutClone(referrerInfo);
MOZ_ASSERT(NS_SUCCEEDED(rv));
nsCOMPtr<nsIInputStream> in;
nsAutoCString contentTypeWithCharset;
nsAutoCString charset;
uint64_t length = 0;
if (aBody) {
aRv = aBody->GetAsStream(getter_AddRefs(in), &length,
contentTypeWithCharset, charset);
if (NS_WARN_IF(aRv.Failed())) {
return false;
}
nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(channel);
if (!uploadChannel) {
aRv.Throw(NS_ERROR_FAILURE);
return false;
}
uploadChannel->ExplicitSetUploadStream(in, contentTypeWithCharset, length,
NS_LITERAL_CSTRING("POST"), false);
} else {
rv = httpChannel->SetRequestMethod(NS_LITERAL_CSTRING("POST"));
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(channel);
if (p) {
p->SetPriority(nsISupportsPriority::PRIORITY_LOWEST);
}
nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel));
if (cos) {
cos->AddClassFlags(nsIClassOfService::Background);
}
// The channel needs to have a loadgroup associated with it, so that we can
// cancel the channel and any redirected channels it may create.
nsCOMPtr<nsILoadGroup> loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
nsCOMPtr<nsIInterfaceRequestor> callbacks =
do_QueryInterface(mWindow->GetDocShell());
loadGroup->SetNotificationCallbacks(callbacks);
channel->SetLoadGroup(loadGroup);
RefPtr<BeaconStreamListener> beaconListener = new BeaconStreamListener();
rv = channel->AsyncOpen(beaconListener);
// do not throw if security checks fail within asyncOpen
NS_ENSURE_SUCCESS(rv, false);
// make the beaconListener hold a strong reference to the loadgroup
// which is released in ::OnStartRequest
beaconListener->SetLoadGroup(loadGroup);
return true;
}
MediaDevices* Navigator::GetMediaDevices(ErrorResult& aRv) {
if (!mMediaDevices) {
if (!mWindow || !mWindow->GetOuterWindow() ||
mWindow->GetOuterWindow()->GetCurrentInnerWindow() != mWindow) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr;
}
mMediaDevices = new MediaDevices(mWindow);
}
return mMediaDevices;
}
void Navigator::MozGetUserMedia(const MediaStreamConstraints& aConstraints,
NavigatorUserMediaSuccessCallback& aOnSuccess,
NavigatorUserMediaErrorCallback& aOnError,
CallerType aCallerType, ErrorResult& aRv) {
MOZ_ASSERT(NS_IsMainThread());
if (!mWindow || !mWindow->GetOuterWindow() ||
mWindow->GetOuterWindow()->GetCurrentInnerWindow() != mWindow) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return;
}
RefPtr<NavigatorUserMediaSuccessCallback> onsuccess(&aOnSuccess);
RefPtr<NavigatorUserMediaErrorCallback> onerror(&aOnError);
nsWeakPtr weakWindow = nsWeakPtr(do_GetWeakReference(mWindow));
MediaManager::Get()
->GetUserMedia(mWindow, aConstraints, aCallerType)
->Then(
GetMainThreadSerialEventTarget(), __func__,
[weakWindow, onsuccess = std::move(onsuccess)](
const RefPtr<DOMMediaStream>& aStream) MOZ_CAN_RUN_SCRIPT {
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(weakWindow);
if (!window || !window->GetOuterWindow() ||
window->GetOuterWindow()->GetCurrentInnerWindow() != window) {
return; // Leave Promise pending after navigation by design.
}
MediaManager::CallOnSuccess(*onsuccess, *aStream);
},
[weakWindow, onerror = std::move(onerror)](
const RefPtr<MediaMgrError>& aError) MOZ_CAN_RUN_SCRIPT {
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(weakWindow);
if (!window || !window->GetOuterWindow() ||
window->GetOuterWindow()->GetCurrentInnerWindow() != window) {
return; // Leave Promise pending after navigation by design.
}
auto error = MakeRefPtr<MediaStreamError>(window, *aError);
MediaManager::CallOnError(*onerror, *error);
});
}
void Navigator::MozGetUserMediaDevices(
const MediaStreamConstraints& aConstraints,
MozGetUserMediaDevicesSuccessCallback& aOnSuccess,
NavigatorUserMediaErrorCallback& aOnError, uint64_t aInnerWindowID,
const nsAString& aCallID, ErrorResult& aRv) {
if (!mWindow || !mWindow->GetOuterWindow() ||
mWindow->GetOuterWindow()->GetCurrentInnerWindow() != mWindow) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return;
}
if (Document* doc = mWindow->GetExtantDoc()) {
if (!mWindow->IsSecureContext()) {
doc->SetDocumentAndPageUseCounter(
eUseCounter_custom_MozGetUserMediaInsec);
}
nsINode* node = doc;
while ((node = nsContentUtils::GetCrossDocParentNode(node))) {
if (NS_FAILED(nsContentUtils::CheckSameOrigin(doc, node))) {
doc->SetDocumentAndPageUseCounter(
eUseCounter_custom_MozGetUserMediaXOrigin);
break;
}
}
}
RefPtr<MediaManager> manager = MediaManager::Get();
// XXXbz aOnError seems to be unused?
nsCOMPtr<nsPIDOMWindowInner> window(mWindow);
aRv = manager->GetUserMediaDevices(window, aConstraints, aOnSuccess,
aInnerWindowID, aCallID);
}
//*****************************************************************************
// Navigator::nsINavigatorBattery
//*****************************************************************************
Promise* Navigator::GetBattery(ErrorResult& aRv) {
if (mBatteryPromise) {
return mBatteryPromise;
}
if (!mWindow || !mWindow->GetDocShell()) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
RefPtr<Promise> batteryPromise = Promise::Create(mWindow->AsGlobal(), aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
mBatteryPromise = batteryPromise;
if (!mBatteryManager) {
mBatteryManager = new battery::BatteryManager(mWindow);
mBatteryManager->Init();
}
mBatteryPromise->MaybeResolve(mBatteryManager);
return mBatteryPromise;
}
already_AddRefed<LegacyMozTCPSocket> Navigator::MozTCPSocket() {
RefPtr<LegacyMozTCPSocket> socket = new LegacyMozTCPSocket(GetWindow());
return socket.forget();
}
void Navigator::GetGamepads(nsTArray<RefPtr<Gamepad>>& aGamepads,
ErrorResult& aRv) {
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return;
}
NS_ENSURE_TRUE_VOID(mWindow->GetDocShell());
nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow);
win->SetHasGamepadEventListener(true);
win->GetGamepads(aGamepads);
}
GamepadServiceTest* Navigator::RequestGamepadServiceTest() {
if (!mGamepadServiceTest) {
mGamepadServiceTest = GamepadServiceTest::CreateTestService(mWindow);
}
return mGamepadServiceTest;
}
already_AddRefed<Promise> Navigator::GetVRDisplays(ErrorResult& aRv) {
if (!mWindow || !mWindow->GetDocShell() || !mWindow->GetExtantDoc()) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
if (!FeaturePolicyUtils::IsFeatureAllowed(mWindow->GetExtantDoc(),
NS_LITERAL_STRING("vr"))) {
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
return nullptr;
}
nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow);
win->NotifyVREventListenerAdded();
RefPtr<Promise> p = Promise::Create(mWindow->AsGlobal(), aRv);
if (aRv.Failed()) {
return nullptr;
}
// We pass mWindow's id to RefreshVRDisplays, so NotifyVRDisplaysUpdated will
// be called asynchronously, resolving the promises in mVRGetDisplaysPromises.
if (!VRDisplay::RefreshVRDisplays(win->WindowID())) {
p->MaybeReject(NS_ERROR_FAILURE);
return p.forget();
}
mVRGetDisplaysPromises.AppendElement(p);
return p.forget();
}
void Navigator::GetActiveVRDisplays(
nsTArray<RefPtr<VRDisplay>>& aDisplays) const {
/**
* Get only the active VR displays.
* GetActiveVRDisplays should only enumerate displays that
* are already active without causing any other hardware to be
* activated.
* We must not call nsGlobalWindow::NotifyVREventListenerAdded here,
* as that would cause enumeration and activation of other VR hardware.
* Activating VR hardware is intrusive to the end user, as it may
* involve physically powering on devices that the user did not
* intend to use.
*/
if (!mWindow || !mWindow->GetDocShell()) {
return;
}
nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow);
nsTArray<RefPtr<VRDisplay>> displays;
if (win->UpdateVRDisplays(displays)) {
for (auto display : displays) {
if (display->IsPresenting()) {
aDisplays.AppendElement(display);
}
}
}
}
void Navigator::NotifyVRDisplaysUpdated() {
// Synchronize the VR devices and resolve the promises in
// mVRGetDisplaysPromises
nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow);
nsTArray<RefPtr<VRDisplay>> vrDisplays;
if (win->UpdateVRDisplays(vrDisplays)) {
for (auto p : mVRGetDisplaysPromises) {
p->MaybeResolve(vrDisplays);
}
} else {
for (auto p : mVRGetDisplaysPromises) {
p->MaybeReject(NS_ERROR_FAILURE);
}
}
mVRGetDisplaysPromises.Clear();
}
void Navigator::NotifyActiveVRDisplaysChanged() {
Navigator_Binding::ClearCachedActiveVRDisplaysValue(this);
}
VRServiceTest* Navigator::RequestVRServiceTest() {
// Ensure that the Mock VR devices are not released prematurely
nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow);
win->NotifyVREventListenerAdded();
if (!mVRServiceTest) {
mVRServiceTest = VRServiceTest::CreateTestService(mWindow);
}
return mVRServiceTest;
}
bool Navigator::IsWebVRContentDetected() const {
nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow);
return win->IsVRContentDetected();
}
bool Navigator::IsWebVRContentPresenting() const {
nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow);
return win->IsVRContentPresenting();
}
void Navigator::RequestVRPresentation(VRDisplay& aDisplay) {
nsGlobalWindowInner* win = nsGlobalWindowInner::Cast(mWindow);
win->DispatchVRDisplayActivate(aDisplay.DisplayId(),
VRDisplayEventReason::Requested);
}
already_AddRefed<Promise> Navigator::RequestMIDIAccess(
const MIDIOptions& aOptions, ErrorResult& aRv) {
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
MIDIAccessManager* accessMgr = MIDIAccessManager::Get();
return accessMgr->RequestMIDIAccess(mWindow, aOptions, aRv);
}
network::Connection* Navigator::GetConnection(ErrorResult& aRv) {
if (!mConnection) {
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
mConnection = network::Connection::CreateForWindow(mWindow);
}
return mConnection;
}
already_AddRefed<ServiceWorkerContainer> Navigator::ServiceWorker() {
MOZ_ASSERT(mWindow);
if (!mServiceWorkerContainer) {
mServiceWorkerContainer =
ServiceWorkerContainer::Create(mWindow->AsGlobal());
}
RefPtr<ServiceWorkerContainer> ref = mServiceWorkerContainer;
return ref.forget();
}
size_t Navigator::SizeOfIncludingThis(
mozilla::MallocSizeOf aMallocSizeOf) const {
size_t n = aMallocSizeOf(this);
// TODO: add SizeOfIncludingThis() to nsMimeTypeArray, bug 674113.
// TODO: add SizeOfIncludingThis() to nsPluginArray, bug 674114.
// TODO: add SizeOfIncludingThis() to Geolocation, bug 674115.
// TODO: add SizeOfIncludingThis() to DesktopNotificationCenter, bug 674116.
return n;
}
void Navigator::SetWindow(nsPIDOMWindowInner* aInnerWindow) {
mWindow = aInnerWindow;
}
void Navigator::OnNavigation() {
if (!mWindow) {
return;
}
// If MediaManager is open let it inform any live streams or pending callbacks
MediaManager* manager = MediaManager::GetIfExists();
if (manager) {
manager->OnNavigation(mWindow->WindowID());
}
}
JSObject* Navigator::WrapObject(JSContext* cx,
JS::Handle<JSObject*> aGivenProto) {
return Navigator_Binding::Wrap(cx, this, aGivenProto);
}
/* static */
bool Navigator::HasUserMediaSupport(JSContext* cx, JSObject* obj) {
// Make enabling peerconnection enable getUserMedia() as well.
// Emulate [SecureContext] unless media.devices.insecure.enabled=true
return (StaticPrefs::media_navigator_enabled() ||
StaticPrefs::media_peerconnection_enabled()) &&
(IsSecureContextOrObjectIsFromSecureContext(cx, obj) ||
StaticPrefs::media_devices_insecure_enabled());
}
/* static */
already_AddRefed<nsPIDOMWindowInner> Navigator::GetWindowFromGlobal(
JSObject* aGlobal) {
nsCOMPtr<nsPIDOMWindowInner> win = xpc::WindowOrNull(aGlobal);
return win.forget();
}
nsresult Navigator::GetPlatform(nsAString& aPlatform,
nsIPrincipal* aCallerPrincipal,
bool aUsePrefOverriddenValue) {
MOZ_ASSERT(NS_IsMainThread());
if (aUsePrefOverriddenValue) {
// If fingerprinting resistance is on, we will spoof this value. See
// nsRFPService.h for details about spoofed values.
if (nsContentUtils::ShouldResistFingerprinting(aCallerPrincipal)) {
aPlatform.AssignLiteral(SPOOFED_PLATFORM);
return NS_OK;
}
nsAutoString override;
nsresult rv =
mozilla::Preferences::GetString("general.platform.override", override);
if (NS_SUCCEEDED(rv)) {
aPlatform = override;
return NS_OK;
}
}
nsresult rv;
nsCOMPtr<nsIHttpProtocolHandler> service(
do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
NS_ENSURE_SUCCESS(rv, rv);
// Sorry for the #if platform ugliness, but Communicator is likewise
// hardcoded and we are seeking backward compatibility here (bug 47080).
#if defined(WIN32)
aPlatform.AssignLiteral("Win32");
#elif defined(XP_MACOSX) && defined(__ppc__)
aPlatform.AssignLiteral("MacPPC");
#elif defined(XP_MACOSX) && defined(__i386__)
aPlatform.AssignLiteral("MacIntel");
#elif defined(XP_MACOSX) && defined(__x86_64__)
aPlatform.AssignLiteral("MacIntel");
#else
// XXX Communicator uses compiled-in build-time string defines
// to indicate the platform it was compiled *for*, not what it is
// currently running *on* which is what this does.
nsAutoCString plat;
rv = service->GetOscpu(plat);
CopyASCIItoUTF16(plat, aPlatform);
#endif
return rv;
}
/* static */
nsresult Navigator::GetAppVersion(nsAString& aAppVersion,
nsIPrincipal* aCallerPrincipal,
bool aUsePrefOverriddenValue) {
MOZ_ASSERT(NS_IsMainThread());
if (aUsePrefOverriddenValue) {
// If fingerprinting resistance is on, we will spoof this value. See
// nsRFPService.h for details about spoofed values.
if (nsContentUtils::ShouldResistFingerprinting(aCallerPrincipal)) {
aAppVersion.AssignLiteral(SPOOFED_APPVERSION);
return NS_OK;
}
nsAutoString override;
nsresult rv = mozilla::Preferences::GetString("general.appversion.override",
override);
if (NS_SUCCEEDED(rv)) {
aAppVersion = override;
return NS_OK;
}
}
nsresult rv;
nsCOMPtr<nsIHttpProtocolHandler> service(
do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString str;
rv = service->GetAppVersion(str);
CopyASCIItoUTF16(str, aAppVersion);
NS_ENSURE_SUCCESS(rv, rv);
aAppVersion.AppendLiteral(" (");
rv = service->GetPlatform(str);
NS_ENSURE_SUCCESS(rv, rv);
AppendASCIItoUTF16(str, aAppVersion);
aAppVersion.Append(char16_t(')'));
return rv;
}
/* static */
void Navigator::AppName(nsAString& aAppName, nsIPrincipal* aCallerPrincipal,
bool aUsePrefOverriddenValue) {
MOZ_ASSERT(NS_IsMainThread());
if (aUsePrefOverriddenValue) {
// If fingerprinting resistance is on, we will spoof this value. See
// nsRFPService.h for details about spoofed values.
if (nsContentUtils::ShouldResistFingerprinting(aCallerPrincipal)) {
aAppName.AssignLiteral(SPOOFED_APPNAME);
return;
}
nsAutoString override;
nsresult rv =
mozilla::Preferences::GetString("general.appname.override", override);
if (NS_SUCCEEDED(rv)) {
aAppName = override;
return;
}
}
aAppName.AssignLiteral("Netscape");
}
void Navigator::ClearUserAgentCache() {
Navigator_Binding::ClearCachedUserAgentValue(this);
}
nsresult Navigator::GetUserAgent(nsPIDOMWindowInner* aWindow,
nsIPrincipal* aCallerPrincipal,
bool aIsCallerChrome, nsAString& aUserAgent) {
MOZ_ASSERT(NS_IsMainThread());
// We will skip the override and pass to httpHandler to get spoofed userAgent
// when 'privacy.resistFingerprinting' is true.
if (!aIsCallerChrome &&
!nsContentUtils::ShouldResistFingerprinting(aCallerPrincipal)) {
nsAutoString override;
nsresult rv =
mozilla::Preferences::GetString("general.useragent.override", override);
if (NS_SUCCEEDED(rv)) {
aUserAgent = override;
return NS_OK;
}
}
// When the caller is content and 'privacy.resistFingerprinting' is true,
// return a spoofed userAgent which reveals the platform but not the
// specific OS version, etc.
if (!aIsCallerChrome &&
nsContentUtils::ShouldResistFingerprinting(aCallerPrincipal)) {
nsAutoCString spoofedUA;
nsRFPService::GetSpoofedUserAgent(spoofedUA, false);
CopyASCIItoUTF16(spoofedUA, aUserAgent);
return NS_OK;
}
nsresult rv;
nsCOMPtr<nsIHttpProtocolHandler> service(
do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsAutoCString ua;
rv = service->GetUserAgent(ua);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
CopyASCIItoUTF16(ua, aUserAgent);
// When the caller is content, we will always return spoofed userAgent and
// ignore the User-Agent header from the document channel when
// 'privacy.resistFingerprinting' is true.
if (!aWindow ||
(nsContentUtils::ShouldResistFingerprinting(aCallerPrincipal) &&
!aIsCallerChrome)) {
return NS_OK;
}
// Copy the User-Agent header from the document channel which has already been
// subject to UA overrides.
nsCOMPtr<Document> doc = aWindow->GetExtantDoc();
if (!doc) {
return NS_OK;
}
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(doc->GetChannel());
if (httpChannel) {
nsAutoCString userAgent;
rv = httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("User-Agent"),
userAgent);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
CopyASCIItoUTF16(userAgent, aUserAgent);
}
return NS_OK;
}
static nsCString RequestKeySystemAccessLogString(
const nsAString& aKeySystem,
const Sequence<MediaKeySystemConfiguration>& aConfigs,
bool aIsSecureContext) {
nsCString str;
str.AppendPrintf(
"Navigator::RequestMediaKeySystemAccess(keySystem='%s' options=",
NS_ConvertUTF16toUTF8(aKeySystem).get());
str.Append(MediaKeySystemAccess::ToCString(aConfigs));
str.AppendLiteral(") secureContext=");
str.AppendInt(aIsSecureContext);
return str;
}
already_AddRefed<Promise> Navigator::RequestMediaKeySystemAccess(
const nsAString& aKeySystem,
const Sequence<MediaKeySystemConfiguration>& aConfigs, ErrorResult& aRv) {
EME_LOG("%s", RequestKeySystemAccessLogString(aKeySystem, aConfigs,
mWindow->IsSecureContext())
.get());
Telemetry::Accumulate(Telemetry::MEDIA_EME_SECURE_CONTEXT,
mWindow->IsSecureContext());
if (!mWindow->IsSecureContext()) {
Document* doc = mWindow->GetExtantDoc();
AutoTArray<nsString, 1> params;
nsString* uri = params.AppendElement();
if (doc) {
Unused << doc->GetDocumentURI(*uri);
}
nsContentUtils::ReportToConsole(
nsIScriptError::warningFlag, NS_LITERAL_CSTRING("Media"), doc,
nsContentUtils::eDOM_PROPERTIES,
"MediaEMEInsecureContextDeprecatedWarning", params);
}
Document* doc = mWindow->GetExtantDoc();
if (doc && !FeaturePolicyUtils::IsFeatureAllowed(
doc, NS_LITERAL_STRING("encrypted-media"))) {
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
return nullptr;
}
RefPtr<DetailedPromise> promise = DetailedPromise::Create(
mWindow->AsGlobal(), aRv,
NS_LITERAL_CSTRING("navigator.requestMediaKeySystemAccess"),
Telemetry::VIDEO_EME_REQUEST_SUCCESS_LATENCY_MS,
Telemetry::VIDEO_EME_REQUEST_FAILURE_LATENCY_MS);
if (aRv.Failed()) {
return nullptr;
}
if (!mMediaKeySystemAccessManager) {
mMediaKeySystemAccessManager = new MediaKeySystemAccessManager(mWindow);
}
mMediaKeySystemAccessManager->Request(promise, aKeySystem, aConfigs);
return promise.forget();
}
Presentation* Navigator::GetPresentation(ErrorResult& aRv) {
if (!mPresentation) {
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
mPresentation = Presentation::Create(mWindow);
}
return mPresentation;
}
CredentialsContainer* Navigator::Credentials() {
if (!mCredentials) {
mCredentials = new CredentialsContainer(GetWindow());
}
return mCredentials;
}
dom::MediaCapabilities* Navigator::MediaCapabilities() {
if (!mMediaCapabilities) {
mMediaCapabilities = new dom::MediaCapabilities(GetWindow()->AsGlobal());
}
return mMediaCapabilities;
}
Clipboard* Navigator::Clipboard() {
if (!mClipboard) {
mClipboard = new dom::Clipboard(GetWindow());
}
return mClipboard;
}
/* static */
bool Navigator::Webdriver() {
return Preferences::GetBool("marionette.enabled", false);
}
} // namespace dom
} // namespace mozilla