gecko-dev/dom/base/Navigator.cpp

2040 lines
51 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set sw=2 ts=2 et tw=78: */
/* 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/MemoryReporting.h"
#include "mozilla/dom/DesktopNotification.h"
#include "nsGeolocation.h"
#include "nsIHttpProtocolHandler.h"
#include "nsICachingChannel.h"
#include "nsIWebContentHandlerRegistrar.h"
#include "nsICookiePermission.h"
#include "nsIScriptSecurityManager.h"
#include "nsCharSeparatedTokenizer.h"
#include "nsContentUtils.h"
#include "nsUnicharUtils.h"
#include "mozilla/Preferences.h"
#include "mozilla/Telemetry.h"
#include "BatteryManager.h"
#include "mozilla/dom/PowerManager.h"
#include "mozilla/dom/WakeLock.h"
#include "mozilla/dom/power/PowerManagerService.h"
#include "mozilla/dom/MobileMessageManager.h"
#include "mozilla/dom/Telephony.h"
#include "mozilla/Hal.h"
#include "nsISiteSpecificUserAgent.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/StaticPtr.h"
#include "Connection.h"
#include "nsDOMEvent.h"
#include "nsGlobalWindow.h"
#ifdef MOZ_B2G_RIL
#include "mozilla/dom/IccManager.h"
#include "mozilla/dom/CellBroadcast.h"
#include "mozilla/dom/MobileConnectionArray.h"
#include "mozilla/dom/Voicemail.h"
#endif
#include "nsIIdleObserver.h"
#include "nsIPermissionManager.h"
#include "nsNetUtil.h"
#include "nsIHttpChannel.h"
#include "TimeManager.h"
#include "DeviceStorage.h"
#include "nsIDOMNavigatorSystemMessages.h"
#include "nsIAppsService.h"
#include "mozIApplication.h"
#ifdef MOZ_MEDIA_NAVIGATOR
#include "MediaManager.h"
#endif
#ifdef MOZ_B2G_BT
#include "BluetoothManager.h"
#endif
#include "DOMCameraManager.h"
#ifdef MOZ_AUDIO_CHANNEL_MANAGER
#include "AudioChannelManager.h"
#endif
#ifdef MOZ_B2G_FM
#include "mozilla/dom/FMRadio.h"
#endif
#include "nsIDOMGlobalPropertyInitializer.h"
#include "nsIDataStoreService.h"
#include "nsJSUtils.h"
#include "nsScriptNameSpaceManager.h"
#include "mozilla/dom/NavigatorBinding.h"
#include "mozilla/dom/Promise.h"
namespace mozilla {
namespace dom {
static bool sDoNotTrackEnabled = false;
static bool sVibratorEnabled = false;
static uint32_t sMaxVibrateMS = 0;
static uint32_t sMaxVibrateListLen = 0;
/* static */
void
Navigator::Init()
{
Preferences::AddBoolVarCache(&sDoNotTrackEnabled,
"privacy.donottrackheader.enabled",
false);
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(nsPIDOMWindow* aWindow)
: mWindow(aWindow)
{
MOZ_ASSERT(aWindow->IsInnerWindow(), "Navigator must get an inner window!");
SetIsDOMBinding();
}
Navigator::~Navigator()
{
Invalidate();
}
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Navigator)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMNavigator)
NS_INTERFACE_MAP_ENTRY(nsIDOMNavigator)
NS_INTERFACE_MAP_ENTRY(nsIMozNavigatorNetwork)
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(mPlugins)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMimeTypes)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGeolocation)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotification)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBatteryManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPowerManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMobileMessageManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTelephony)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConnection)
#ifdef MOZ_B2G_RIL
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMobileConnections)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCellBroadcast)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIccManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVoicemail)
#endif
#ifdef MOZ_B2G_BT
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBluetooth)
#endif
#ifdef MOZ_AUDIO_CHANNEL_MANAGER
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAudioChannelManager)
#endif
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCameraManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessagesManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDeviceStorageStores)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTimeManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
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.
if (mPlugins) {
mPlugins->Invalidate();
mPlugins = nullptr;
}
mMimeTypes = nullptr;
// If there is a page transition, make sure delete the geolocation object.
if (mGeolocation) {
mGeolocation->Shutdown();
mGeolocation = nullptr;
}
if (mNotification) {
mNotification->Shutdown();
mNotification = nullptr;
}
if (mBatteryManager) {
mBatteryManager->Shutdown();
mBatteryManager = nullptr;
}
#ifdef MOZ_B2G_FM
if (mFMRadio) {
mFMRadio->Shutdown();
mFMRadio = nullptr;
}
#endif
if (mPowerManager) {
mPowerManager->Shutdown();
mPowerManager = nullptr;
}
if (mMobileMessageManager) {
mMobileMessageManager->Shutdown();
mMobileMessageManager = nullptr;
}
if (mTelephony) {
mTelephony = nullptr;
}
if (mConnection) {
mConnection->Shutdown();
mConnection = nullptr;
}
#ifdef MOZ_B2G_RIL
if (mMobileConnections) {
mMobileConnections = nullptr;
}
if (mCellBroadcast) {
mCellBroadcast = nullptr;
}
if (mIccManager) {
mIccManager->Shutdown();
mIccManager = nullptr;
}
if (mVoicemail) {
mVoicemail = nullptr;
}
#endif
#ifdef MOZ_B2G_BT
if (mBluetooth) {
mBluetooth = nullptr;
}
#endif
mCameraManager = nullptr;
if (mMessagesManager) {
mMessagesManager = nullptr;
}
#ifdef MOZ_AUDIO_CHANNEL_MANAGER
if (mAudioChannelManager) {
mAudioChannelManager = nullptr;
}
#endif
uint32_t len = mDeviceStorageStores.Length();
for (uint32_t i = 0; i < len; ++i) {
mDeviceStorageStores[i]->Shutdown();
}
mDeviceStorageStores.Clear();
if (mTimeManager) {
mTimeManager = nullptr;
}
}
//*****************************************************************************
// Navigator::nsIDOMNavigator
//*****************************************************************************
NS_IMETHODIMP
Navigator::GetUserAgent(nsAString& aUserAgent)
{
nsresult rv = NS_GetNavigatorUserAgent(aUserAgent);
NS_ENSURE_SUCCESS(rv, rv);
if (!mWindow || !mWindow->GetDocShell()) {
return NS_OK;
}
nsIDocument* doc = mWindow->GetExtantDoc();
if (!doc) {
return NS_OK;
}
nsCOMPtr<nsIURI> codebaseURI;
doc->NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
if (!codebaseURI) {
return NS_OK;
}
nsCOMPtr<nsISiteSpecificUserAgent> siteSpecificUA =
do_GetService("@mozilla.org/dom/site-specific-user-agent;1");
NS_ENSURE_TRUE(siteSpecificUA, NS_OK);
return siteSpecificUA->GetUserAgentForURIAndWindow(codebaseURI, mWindow,
aUserAgent);
}
NS_IMETHODIMP
Navigator::GetAppCodeName(nsAString& aAppCodeName)
{
nsresult rv;
nsCOMPtr<nsIHttpProtocolHandler>
service(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString appName;
rv = service->GetAppName(appName);
CopyASCIItoUTF16(appName, aAppCodeName);
return rv;
}
NS_IMETHODIMP
Navigator::GetAppVersion(nsAString& aAppVersion)
{
return NS_GetNavigatorAppVersion(aAppVersion);
}
NS_IMETHODIMP
Navigator::GetAppName(nsAString& aAppName)
{
NS_GetNavigatorAppName(aAppName);
return NS_OK;
}
/**
* JS property navigator.language, exposed to web content.
* Take first value from Accept-Languages (HTTP header), which is
* the "content language" freely set by the user in the Pref window.
*
* Do not use UI language (chosen app locale) here.
* See RFC 2616, Section 15.1.4 "Privacy Issues Connected to Accept Headers"
*
* "en", "en-US" and "i-cherokee" and "" are valid.
* Fallback in case of invalid pref should be "" (empty string), to
* let site do fallback, e.g. to site's local language.
*/
NS_IMETHODIMP
Navigator::GetLanguage(nsAString& aLanguage)
{
// E.g. "de-de, en-us,en".
const nsAdoptingString& acceptLang =
Preferences::GetLocalizedString("intl.accept_languages");
// Take everything before the first "," or ";", without trailing space.
nsCharSeparatedTokenizer langTokenizer(acceptLang, ',');
const nsSubstring &firstLangPart = langTokenizer.nextToken();
nsCharSeparatedTokenizer qTokenizer(firstLangPart, ';');
aLanguage.Assign(qTokenizer.nextToken());
// Checks and fixups:
// replace "_" with "-" to avoid POSIX/Windows "en_US" notation.
if (aLanguage.Length() > 2 && aLanguage[2] == char16_t('_')) {
aLanguage.Replace(2, 1, char16_t('-')); // TODO replace all
}
// 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".
if (aLanguage.Length() <= 2) {
return NS_OK;
}
nsCharSeparatedTokenizer localeTokenizer(aLanguage, '-');
int32_t pos = 0;
bool first = true;
while (localeTokenizer.hasMoreTokens()) {
const nsSubstring& code = localeTokenizer.nextToken();
if (code.Length() == 2 && !first) {
nsAutoString upper(code);
ToUpperCase(upper);
aLanguage.Replace(pos, code.Length(), upper);
}
pos += code.Length() + 1; // 1 is the separator
first = false;
}
return NS_OK;
}
NS_IMETHODIMP
Navigator::GetPlatform(nsAString& aPlatform)
{
return NS_GetNavigatorPlatform(aPlatform);
}
NS_IMETHODIMP
Navigator::GetOscpu(nsAString& aOSCPU)
{
if (!nsContentUtils::IsCallerChrome()) {
const nsAdoptingString& override =
Preferences::GetString("general.oscpu.override");
if (override) {
aOSCPU = override;
return NS_OK;
}
}
nsresult rv;
nsCOMPtr<nsIHttpProtocolHandler>
service(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString oscpu;
rv = service->GetOscpu(oscpu);
CopyASCIItoUTF16(oscpu, aOSCPU);
return rv;
}
NS_IMETHODIMP
Navigator::GetVendor(nsAString& aVendor)
{
aVendor.Truncate();
return NS_OK;
}
NS_IMETHODIMP
Navigator::GetVendorSub(nsAString& aVendorSub)
{
aVendorSub.Truncate();
return NS_OK;
}
NS_IMETHODIMP
Navigator::GetProduct(nsAString& aProduct)
{
aProduct.AssignLiteral("Gecko");
return NS_OK;
}
NS_IMETHODIMP
Navigator::GetProductSub(nsAString& aProductSub)
{
// Legacy build ID hardcoded for backward compatibility (bug 776376)
aProductSub.AssignLiteral("20100101");
return NS_OK;
}
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;
}
// Values for the network.cookie.cookieBehavior pref are documented in
// nsCookieService.cpp.
#define COOKIE_BEHAVIOR_REJECT 2
bool
Navigator::CookieEnabled()
{
bool cookieEnabled =
(Preferences::GetInt("network.cookie.cookieBehavior",
COOKIE_BEHAVIOR_REJECT) != COOKIE_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<nsIDocument> doc = mWindow->GetExtantDoc();
if (!doc) {
return cookieEnabled;
}
nsCOMPtr<nsIURI> codebaseURI;
doc->NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
if (!codebaseURI) {
// Not a codebase, so technically can't set cookies, but let's
// just return the default value.
return cookieEnabled;
}
nsCOMPtr<nsICookiePermission> permMgr =
do_GetService(NS_COOKIEPERMISSION_CONTRACTID);
NS_ENSURE_TRUE(permMgr, cookieEnabled);
// Pass null for the channel, just like the cookie service does.
nsCookieAccess access;
nsresult rv = permMgr->CanAccess(codebaseURI, nullptr, &access);
NS_ENSURE_SUCCESS(rv, cookieEnabled);
if (access != nsICookiePermission::ACCESS_DEFAULT) {
cookieEnabled = access != nsICookiePermission::ACCESS_DENY;
}
return cookieEnabled;
}
bool
Navigator::OnLine()
{
return !NS_IsOffline();
}
NS_IMETHODIMP
Navigator::GetBuildID(nsAString& aBuildID)
{
if (!nsContentUtils::IsCallerChrome()) {
const nsAdoptingString& override =
Preferences::GetString("general.buildID.override");
if (override) {
aBuildID = override;
return NS_OK;
}
}
nsCOMPtr<nsIXULAppInfo> appInfo =
do_GetService("@mozilla.org/xre/app-info;1");
if (!appInfo) {
return NS_ERROR_NOT_IMPLEMENTED;
}
nsAutoCString buildID;
nsresult rv = appInfo->GetAppBuildID(buildID);
if (NS_FAILED(rv)) {
return rv;
}
aBuildID.Truncate();
AppendASCIItoUTF16(buildID, aBuildID);
return NS_OK;
}
NS_IMETHODIMP
Navigator::GetDoNotTrack(nsAString &aResult)
{
if (sDoNotTrackEnabled) {
aResult.AssignLiteral("yes");
} else {
aResult.AssignLiteral("unspecified");
}
return NS_OK;
}
bool
Navigator::JavaEnabled(ErrorResult& aRv)
{
Telemetry::AutoTimer<Telemetry::CHECK_JAVA_ENABLED> telemetryTimer;
// Return true if we have a handler for "application/x-java-vm",
// otherwise return false.
if (!mMimeTypes) {
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return false;
}
mMimeTypes = new nsMimeTypeArray(mWindow);
}
RefreshMIMEArray();
nsMimeType *mimeType =
mMimeTypes->NamedItem(NS_LITERAL_STRING("application/x-java-vm"));
return mimeType && mimeType->GetEnabledPlugin();
}
void
Navigator::RefreshMIMEArray()
{
if (mMimeTypes) {
mMimeTypes->Refresh();
}
}
bool
Navigator::HasDesktopNotificationSupport()
{
return Preferences::GetBool("notification.feature.enabled", false);
}
namespace {
class VibrateWindowListener : public nsIDOMEventListener
{
public:
VibrateWindowListener(nsIDOMWindow* aWindow, nsIDocument* 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 */);
}
virtual ~VibrateWindowListener()
{
}
void RemoveListener();
NS_DECL_ISUPPORTS
NS_DECL_NSIDOMEVENTLISTENER
private:
nsWeakPtr mWindow;
nsWeakPtr mDocument;
};
NS_IMPL_ISUPPORTS1(VibrateWindowListener, nsIDOMEventListener)
StaticRefPtr<VibrateWindowListener> gVibrateWindowListener;
NS_IMETHODIMP
VibrateWindowListener::HandleEvent(nsIDOMEvent* aEvent)
{
nsCOMPtr<nsIDocument> doc =
do_QueryInterface(aEvent->InternalDOMEvent()->GetTarget());
if (!doc || doc->Hidden()) {
// 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<nsIDOMWindow> 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 */);
}
} // anonymous namespace
void
Navigator::AddIdleObserver(MozIdleObserver& aIdleObserver, ErrorResult& aRv)
{
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return;
}
CallbackObjectHolder<MozIdleObserver, nsIIdleObserver> holder(&aIdleObserver);
nsCOMPtr<nsIIdleObserver> obs = holder.ToXPCOMCallback();
if (NS_FAILED(mWindow->RegisterIdleObserver(obs))) {
NS_WARNING("Failed to add idle observer.");
}
}
void
Navigator::RemoveIdleObserver(MozIdleObserver& aIdleObserver, ErrorResult& aRv)
{
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return;
}
CallbackObjectHolder<MozIdleObserver, nsIIdleObserver> holder(&aIdleObserver);
nsCOMPtr<nsIIdleObserver> obs = holder.ToXPCOMCallback();
if (NS_FAILED(mWindow->UnregisterIdleObserver(obs))) {
NS_WARNING("Failed to remove idle observer.");
}
}
bool
Navigator::Vibrate(uint32_t aDuration)
{
nsAutoTArray<uint32_t, 1> pattern;
pattern.AppendElement(aDuration);
return Vibrate(pattern);
}
bool
Navigator::Vibrate(const nsTArray<uint32_t>& aPattern)
{
if (!mWindow) {
return false;
}
nsCOMPtr<nsIDocument> doc = mWindow->GetExtantDoc();
if (!doc) {
return false;
}
if (doc->Hidden()) {
// Hidden documents cannot start or stop a vibration.
return false;
}
if (aPattern.Length() > sMaxVibrateListLen) {
return false;
}
for (size_t i = 0; i < aPattern.Length(); ++i) {
if (aPattern[i] > sMaxVibrateMS) {
return false;
}
}
// The spec says we check sVibratorEnabled after we've done the sanity
// checking on the pattern.
if (aPattern.IsEmpty() || !sVibratorEnabled) {
return true;
}
// 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(aPattern, mWindow);
return true;
}
//*****************************************************************************
// Navigator::nsIDOMClientInformation
//*****************************************************************************
void
Navigator::RegisterContentHandler(const nsAString& aMIMEType,
const nsAString& aURI,
const nsAString& aTitle,
ErrorResult& aRv)
{
if (!mWindow || !mWindow->GetOuterWindow() || !mWindow->GetDocShell()) {
return;
}
nsCOMPtr<nsIWebContentHandlerRegistrar> registrar =
do_GetService(NS_WEBCONTENTHANDLERREGISTRAR_CONTRACTID);
if (!registrar) {
return;
}
aRv = registrar->RegisterContentHandler(aMIMEType, aURI, aTitle,
mWindow->GetOuterWindow());
}
void
Navigator::RegisterProtocolHandler(const nsAString& aProtocol,
const nsAString& aURI,
const nsAString& aTitle,
ErrorResult& aRv)
{
if (!mWindow || !mWindow->GetOuterWindow() || !mWindow->GetDocShell()) {
return;
}
nsCOMPtr<nsIWebContentHandlerRegistrar> registrar =
do_GetService(NS_WEBCONTENTHANDLERREGISTRAR_CONTRACTID);
if (!registrar) {
return;
}
aRv = registrar->RegisterProtocolHandler(aProtocol, aURI, aTitle,
mWindow->GetOuterWindow());
}
bool
Navigator::MozIsLocallyAvailable(const nsAString &aURI,
bool aWhenOffline,
ErrorResult& aRv)
{
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri), aURI);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return false;
}
// This method of checking the cache will only work for http/https urls.
bool match;
rv = uri->SchemeIs("http", &match);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return false;
}
if (!match) {
rv = uri->SchemeIs("https", &match);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return false;
}
if (!match) {
aRv.Throw(NS_ERROR_DOM_BAD_URI);
return false;
}
}
// Same origin check.
JSContext *cx = nsContentUtils::GetCurrentJSContext();
if (!cx) {
aRv.Throw(NS_ERROR_FAILURE);
return false;
}
rv = nsContentUtils::GetSecurityManager()->CheckSameOrigin(cx, uri);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return false;
}
// These load flags cause an error to be thrown if there is no
// valid cache entry, and skip the load if there is.
// If the cache is busy, assume that it is not yet available rather
// than waiting for it to become available.
uint32_t loadFlags = nsIChannel::INHIBIT_CACHING |
nsICachingChannel::LOAD_NO_NETWORK_IO |
nsICachingChannel::LOAD_ONLY_IF_MODIFIED |
nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY;
if (aWhenOffline) {
loadFlags |= nsICachingChannel::LOAD_CHECK_OFFLINE_CACHE |
nsICachingChannel::LOAD_ONLY_FROM_CACHE |
nsIRequest::LOAD_FROM_CACHE;
}
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return false;
}
nsCOMPtr<nsILoadGroup> loadGroup;
nsCOMPtr<nsIDocument> doc = mWindow->GetDoc();
if (doc) {
loadGroup = doc->GetDocumentLoadGroup();
}
nsCOMPtr<nsIChannel> channel;
rv = NS_NewChannel(getter_AddRefs(channel), uri,
nullptr, loadGroup, nullptr, loadFlags);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return false;
}
nsCOMPtr<nsIInputStream> stream;
rv = channel->Open(getter_AddRefs(stream));
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return false;
}
stream->Close();
nsresult status;
rv = channel->GetStatus(&status);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return false;
}
if (NS_FAILED(status)) {
return false;
}
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
bool isAvailable;
rv = httpChannel->GetRequestSucceeded(&isAvailable);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return false;
}
return isAvailable;
}
nsDOMDeviceStorage*
Navigator::GetDeviceStorage(const nsAString& aType, ErrorResult& aRv)
{
if (!mWindow || !mWindow->GetOuterWindow() || !mWindow->GetDocShell()) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsRefPtr<nsDOMDeviceStorage> storage;
nsDOMDeviceStorage::CreateDeviceStorageFor(mWindow, aType,
getter_AddRefs(storage));
if (!storage) {
return nullptr;
}
mDeviceStorageStores.AppendElement(storage);
return storage;
}
void
Navigator::GetDeviceStorages(const nsAString& aType,
nsTArray<nsRefPtr<nsDOMDeviceStorage> >& aStores,
ErrorResult& aRv)
{
if (!mWindow || !mWindow->GetOuterWindow() || !mWindow->GetDocShell()) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
nsDOMDeviceStorage::CreateDeviceStoragesFor(mWindow, aType, aStores);
mDeviceStorageStores.AppendElements(aStores);
}
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->GetOuterWindow()))) {
mGeolocation = nullptr;
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
return mGeolocation;
}
#ifdef MOZ_MEDIA_NAVIGATOR
void
Navigator::MozGetUserMedia(JSContext* aCx,
const MediaStreamConstraints& aConstraints,
NavigatorUserMediaSuccessCallback& aOnSuccess,
NavigatorUserMediaErrorCallback& aOnError,
ErrorResult& aRv)
{
CallbackObjectHolder<NavigatorUserMediaSuccessCallback,
nsIDOMGetUserMediaSuccessCallback> holder1(&aOnSuccess);
nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> onsuccess =
holder1.ToXPCOMCallback();
CallbackObjectHolder<NavigatorUserMediaErrorCallback,
nsIDOMGetUserMediaErrorCallback> holder2(&aOnError);
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onerror = holder2.ToXPCOMCallback();
if (!mWindow || !mWindow->GetOuterWindow() ||
mWindow->GetOuterWindow()->GetCurrentInnerWindow() != mWindow) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return;
}
bool privileged = nsContentUtils::IsChromeDoc(mWindow->GetExtantDoc());
MediaManager* manager = MediaManager::Get();
aRv = manager->GetUserMedia(aCx, privileged, mWindow, aConstraints,
onsuccess, onerror);
}
void
Navigator::MozGetUserMediaDevices(const MediaStreamConstraintsInternal& aConstraints,
MozGetUserMediaDevicesSuccessCallback& aOnSuccess,
NavigatorUserMediaErrorCallback& aOnError,
ErrorResult& aRv)
{
CallbackObjectHolder<MozGetUserMediaDevicesSuccessCallback,
nsIGetUserMediaDevicesSuccessCallback> holder1(&aOnSuccess);
nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> onsuccess =
holder1.ToXPCOMCallback();
CallbackObjectHolder<NavigatorUserMediaErrorCallback,
nsIDOMGetUserMediaErrorCallback> holder2(&aOnError);
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onerror = holder2.ToXPCOMCallback();
if (!mWindow || !mWindow->GetOuterWindow() ||
mWindow->GetOuterWindow()->GetCurrentInnerWindow() != mWindow) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return;
}
MediaManager* manager = MediaManager::Get();
aRv = manager->GetUserMediaDevices(mWindow, aConstraints, onsuccess, onerror);
}
#endif
DesktopNotificationCenter*
Navigator::GetMozNotification(ErrorResult& aRv)
{
if (mNotification) {
return mNotification;
}
if (!mWindow || !mWindow->GetDocShell()) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
mNotification = new DesktopNotificationCenter(mWindow);
return mNotification;
}
#ifdef MOZ_B2G_FM
using mozilla::dom::FMRadio;
FMRadio*
Navigator::GetMozFMRadio(ErrorResult& aRv)
{
if (!mFMRadio) {
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
NS_ENSURE_TRUE(mWindow->GetDocShell(), nullptr);
mFMRadio = new FMRadio();
mFMRadio->Init(mWindow);
}
return mFMRadio;
}
#endif // MOZ_B2G_FM
//*****************************************************************************
// Navigator::nsINavigatorBattery
//*****************************************************************************
battery::BatteryManager*
Navigator::GetBattery(ErrorResult& aRv)
{
if (!mBatteryManager) {
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
NS_ENSURE_TRUE(mWindow->GetDocShell(), nullptr);
mBatteryManager = new battery::BatteryManager(mWindow);
mBatteryManager->Init();
}
return mBatteryManager;
}
already_AddRefed<Promise>
Navigator::GetDataStores(const nsAString& aName, ErrorResult& aRv)
{
if (!mWindow || !mWindow->GetDocShell()) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
nsCOMPtr<nsIDataStoreService> service =
do_GetService("@mozilla.org/datastore-service;1");
if (!service) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsCOMPtr<nsISupports> promise;
aRv = service->GetDataStores(mWindow, aName, getter_AddRefs(promise));
nsRefPtr<Promise> p = static_cast<Promise*>(promise.get());
return p.forget();
}
PowerManager*
Navigator::GetMozPower(ErrorResult& aRv)
{
if (!mPowerManager) {
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
mPowerManager = PowerManager::CreateInstance(mWindow);
if (!mPowerManager) {
// We failed to get the power manager service?
aRv.Throw(NS_ERROR_UNEXPECTED);
}
}
return mPowerManager;
}
already_AddRefed<WakeLock>
Navigator::RequestWakeLock(const nsAString &aTopic, ErrorResult& aRv)
{
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
nsRefPtr<power::PowerManagerService> pmService =
power::PowerManagerService::GetInstance();
// Maybe it went away for some reason... Or maybe we're just called
// from our XPCOM method.
NS_ENSURE_TRUE(pmService, nullptr);
ErrorResult rv;
return pmService->NewWakeLock(aTopic, mWindow, rv);
}
nsIDOMMozMobileMessageManager*
Navigator::GetMozMobileMessage()
{
if (!mMobileMessageManager) {
// Check that our window has not gone away
NS_ENSURE_TRUE(mWindow, nullptr);
NS_ENSURE_TRUE(mWindow->GetDocShell(), nullptr);
mMobileMessageManager = new MobileMessageManager();
mMobileMessageManager->Init(mWindow);
}
return mMobileMessageManager;
}
Telephony*
Navigator::GetMozTelephony(ErrorResult& aRv)
{
if (!mTelephony) {
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
mTelephony = Telephony::Create(mWindow, aRv);
}
return mTelephony;
}
#ifdef MOZ_B2G_RIL
MobileConnectionArray*
Navigator::GetMozMobileConnections(ErrorResult& aRv)
{
if (!mMobileConnections) {
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
mMobileConnections = new MobileConnectionArray(mWindow);
}
return mMobileConnections;
}
CellBroadcast*
Navigator::GetMozCellBroadcast(ErrorResult& aRv)
{
if (!mCellBroadcast) {
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
mCellBroadcast = CellBroadcast::Create(mWindow, aRv);
}
return mCellBroadcast;
}
Voicemail*
Navigator::GetMozVoicemail(ErrorResult& aRv)
{
if (!mVoicemail) {
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
aRv = NS_NewVoicemail(mWindow, getter_AddRefs(mVoicemail));
if (aRv.Failed()) {
return nullptr;
}
}
return mVoicemail;
}
nsIDOMMozIccManager*
Navigator::GetMozIccManager(ErrorResult& aRv)
{
if (!mIccManager) {
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
NS_ENSURE_TRUE(mWindow->GetDocShell(), nullptr);
mIccManager = new IccManager(mWindow);
}
return mIccManager;
}
#endif // MOZ_B2G_RIL
#ifdef MOZ_GAMEPAD
void
Navigator::GetGamepads(nsTArray<nsRefPtr<Gamepad> >& aGamepads,
ErrorResult& aRv)
{
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return;
}
NS_ENSURE_TRUE_VOID(mWindow->GetDocShell());
nsGlobalWindow* win = static_cast<nsGlobalWindow*>(mWindow.get());
win->SetHasGamepadEventListener(true);
win->GetGamepads(aGamepads);
}
#endif
//*****************************************************************************
// Navigator::nsIMozNavigatorNetwork
//*****************************************************************************
NS_IMETHODIMP
Navigator::GetMozConnection(nsISupports** aConnection)
{
nsCOMPtr<nsINetworkProperties> properties = GetMozConnection();
properties.forget(aConnection);
return NS_OK;
}
network::Connection*
Navigator::GetMozConnection()
{
if (!mConnection) {
NS_ENSURE_TRUE(mWindow, nullptr);
NS_ENSURE_TRUE(mWindow->GetDocShell(), nullptr);
mConnection = new network::Connection();
mConnection->Init(mWindow);
}
return mConnection;
}
#ifdef MOZ_B2G_BT
bluetooth::BluetoothManager*
Navigator::GetMozBluetooth(ErrorResult& aRv)
{
if (!mBluetooth) {
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
mBluetooth = bluetooth::BluetoothManager::Create(mWindow);
}
return mBluetooth;
}
#endif //MOZ_B2G_BT
nsresult
Navigator::EnsureMessagesManager()
{
if (mMessagesManager) {
return NS_OK;
}
NS_ENSURE_STATE(mWindow);
nsresult rv;
nsCOMPtr<nsIDOMNavigatorSystemMessages> messageManager =
do_CreateInstance("@mozilla.org/system-message-manager;1", &rv);
nsCOMPtr<nsIDOMGlobalPropertyInitializer> gpi =
do_QueryInterface(messageManager);
NS_ENSURE_TRUE(gpi, NS_ERROR_FAILURE);
// We don't do anything with the return value.
AutoJSContext cx;
JS::Rooted<JS::Value> prop_val(cx);
rv = gpi->Init(mWindow, &prop_val);
NS_ENSURE_SUCCESS(rv, rv);
mMessagesManager = messageManager.forget();
return NS_OK;
}
bool
Navigator::MozHasPendingMessage(const nsAString& aType, ErrorResult& aRv)
{
// The WebIDL binding is responsible for the pref check here.
nsresult rv = EnsureMessagesManager();
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return false;
}
bool result = false;
rv = mMessagesManager->MozHasPendingMessage(aType, &result);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return false;
}
return result;
}
void
Navigator::MozSetMessageHandler(const nsAString& aType,
systemMessageCallback* aCallback,
ErrorResult& aRv)
{
// The WebIDL binding is responsible for the pref check here.
nsresult rv = EnsureMessagesManager();
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return;
}
CallbackObjectHolder<systemMessageCallback, nsIDOMSystemMessageCallback>
holder(aCallback);
nsCOMPtr<nsIDOMSystemMessageCallback> callback = holder.ToXPCOMCallback();
rv = mMessagesManager->MozSetMessageHandler(aType, callback);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
}
}
#ifdef MOZ_TIME_MANAGER
time::TimeManager*
Navigator::GetMozTime(ErrorResult& aRv)
{
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
if (!mTimeManager) {
mTimeManager = new time::TimeManager(mWindow);
}
return mTimeManager;
}
#endif
nsDOMCameraManager*
Navigator::GetMozCameras(ErrorResult& aRv)
{
if (!mCameraManager) {
if (!mWindow ||
!mWindow->GetOuterWindow() ||
mWindow->GetOuterWindow()->GetCurrentInnerWindow() != mWindow) {
aRv.Throw(NS_ERROR_NOT_AVAILABLE);
return nullptr;
}
mCameraManager = nsDOMCameraManager::CreateInstance(mWindow);
}
return mCameraManager;
}
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(nsPIDOMWindow *aInnerWindow)
{
NS_ASSERTION(aInnerWindow->IsInnerWindow(),
"Navigator must get an inner window!");
mWindow = aInnerWindow;
}
void
Navigator::OnNavigation()
{
if (!mWindow) {
return;
}
#ifdef MOZ_MEDIA_NAVIGATOR
// Inform MediaManager in case there are live streams or pending callbacks.
MediaManager *manager = MediaManager::Get();
if (manager) {
manager->OnNavigation(mWindow->WindowID());
}
#endif
if (mCameraManager) {
mCameraManager->OnNavigation(mWindow->WindowID());
}
}
bool
Navigator::CheckPermission(const char* type)
{
return CheckPermission(mWindow, type);
}
/* static */
bool
Navigator::CheckPermission(nsPIDOMWindow* aWindow, const char* aType)
{
if (!aWindow) {
return false;
}
nsCOMPtr<nsIPermissionManager> permMgr =
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
NS_ENSURE_TRUE(permMgr, false);
uint32_t permission = nsIPermissionManager::DENY_ACTION;
permMgr->TestPermissionFromWindow(aWindow, aType, &permission);
return permission == nsIPermissionManager::ALLOW_ACTION;
}
#ifdef MOZ_AUDIO_CHANNEL_MANAGER
system::AudioChannelManager*
Navigator::GetMozAudioChannelManager(ErrorResult& aRv)
{
if (!mAudioChannelManager) {
if (!mWindow) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
mAudioChannelManager = new system::AudioChannelManager();
mAudioChannelManager->Init(mWindow);
}
return mAudioChannelManager;
}
#endif
bool
Navigator::DoNewResolve(JSContext* aCx, JS::Handle<JSObject*> aObject,
JS::Handle<jsid> aId,
JS::MutableHandle<JS::Value> aValue)
{
if (!JSID_IS_STRING(aId)) {
return true;
}
nsScriptNameSpaceManager* nameSpaceManager = GetNameSpaceManager();
if (!nameSpaceManager) {
return Throw(aCx, NS_ERROR_NOT_INITIALIZED);
}
nsDependentJSString name(aId);
const nsGlobalNameStruct* name_struct =
nameSpaceManager->LookupNavigatorName(name);
if (!name_struct) {
return true;
}
if (name_struct->mType == nsGlobalNameStruct::eTypeNewDOMBinding) {
ConstructNavigatorProperty construct = name_struct->mConstructNavigatorProperty;
MOZ_ASSERT(construct);
JS::Rooted<JSObject*> naviObj(aCx,
js::CheckedUnwrap(aObject,
/* stopAtOuter = */ false));
if (!naviObj) {
return Throw(aCx, NS_ERROR_DOM_SECURITY_ERR);
}
JS::Rooted<JSObject*> domObject(aCx);
{
JSAutoCompartment ac(aCx, naviObj);
// Check whether our constructor is enabled after we unwrap Xrays, since
// we don't want to define an interface on the Xray if it's disabled in
// the target global, even if it's enabled in the Xray's global.
if (name_struct->mConstructorEnabled &&
!(*name_struct->mConstructorEnabled)(aCx, naviObj)) {
return true;
}
if (name.EqualsLiteral("mozSettings")) {
bool hasPermission = CheckPermission("settings-read") ||
CheckPermission("settings-write");
if (!hasPermission) {
aValue.setNull();
return true;
}
}
if (name.EqualsLiteral("mozDownloadManager")) {
if (!CheckPermission("downloads")) {
aValue.setNull();
return true;
}
}
domObject = construct(aCx, naviObj);
if (!domObject) {
return Throw(aCx, NS_ERROR_FAILURE);
}
}
if (!JS_WrapObject(aCx, &domObject)) {
return false;
}
aValue.setObject(*domObject);
return true;
}
NS_ASSERTION(name_struct->mType == nsGlobalNameStruct::eTypeNavigatorProperty,
"unexpected type");
nsresult rv = NS_OK;
nsCOMPtr<nsISupports> native(do_CreateInstance(name_struct->mCID, &rv));
if (NS_FAILED(rv)) {
return Throw(aCx, rv);
}
JS::Rooted<JS::Value> prop_val(aCx, JS::UndefinedValue()); // Property value.
nsCOMPtr<nsIDOMGlobalPropertyInitializer> gpi(do_QueryInterface(native));
if (gpi) {
if (!mWindow) {
return Throw(aCx, NS_ERROR_UNEXPECTED);
}
rv = gpi->Init(mWindow, &prop_val);
if (NS_FAILED(rv)) {
return Throw(aCx, rv);
}
}
if (JSVAL_IS_PRIMITIVE(prop_val) && !JSVAL_IS_NULL(prop_val)) {
rv = nsContentUtils::WrapNative(aCx, aObject, native, &prop_val, true);
if (NS_FAILED(rv)) {
return Throw(aCx, rv);
}
}
if (!JS_WrapValue(aCx, &prop_val)) {
return Throw(aCx, NS_ERROR_UNEXPECTED);
}
aValue.set(prop_val);
return true;
}
static PLDHashOperator
SaveNavigatorName(const nsAString& aName, void* aClosure)
{
nsTArray<nsString>* arr = static_cast<nsTArray<nsString>*>(aClosure);
arr->AppendElement(aName);
return PL_DHASH_NEXT;
}
void
Navigator::GetOwnPropertyNames(JSContext* aCx, nsTArray<nsString>& aNames,
ErrorResult& aRv)
{
nsScriptNameSpaceManager *nameSpaceManager = GetNameSpaceManager();
if (!nameSpaceManager) {
NS_ERROR("Can't get namespace manager.");
aRv.Throw(NS_ERROR_UNEXPECTED);
return;
}
nameSpaceManager->EnumerateNavigatorNames(SaveNavigatorName, &aNames);
}
JSObject*
Navigator::WrapObject(JSContext* cx, JS::Handle<JSObject*> scope)
{
return NavigatorBinding::Wrap(cx, scope, this);
}
/* static */
bool
Navigator::HasBatterySupport(JSContext* /* unused*/, JSObject* /*unused */)
{
return battery::BatteryManager::HasSupport();
}
/* static */
bool
Navigator::HasPowerSupport(JSContext* /* unused */, JSObject* aGlobal)
{
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
return win && PowerManager::CheckPermission(win);
}
/* static */
bool
Navigator::HasPhoneNumberSupport(JSContext* /* unused */, JSObject* aGlobal)
{
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
return CheckPermission(win, "phonenumberservice");
}
/* static */
bool
Navigator::HasIdleSupport(JSContext* /* unused */, JSObject* aGlobal)
{
if (!nsContentUtils::IsIdleObserverAPIEnabled()) {
return false;
}
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
return CheckPermission(win, "idle");
}
/* static */
bool
Navigator::HasWakeLockSupport(JSContext* /* unused*/, JSObject* /*unused */)
{
nsCOMPtr<nsIPowerManagerService> pmService =
do_GetService(POWERMANAGERSERVICE_CONTRACTID);
// No service means no wake lock support
return !!pmService;
}
/* static */
bool
Navigator::HasMobileMessageSupport(JSContext* /* unused */, JSObject* aGlobal)
{
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
#ifndef MOZ_WEBSMS_BACKEND
return false;
#endif
// First of all, the general pref has to be turned on.
bool enabled = false;
Preferences::GetBool("dom.sms.enabled", &enabled);
NS_ENSURE_TRUE(enabled, false);
NS_ENSURE_TRUE(win, false);
NS_ENSURE_TRUE(win->GetDocShell(), false);
if (!CheckPermission(win, "sms")) {
return false;
}
return true;
}
/* static */
bool
Navigator::HasTelephonySupport(JSContext* cx, JSObject* aGlobal)
{
JS::Rooted<JSObject*> global(cx, aGlobal);
// First of all, the general pref has to be turned on.
bool enabled = false;
Preferences::GetBool("dom.telephony.enabled", &enabled);
NS_ENSURE_TRUE(enabled, false);
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(global);
return win && CheckPermission(win, "telephony");
}
/* static */
bool
Navigator::HasCameraSupport(JSContext* /* unused */, JSObject* aGlobal)
{
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
return win && nsDOMCameraManager::CheckPermission(win);
}
#ifdef MOZ_B2G_RIL
/* static */
bool
Navigator::HasMobileConnectionSupport(JSContext* /* unused */,
JSObject* aGlobal)
{
// First of all, the general pref has to be turned on.
bool enabled = false;
Preferences::GetBool("dom.mobileconnection.enabled", &enabled);
NS_ENSURE_TRUE(enabled, false);
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
return win && (CheckPermission(win, "mobileconnection") ||
CheckPermission(win, "mobilenetwork"));
}
/* static */
bool
Navigator::HasCellBroadcastSupport(JSContext* /* unused */,
JSObject* aGlobal)
{
// First of all, the general pref has to be turned on.
bool enabled = false;
Preferences::GetBool("dom.cellbroadcast.enabled", &enabled);
NS_ENSURE_TRUE(enabled, false);
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
return win && CheckPermission(win, "cellbroadcast");
}
/* static */
bool
Navigator::HasVoicemailSupport(JSContext* /* unused */,
JSObject* aGlobal)
{
// First of all, the general pref has to be turned on.
bool enabled = false;
Preferences::GetBool("dom.voicemail.enabled", &enabled);
NS_ENSURE_TRUE(enabled, false);
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
return win && CheckPermission(win, "voicemail");
}
/* static */
bool
Navigator::HasIccManagerSupport(JSContext* /* unused */,
JSObject* aGlobal)
{
// First of all, the general pref has to be turned on.
bool enabled = false;
Preferences::GetBool("dom.icc.enabled", &enabled);
NS_ENSURE_TRUE(enabled, false);
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
return win && CheckPermission(win, "mobileconnection");
}
#endif // MOZ_B2G_RIL
#ifdef MOZ_B2G_BT
/* static */
bool
Navigator::HasBluetoothSupport(JSContext* /* unused */, JSObject* aGlobal)
{
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
return win && bluetooth::BluetoothManager::CheckPermission(win);
}
#endif // MOZ_B2G_BT
#ifdef MOZ_B2G_FM
/* static */
bool
Navigator::HasFMRadioSupport(JSContext* /* unused */, JSObject* aGlobal)
{
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
return win && CheckPermission(win, "fmradio");
}
#endif // MOZ_B2G_FM
#ifdef MOZ_NFC
/* static */
bool
Navigator::HasNfcSupport(JSContext* /* unused */, JSObject* aGlobal)
{
// Do not support NFC if NFC content helper does not exist.
nsCOMPtr<nsISupports> contentHelper = do_GetService("@mozilla.org/nfc/content-helper;1");
if (!contentHelper) {
return false;
}
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
return win && (CheckPermission(win, "nfc-read") ||
CheckPermission(win, "nfc-write"));
}
/* static */
bool
Navigator::HasNfcPeerSupport(JSContext* /* unused */, JSObject* aGlobal)
{
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
return win && CheckPermission(win, "nfc-write");
}
/* static */
bool
Navigator::HasNfcManagerSupport(JSContext* /* unused */, JSObject* aGlobal)
{
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
return win && CheckPermission(win, "nfc-manager");
}
#endif // MOZ_NFC
#ifdef MOZ_TIME_MANAGER
/* static */
bool
Navigator::HasTimeSupport(JSContext* /* unused */, JSObject* aGlobal)
{
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
return win && CheckPermission(win, "time");
}
#endif // MOZ_TIME_MANAGER
#ifdef MOZ_MEDIA_NAVIGATOR
/* static */
bool
Navigator::HasUserMediaSupport(JSContext* /* unused */,
JSObject* /* unused */)
{
// Make enabling peerconnection enable getUserMedia() as well
return Preferences::GetBool("media.navigator.enabled", false) ||
Preferences::GetBool("media.peerconnection.enabled", false);
}
#endif // MOZ_MEDIA_NAVIGATOR
/* static */
bool
Navigator::HasPushNotificationsSupport(JSContext* /* unused */,
JSObject* aGlobal)
{
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
return Preferences::GetBool("services.push.enabled", false) &&
win && CheckPermission(win, "push");
}
/* static */
bool
Navigator::HasInputMethodSupport(JSContext* /* unused */,
JSObject* aGlobal)
{
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(aGlobal);
if (Preferences::GetBool("dom.mozInputMethod.testing", false)) {
return true;
}
return Preferences::GetBool("dom.mozInputMethod.enabled", false) &&
win && CheckPermission(win, "input");
}
/* static */
bool
Navigator::HasDataStoreSupport(JSContext* cx, JSObject* aGlobal)
{
JS::Rooted<JSObject*> global(cx, aGlobal);
// First of all, the general pref has to be turned on.
bool enabled = false;
Preferences::GetBool("dom.datastore.enabled", &enabled);
NS_ENSURE_TRUE(enabled, false);
// Just for testing, we can enable DataStore for any kind of app.
if (Preferences::GetBool("dom.testing.datastore_enabled_for_hosted_apps", false)) {
return true;
}
nsCOMPtr<nsPIDOMWindow> win = GetWindowFromGlobal(global);
if (!win) {
return false;
}
nsIDocument* doc = win->GetExtantDoc();
if (!doc || !doc->NodePrincipal()) {
return false;
}
uint16_t status;
if (NS_FAILED(doc->NodePrincipal()->GetAppStatus(&status))) {
return false;
}
return status == nsIPrincipal::APP_STATUS_CERTIFIED;
}
/* static */
already_AddRefed<nsPIDOMWindow>
Navigator::GetWindowFromGlobal(JSObject* aGlobal)
{
nsCOMPtr<nsPIDOMWindow> win =
do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(aGlobal));
MOZ_ASSERT(!win || win->IsInnerWindow());
return win.forget();
}
} // namespace dom
} // namespace mozilla
nsresult
NS_GetNavigatorUserAgent(nsAString& aUserAgent)
{
nsresult rv;
nsCOMPtr<nsIHttpProtocolHandler>
service(do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &rv));
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString ua;
rv = service->GetUserAgent(ua);
CopyASCIItoUTF16(ua, aUserAgent);
return rv;
}
nsresult
NS_GetNavigatorPlatform(nsAString& aPlatform)
{
if (!nsContentUtils::IsCallerChrome()) {
const nsAdoptingString& override =
mozilla::Preferences::GetString("general.platform.override");
if (override) {
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(_WIN64)
aPlatform.AssignLiteral("Win64");
#elif 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");
#elif defined(XP_OS2)
aPlatform.AssignLiteral("OS/2");
#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;
}
nsresult
NS_GetNavigatorAppVersion(nsAString& aAppVersion)
{
if (!nsContentUtils::IsCallerChrome()) {
const nsAdoptingString& override =
mozilla::Preferences::GetString("general.appversion.override");
if (override) {
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;
}
void
NS_GetNavigatorAppName(nsAString& aAppName)
{
if (!nsContentUtils::IsCallerChrome()) {
const nsAdoptingString& override =
mozilla::Preferences::GetString("general.appname.override");
if (override) {
aAppName = override;
return;
}
}
aAppName.AssignLiteral("Netscape");
}