/* -*- 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 "nsDesktopNotification.h" #include "nsGeolocation.h" #include "nsIHttpProtocolHandler.h" #include "nsICachingChannel.h" #include "nsIDocShell.h" #include "nsIWebContentHandlerRegistrar.h" #include "nsICookiePermission.h" #include "nsIScriptSecurityManager.h" #include "nsIJSContextStack.h" #include "nsCharSeparatedTokenizer.h" #include "nsContentUtils.h" #include "nsUnicharUtils.h" #include "mozilla/Preferences.h" #include "mozilla/Telemetry.h" #include "BatteryManager.h" #include "PowerManager.h" #include "nsIDOMWakeLock.h" #include "nsIPowerManagerService.h" #include "SmsManager.h" #include "nsISmsService.h" #include "mozilla/Hal.h" #include "nsIWebNavigation.h" #include "nsISiteSpecificUserAgent.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/StaticPtr.h" #include "Connection.h" #include "nsIObserverService.h" #ifdef MOZ_B2G_RIL #include "MobileConnection.h" #include "mozilla/dom/CellBroadcast.h" #endif #include "nsIIdleObserver.h" #include "nsIPermissionManager.h" #include "nsNetUtil.h" #include "nsIHttpChannel.h" #include "TimeManager.h" #ifdef MOZ_MEDIA_NAVIGATOR #include "MediaManager.h" #endif #ifdef MOZ_B2G_RIL #include "TelephonyFactory.h" #endif #ifdef MOZ_B2G_BT #include "nsIDOMBluetoothManager.h" #include "BluetoothManager.h" #endif #include "nsIDOMCameraManager.h" #include "DOMCameraManager.h" #ifdef MOZ_AUDIO_CHANNEL_MANAGER #include "AudioChannelManager.h" #endif #include "nsIDOMGlobalPropertyInitializer.h" using namespace mozilla::dom::power; using namespace mozilla::dom::sms; // This should not be in the namespace. DOMCI_DATA(Navigator, mozilla::dom::Navigator) namespace mozilla { namespace dom { static const char sJSStackContractID[] = "@mozilla.org/js/xpc/ContextStack;1"; 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(do_GetWeakReference(aWindow)) { NS_ASSERTION(aWindow->IsInnerWindow(), "Navigator must get an inner window!"); nsCOMPtr obsService = mozilla::services::GetObserverService(); if (obsService) obsService->AddObserver(this, "plugin-info-updated", false); } Navigator::~Navigator() { Invalidate(); } NS_INTERFACE_MAP_BEGIN(Navigator) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMNavigator) NS_INTERFACE_MAP_ENTRY(nsIDOMNavigator) NS_INTERFACE_MAP_ENTRY(nsIDOMClientInformation) NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorDeviceStorage) NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorGeolocation) NS_INTERFACE_MAP_ENTRY(nsINavigatorBattery) NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorDesktopNotification) NS_INTERFACE_MAP_ENTRY(nsIDOMMozNavigatorSms) NS_INTERFACE_MAP_ENTRY(nsIObserver) #ifdef MOZ_MEDIA_NAVIGATOR NS_INTERFACE_MAP_ENTRY(nsINavigatorUserMedia) NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorUserMedia) #endif #ifdef MOZ_B2G_RIL NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorTelephony) #endif NS_INTERFACE_MAP_ENTRY(nsIDOMMozNavigatorNetwork) #ifdef MOZ_B2G_RIL NS_INTERFACE_MAP_ENTRY(nsIMozNavigatorMobileConnection) NS_INTERFACE_MAP_ENTRY(nsIMozNavigatorCellBroadcast) #endif #ifdef MOZ_B2G_BT NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorBluetooth) #endif NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorCamera) NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorSystemMessages) #ifdef MOZ_TIME_MANAGER NS_INTERFACE_MAP_ENTRY(nsIDOMMozNavigatorTime) #endif #ifdef MOZ_AUDIO_CHANNEL_MANAGER NS_INTERFACE_MAP_ENTRY(nsIMozNavigatorAudioChannelManager) #endif NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Navigator) NS_INTERFACE_MAP_END NS_IMPL_ADDREF(Navigator) NS_IMPL_RELEASE(Navigator) void Navigator::Invalidate() { mWindow = nullptr; nsCOMPtr obsService = mozilla::services::GetObserverService(); if (obsService) { obsService->RemoveObserver(this, "plugin-info-updated"); } if (mPlugins) { mPlugins->Invalidate(); mPlugins = 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; } if (mPowerManager) { mPowerManager->Shutdown(); mPowerManager = nullptr; } if (mSmsManager) { mSmsManager->Shutdown(); mSmsManager = nullptr; } #ifdef MOZ_B2G_RIL if (mTelephony) { mTelephony = nullptr; } if (mVoicemail) { mVoicemail = nullptr; } #endif if (mConnection) { mConnection->Shutdown(); mConnection = nullptr; } #ifdef MOZ_B2G_RIL if (mMobileConnection) { mMobileConnection->Shutdown(); mMobileConnection = nullptr; } if (mCellBroadcast) { mCellBroadcast = nullptr; } #endif #ifdef MOZ_B2G_BT if (mBluetooth) { mBluetooth = nullptr; } #endif mCameraManager = nullptr; #ifdef MOZ_SYS_MSG if (mMessagesManager) { mMessagesManager = nullptr; } #endif #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; } } nsPIDOMWindow * Navigator::GetWindow() { nsCOMPtr win(do_QueryReferent(mWindow)); return win; } //***************************************************************************** // Navigator::nsIObserver //***************************************************************************** NS_IMETHODIMP Navigator::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *aData) { if (!nsCRT::strcmp(aTopic, "plugin-info-updated") && mPlugins) { mPlugins->Refresh(false); } return NS_OK; } //***************************************************************************** // Navigator::nsIDOMNavigator //***************************************************************************** NS_IMETHODIMP Navigator::GetUserAgent(nsAString& aUserAgent) { nsresult rv = NS_GetNavigatorUserAgent(aUserAgent); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr win(do_QueryReferent(mWindow)); if (!win || !win->GetDocShell()) { return NS_OK; } nsIDocument* doc = win->GetExtantDoc(); if (!doc) { return NS_OK; } nsCOMPtr codebaseURI; doc->NodePrincipal()->GetURI(getter_AddRefs(codebaseURI)); if (!codebaseURI) { return NS_OK; } nsCOMPtr siteSpecificUA = do_GetService("@mozilla.org/dom/site-specific-user-agent;1"); NS_ENSURE_TRUE(siteSpecificUA, NS_OK); return siteSpecificUA->GetUserAgentForURI(codebaseURI, aUserAgent); } NS_IMETHODIMP Navigator::GetAppCodeName(nsAString& aAppCodeName) { nsresult rv; nsCOMPtr 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) { return NS_GetNavigatorAppName(aAppName); } /** * 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] == PRUnichar('_')) { aLanguage.Replace(2, 1, PRUnichar('-')); // 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 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; } NS_IMETHODIMP Navigator::GetMimeTypes(nsIDOMMimeTypeArray** aMimeTypes) { if (!mMimeTypes) { mMimeTypes = new nsMimeTypeArray(this); } NS_ADDREF(*aMimeTypes = mMimeTypes); return NS_OK; } NS_IMETHODIMP Navigator::GetPlugins(nsIDOMPluginArray** aPlugins) { if (!mPlugins) { nsCOMPtr win(do_QueryReferent(mWindow)); mPlugins = new nsPluginArray(this, win ? win->GetDocShell() : nullptr); } NS_ADDREF(*aPlugins = mPlugins); return NS_OK; } // Values for the network.cookie.cookieBehavior pref are documented in // nsCookieService.cpp. #define COOKIE_BEHAVIOR_REJECT 2 NS_IMETHODIMP Navigator::GetCookieEnabled(bool* aCookieEnabled) { *aCookieEnabled = (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. nsCOMPtr win(do_QueryReferent(mWindow)); if (!win || !win->GetDocShell()) { return NS_OK; } nsCOMPtr doc = do_QueryInterface(win->GetExtantDocument()); if (!doc) { return NS_OK; } nsCOMPtr 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 NS_OK; } nsCOMPtr permMgr = do_GetService(NS_COOKIEPERMISSION_CONTRACTID); NS_ENSURE_TRUE(permMgr, NS_OK); // Pass null for the channel, just like the cookie service does. nsCookieAccess access; nsresult rv = permMgr->CanAccess(codebaseURI, nullptr, &access); NS_ENSURE_SUCCESS(rv, NS_OK); if (access != nsICookiePermission::ACCESS_DEFAULT) { *aCookieEnabled = access != nsICookiePermission::ACCESS_DENY; } return NS_OK; } NS_IMETHODIMP Navigator::GetOnLine(bool* aOnline) { NS_PRECONDITION(aOnline, "Null out param"); *aOnline = !NS_IsOffline(); return NS_OK; } 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 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; } NS_IMETHODIMP Navigator::JavaEnabled(bool* aReturn) { Telemetry::AutoTimer telemetryTimer; // Return true if we have a handler for "application/x-java-vm", // otherwise return false. *aReturn = false; if (!mMimeTypes) { mMimeTypes = new nsMimeTypeArray(this); } RefreshMIMEArray(); uint32_t count; mMimeTypes->GetLength(&count); for (uint32_t i = 0; i < count; i++) { nsresult rv; nsIDOMMimeType* type = mMimeTypes->GetItemAt(i, &rv); if (NS_FAILED(rv) || !type) { continue; } nsAutoString mimeString; if (NS_FAILED(type->GetType(mimeString))) { continue; } if (mimeString.EqualsLiteral("application/x-java-vm")) { *aReturn = true; break; } } return NS_OK; } NS_IMETHODIMP Navigator::TaintEnabled(bool *aReturn) { *aReturn = false; return NS_OK; } 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, nsIDOMDocument *aDocument) { mWindow = do_GetWeakReference(aWindow); mDocument = do_GetWeakReference(aDocument); nsCOMPtr target = do_QueryInterface(aDocument); NS_NAMED_LITERAL_STRING(visibilitychange, "visibilitychange"); target->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 gVibrateWindowListener; NS_IMETHODIMP VibrateWindowListener::HandleEvent(nsIDOMEvent* aEvent) { nsCOMPtr target; aEvent->GetTarget(getter_AddRefs(target)); nsCOMPtr doc = do_QueryInterface(target); bool hidden = true; if (doc) { doc->GetHidden(&hidden); } if (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 window = do_QueryReferent(mWindow); hal::CancelVibrate(window); RemoveListener(); gVibrateWindowListener = NULL; // Careful: The line above might have deleted |this|! } return NS_OK; } void VibrateWindowListener::RemoveListener() { nsCOMPtr target = do_QueryReferent(mDocument); if (!target) { return; } NS_NAMED_LITERAL_STRING(visibilitychange, "visibilitychange"); target->RemoveSystemEventListener(visibilitychange, this, true /* use capture */); } /** * Converts a jsval into a vibration duration, checking that the duration is in * bounds (non-negative and not larger than sMaxVibrateMS). * * Returns true on success, false on failure. */ bool GetVibrationDurationFromJsval(const jsval& aJSVal, JSContext* cx, int32_t* aOut) { return JS_ValueToInt32(cx, aJSVal, aOut) && *aOut >= 0 && static_cast(*aOut) <= sMaxVibrateMS; } } // anonymous namespace NS_IMETHODIMP Navigator::AddIdleObserver(nsIIdleObserver* aIdleObserver) { if (!nsContentUtils::IsIdleObserverAPIEnabled()) { NS_WARNING("The IdleObserver API has been disabled."); return NS_OK; } NS_ENSURE_ARG_POINTER(aIdleObserver); nsCOMPtr win = do_QueryReferent(mWindow); NS_ENSURE_TRUE(win, NS_ERROR_UNEXPECTED); if (!CheckPermission("idle")) { return NS_ERROR_DOM_SECURITY_ERR; } if (NS_FAILED(win->RegisterIdleObserver(aIdleObserver))) { NS_WARNING("Failed to add idle observer."); } return NS_OK; } NS_IMETHODIMP Navigator::RemoveIdleObserver(nsIIdleObserver* aIdleObserver) { if (!nsContentUtils::IsIdleObserverAPIEnabled()) { NS_WARNING("The IdleObserver API has been disabled"); return NS_OK; } NS_ENSURE_ARG_POINTER(aIdleObserver); nsCOMPtr win = do_QueryReferent(mWindow); NS_ENSURE_TRUE(win, NS_ERROR_UNEXPECTED); if (NS_FAILED(win->UnregisterIdleObserver(aIdleObserver))) { NS_WARNING("Failed to remove idle observer."); } return NS_OK; } NS_IMETHODIMP Navigator::Vibrate(const jsval& aPattern, JSContext* cx) { nsCOMPtr win = do_QueryReferent(mWindow); NS_ENSURE_TRUE(win, NS_OK); nsCOMPtr domDoc = win->GetExtantDocument(); NS_ENSURE_TRUE(domDoc, NS_ERROR_FAILURE); bool hidden = true; domDoc->GetHidden(&hidden); if (hidden) { // Hidden documents cannot start or stop a vibration. return NS_OK; } nsAutoTArray pattern; // null or undefined pattern is an error. if (JSVAL_IS_NULL(aPattern) || JSVAL_IS_VOID(aPattern)) { return NS_ERROR_DOM_NOT_SUPPORTED_ERR; } if (JSVAL_IS_PRIMITIVE(aPattern)) { int32_t p; if (GetVibrationDurationFromJsval(aPattern, cx, &p)) { pattern.AppendElement(p); } else { return NS_ERROR_DOM_NOT_SUPPORTED_ERR; } } else { JSObject *obj = JSVAL_TO_OBJECT(aPattern); uint32_t length; if (!JS_GetArrayLength(cx, obj, &length) || length > sMaxVibrateListLen) { return NS_ERROR_DOM_NOT_SUPPORTED_ERR; } pattern.SetLength(length); for (uint32_t i = 0; i < length; ++i) { jsval v; int32_t pv; if (JS_GetElement(cx, obj, i, &v) && GetVibrationDurationFromJsval(v, cx, &pv)) { pattern[i] = pv; } else { return NS_ERROR_DOM_NOT_SUPPORTED_ERR; } } } // The spec says we check sVibratorEnabled after we've done the sanity // checking on the pattern. if (!sVibratorEnabled) { return NS_OK; } // 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(win, domDoc); nsCOMPtr domWindow = do_QueryInterface(static_cast(win)); hal::Vibrate(pattern, domWindow); return NS_OK; } //***************************************************************************** // Navigator::nsIDOMClientInformation //***************************************************************************** NS_IMETHODIMP Navigator::RegisterContentHandler(const nsAString& aMIMEType, const nsAString& aURI, const nsAString& aTitle) { nsCOMPtr win(do_QueryReferent(mWindow)); if (!win || !win->GetOuterWindow() || !win->GetDocShell()) { return NS_OK; } nsCOMPtr registrar = do_GetService(NS_WEBCONTENTHANDLERREGISTRAR_CONTRACTID); if (!registrar) { return NS_OK; } return registrar->RegisterContentHandler(aMIMEType, aURI, aTitle, win->GetOuterWindow()); } NS_IMETHODIMP Navigator::RegisterProtocolHandler(const nsAString& aProtocol, const nsAString& aURI, const nsAString& aTitle) { nsCOMPtr win(do_QueryReferent(mWindow)); if (!win || !win->GetOuterWindow() || !win->GetDocShell()) { return NS_OK; } nsCOMPtr registrar = do_GetService(NS_WEBCONTENTHANDLERREGISTRAR_CONTRACTID); if (!registrar) { return NS_OK; } return registrar->RegisterProtocolHandler(aProtocol, aURI, aTitle, win->GetOuterWindow()); } NS_IMETHODIMP Navigator::MozIsLocallyAvailable(const nsAString &aURI, bool aWhenOffline, bool* aIsAvailable) { nsCOMPtr uri; nsresult rv = NS_NewURI(getter_AddRefs(uri), aURI); NS_ENSURE_SUCCESS(rv, rv); // This method of checking the cache will only work for http/https urls. bool match; rv = uri->SchemeIs("http", &match); NS_ENSURE_SUCCESS(rv, rv); if (!match) { rv = uri->SchemeIs("https", &match); NS_ENSURE_SUCCESS(rv, rv); if (!match) { return NS_ERROR_DOM_BAD_URI; } } // Same origin check. nsCOMPtr stack = do_GetService(sJSStackContractID); NS_ENSURE_TRUE(stack, NS_ERROR_FAILURE); JSContext* cx = nullptr; rv = stack->Peek(&cx); NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE); rv = nsContentUtils::GetSecurityManager()->CheckSameOrigin(cx, uri); NS_ENSURE_SUCCESS(rv, rv); // 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; } nsCOMPtr win(do_QueryReferent(mWindow)); if (!win) { return NS_ERROR_FAILURE; } nsCOMPtr loadGroup; nsCOMPtr doc = win->GetDoc(); if (doc) { loadGroup = doc->GetDocumentLoadGroup(); } nsCOMPtr channel; rv = NS_NewChannel(getter_AddRefs(channel), uri, nullptr, loadGroup, nullptr, loadFlags); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr stream; rv = channel->Open(getter_AddRefs(stream)); NS_ENSURE_SUCCESS(rv, rv); stream->Close(); nsresult status; rv = channel->GetStatus(&status); NS_ENSURE_SUCCESS(rv, rv); if (NS_FAILED(status)) { *aIsAvailable = false; return NS_OK; } nsCOMPtr httpChannel = do_QueryInterface(channel); return httpChannel->GetRequestSucceeded(aIsAvailable); } //***************************************************************************** // Navigator::nsIDOMNavigatorDeviceStorage //***************************************************************************** NS_IMETHODIMP Navigator::GetDeviceStorage(const nsAString &aType, nsIDOMDeviceStorage** _retval) { if (!Preferences::GetBool("device.storage.enabled", false)) { return NS_OK; } nsCOMPtr win(do_QueryReferent(mWindow)); if (!win || !win->GetOuterWindow() || !win->GetDocShell()) { return NS_ERROR_FAILURE; } nsRefPtr storage; nsDOMDeviceStorage::CreateDeviceStoragesFor(win, aType, getter_AddRefs(storage)); if (!storage) { return NS_OK; } NS_ADDREF(*_retval = storage.get()); mDeviceStorageStores.AppendElement(storage); return NS_OK; } //***************************************************************************** // Navigator::nsIDOMNavigatorGeolocation //***************************************************************************** NS_IMETHODIMP Navigator::GetGeolocation(nsIDOMGeoGeolocation** _retval) { NS_ENSURE_ARG_POINTER(_retval); *_retval = nullptr; if (!Preferences::GetBool("geo.enabled", true)) { return NS_OK; } if (mGeolocation) { NS_ADDREF(*_retval = mGeolocation); return NS_OK; } nsCOMPtr win(do_QueryReferent(mWindow)); if (!win || !win->GetOuterWindow() || !win->GetDocShell()) { return NS_ERROR_FAILURE; } mGeolocation = new nsGeolocation(); if (!mGeolocation) { return NS_ERROR_FAILURE; } if (NS_FAILED(mGeolocation->Init(win->GetOuterWindow()))) { mGeolocation = nullptr; return NS_ERROR_FAILURE; } NS_ADDREF(*_retval = mGeolocation); return NS_OK; } //***************************************************************************** // Navigator::nsIDOMNavigatorUserMedia (mozGetUserMedia) //***************************************************************************** #ifdef MOZ_MEDIA_NAVIGATOR NS_IMETHODIMP Navigator::MozGetUserMedia(nsIMediaStreamOptions* aParams, nsIDOMGetUserMediaSuccessCallback* aOnSuccess, nsIDOMGetUserMediaErrorCallback* aOnError) { // Make enabling peerconnection enable getUserMedia() as well if (!(Preferences::GetBool("media.navigator.enabled", false) || Preferences::GetBool("media.peerconnection.enabled", false))) { return NS_OK; } nsCOMPtr win = do_QueryReferent(mWindow); if (!win || !win->GetOuterWindow() || win->GetOuterWindow()->GetCurrentInnerWindow() != win) { return NS_ERROR_NOT_AVAILABLE; } bool privileged = nsContentUtils::IsChromeDoc(win->GetExtantDoc()); MediaManager* manager = MediaManager::Get(); return manager->GetUserMedia(privileged, win, aParams, aOnSuccess, aOnError); } //***************************************************************************** // Navigator::nsINavigatorUserMedia (mozGetUserMediaDevices) //***************************************************************************** NS_IMETHODIMP Navigator::MozGetUserMediaDevices(nsIGetUserMediaDevicesSuccessCallback* aOnSuccess, nsIDOMGetUserMediaErrorCallback* aOnError) { nsCOMPtr win = do_QueryReferent(mWindow); if (!win || !win->GetOuterWindow() || win->GetOuterWindow()->GetCurrentInnerWindow() != win) { return NS_ERROR_NOT_AVAILABLE; } // Check if the caller is chrome privileged, bail if not if (!nsContentUtils::IsChromeDoc(win->GetExtantDoc())) { return NS_ERROR_FAILURE; } MediaManager* manager = MediaManager::Get(); return manager->GetUserMediaDevices(win, aOnSuccess, aOnError); } #endif //***************************************************************************** // Navigator::nsIDOMNavigatorDesktopNotification //***************************************************************************** NS_IMETHODIMP Navigator::GetMozNotification(nsIDOMDesktopNotificationCenter** aRetVal) { NS_ENSURE_ARG_POINTER(aRetVal); *aRetVal = nullptr; if (mNotification) { NS_ADDREF(*aRetVal = mNotification); return NS_OK; } nsCOMPtr win(do_QueryReferent(mWindow)); NS_ENSURE_TRUE(win && win->GetDocShell(), NS_ERROR_FAILURE); mNotification = new nsDesktopNotificationCenter(win); NS_ADDREF(*aRetVal = mNotification); return NS_OK; } //***************************************************************************** // Navigator::nsINavigatorBattery //***************************************************************************** NS_IMETHODIMP Navigator::GetBattery(nsIDOMBatteryManager** aBattery) { if (!mBatteryManager) { *aBattery = nullptr; nsCOMPtr win(do_QueryReferent(mWindow)); NS_ENSURE_TRUE(win && win->GetDocShell(), NS_OK); mBatteryManager = new battery::BatteryManager(); mBatteryManager->Init(win); } NS_ADDREF(*aBattery = mBatteryManager); return NS_OK; } NS_IMETHODIMP Navigator::GetMozPower(nsIDOMMozPowerManager** aPower) { *aPower = nullptr; if (!mPowerManager) { nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_TRUE(window, NS_OK); mPowerManager = PowerManager::CheckPermissionAndCreateInstance(window); NS_ENSURE_TRUE(mPowerManager, NS_OK); } nsCOMPtr power(mPowerManager); power.forget(aPower); return NS_OK; } NS_IMETHODIMP Navigator::RequestWakeLock(const nsAString &aTopic, nsIDOMMozWakeLock **aWakeLock) { *aWakeLock = nullptr; nsCOMPtr win = do_QueryReferent(mWindow); NS_ENSURE_TRUE(win, NS_OK); nsCOMPtr pmService = do_GetService(POWERMANAGERSERVICE_CONTRACTID); NS_ENSURE_TRUE(pmService, NS_OK); return pmService->NewWakeLock(aTopic, win, aWakeLock); } //***************************************************************************** // Navigator::nsIDOMNavigatorSms //***************************************************************************** NS_IMETHODIMP Navigator::GetMozSms(nsIDOMMozSmsManager** aSmsManager) { *aSmsManager = nullptr; if (!mSmsManager) { nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_TRUE(window && window->GetDocShell(), NS_OK); mSmsManager = SmsManager::CreateInstanceIfAllowed(window); NS_ENSURE_TRUE(mSmsManager, NS_OK); } NS_ADDREF(*aSmsManager = mSmsManager); return NS_OK; } #ifdef MOZ_B2G_RIL //***************************************************************************** // Navigator::nsIMozNavigatorCellBroadcast //***************************************************************************** NS_IMETHODIMP Navigator::GetMozCellBroadcast(nsIDOMMozCellBroadcast** aCellBroadcast) { *aCellBroadcast = nullptr; if (!mCellBroadcast) { nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_TRUE(window, NS_OK); if (!CheckPermission("cellbroadcast")) { return NS_OK; } nsresult rv = NS_NewCellBroadcast(window, getter_AddRefs(mCellBroadcast)); NS_ENSURE_SUCCESS(rv, rv); } NS_ADDREF(*aCellBroadcast = mCellBroadcast); return NS_OK; } //***************************************************************************** // nsNavigator::nsIDOMNavigatorTelephony //***************************************************************************** NS_IMETHODIMP Navigator::GetMozTelephony(nsIDOMTelephony** aTelephony) { nsCOMPtr telephony = mTelephony; if (!telephony) { nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); nsresult rv = NS_NewTelephony(window, getter_AddRefs(mTelephony)); NS_ENSURE_SUCCESS(rv, rv); // mTelephony may be null here! telephony = mTelephony; } telephony.forget(aTelephony); return NS_OK; } NS_IMETHODIMP Navigator::GetMozVoicemail(nsIDOMMozVoicemail** aVoicemail) { *aVoicemail = nullptr; if (!mVoicemail) { nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_TRUE(window, NS_OK); if (!CheckPermission("voicemail")) { return NS_OK; } nsresult rv = NS_NewVoicemail(window, getter_AddRefs(mVoicemail)); NS_ENSURE_SUCCESS(rv, rv); } NS_ADDREF(*aVoicemail = mVoicemail); return NS_OK; } #endif // MOZ_B2G_RIL //***************************************************************************** // Navigator::nsIDOMNavigatorNetwork //***************************************************************************** NS_IMETHODIMP Navigator::GetMozConnection(nsIDOMMozConnection** aConnection) { *aConnection = nullptr; if (!mConnection) { nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_TRUE(window && window->GetDocShell(), NS_OK); mConnection = new network::Connection(); mConnection->Init(window); } NS_ADDREF(*aConnection = mConnection); return NS_OK; } #ifdef MOZ_B2G_RIL //***************************************************************************** // Navigator::nsINavigatorMobileConnection //***************************************************************************** NS_IMETHODIMP Navigator::GetMozMobileConnection(nsIDOMMozMobileConnection** aMobileConnection) { *aMobileConnection = nullptr; if (!mMobileConnection) { nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_TRUE(window, NS_OK); if (!CheckPermission("mobileconnection")) { return NS_OK; } mMobileConnection = new network::MobileConnection(); mMobileConnection->Init(window); } NS_ADDREF(*aMobileConnection = mMobileConnection); return NS_OK; } #endif // MOZ_B2G_RIL #ifdef MOZ_B2G_BT //***************************************************************************** // nsNavigator::nsIDOMNavigatorBluetooth //***************************************************************************** NS_IMETHODIMP Navigator::GetMozBluetooth(nsIDOMBluetoothManager** aBluetooth) { nsCOMPtr bluetooth = mBluetooth; if (!bluetooth) { nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); nsresult rv = NS_NewBluetoothManager(window, getter_AddRefs(mBluetooth)); NS_ENSURE_SUCCESS(rv, rv); bluetooth = mBluetooth; } bluetooth.forget(aBluetooth); return NS_OK; } #endif //MOZ_B2G_BT //***************************************************************************** // nsNavigator::nsIDOMNavigatorSystemMessages //***************************************************************************** #ifdef MOZ_SYS_MSG nsresult Navigator::EnsureMessagesManager() { if (mMessagesManager) { return NS_OK; } nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); nsresult rv; nsCOMPtr messageManager = do_CreateInstance("@mozilla.org/system-message-manager;1", &rv); nsCOMPtr gpi = do_QueryInterface(messageManager); NS_ENSURE_TRUE(gpi, NS_ERROR_FAILURE); // We don't do anything with the return value. jsval prop_val = JSVAL_VOID; rv = gpi->Init(window, &prop_val); NS_ENSURE_SUCCESS(rv, rv); mMessagesManager = messageManager.forget(); return NS_OK; } #endif NS_IMETHODIMP Navigator::MozHasPendingMessage(const nsAString& aType, bool *aResult) { #ifdef MOZ_SYS_MSG *aResult = false; nsresult rv = EnsureMessagesManager(); NS_ENSURE_SUCCESS(rv, rv); return mMessagesManager->MozHasPendingMessage(aType, aResult); #else return NS_ERROR_NOT_IMPLEMENTED; #endif } NS_IMETHODIMP Navigator::MozSetMessageHandler(const nsAString& aType, nsIDOMSystemMessageCallback *aCallback) { #ifdef MOZ_SYS_MSG nsresult rv = EnsureMessagesManager(); NS_ENSURE_SUCCESS(rv, rv); return mMessagesManager->MozSetMessageHandler(aType, aCallback); #else return NS_ERROR_NOT_IMPLEMENTED; #endif } //***************************************************************************** // Navigator::nsIDOMNavigatorTime //***************************************************************************** #ifdef MOZ_TIME_MANAGER NS_IMETHODIMP Navigator::GetMozTime(nsIDOMMozTimeManager** aTime) { *aTime = nullptr; if (!CheckPermission("time")) { return NS_ERROR_DOM_SECURITY_ERR; } if (!mTimeManager) { mTimeManager = new time::TimeManager(); } NS_ADDREF(*aTime = mTimeManager); return NS_OK; } #endif //***************************************************************************** // nsNavigator::nsIDOMNavigatorCamera //***************************************************************************** NS_IMETHODIMP Navigator::GetMozCameras(nsIDOMCameraManager** aCameraManager) { if (!mCameraManager) { nsCOMPtr win = do_QueryReferent(mWindow); NS_ENSURE_TRUE(win, NS_ERROR_FAILURE); if (!win->GetOuterWindow() || win->GetOuterWindow()->GetCurrentInnerWindow() != win) { return NS_ERROR_NOT_AVAILABLE; } mCameraManager = nsDOMCameraManager::CheckPermissionAndCreateInstance(win); NS_ENSURE_TRUE(mCameraManager, NS_OK); } nsRefPtr cameraManager = mCameraManager; cameraManager.forget(aCameraManager); return NS_OK; } size_t Navigator::SizeOfIncludingThis(nsMallocSizeOfFun 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 nsGeolocation, bug 674115. // TODO: add SizeOfIncludingThis() to nsDesktopNotificationCenter, bug 674116. return n; } void Navigator::SetWindow(nsPIDOMWindow *aInnerWindow) { NS_ASSERTION(aInnerWindow->IsInnerWindow(), "Navigator must get an inner window!"); mWindow = do_GetWeakReference(aInnerWindow); } void Navigator::OnNavigation() { nsCOMPtr win = do_QueryReferent(mWindow); if (!win) { return; } #ifdef MOZ_MEDIA_NAVIGATOR // Inform MediaManager in case there are live streams or pending callbacks. MediaManager *manager = MediaManager::Get(); manager->OnNavigation(win->WindowID()); #endif if (mCameraManager) { mCameraManager->OnNavigation(win->WindowID()); } } bool Navigator::CheckPermission(const char* type) { nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_TRUE(window, false); nsCOMPtr permMgr = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID); NS_ENSURE_TRUE(permMgr, false); uint32_t permission = nsIPermissionManager::DENY_ACTION; permMgr->TestPermissionFromWindow(window, type, &permission); return permission == nsIPermissionManager::ALLOW_ACTION; } //***************************************************************************** // Navigator::nsINavigatorAudioChannelManager //***************************************************************************** #ifdef MOZ_AUDIO_CHANNEL_MANAGER NS_IMETHODIMP Navigator::GetMozAudioChannelManager(nsIAudioChannelManager** aAudioChannelManager) { *aAudioChannelManager = nullptr; if (!mAudioChannelManager) { nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_TRUE(window, NS_OK); mAudioChannelManager = new system::AudioChannelManager(); mAudioChannelManager->Init(window); } NS_ADDREF(*aAudioChannelManager = mAudioChannelManager); return NS_OK; } #endif } // namespace dom } // namespace mozilla nsresult NS_GetNavigatorUserAgent(nsAString& aUserAgent) { nsresult rv; nsCOMPtr 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 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 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(PRUnichar(')')); return rv; } nsresult NS_GetNavigatorAppName(nsAString& aAppName) { if (!nsContentUtils::IsCallerChrome()) { const nsAdoptingString& override = mozilla::Preferences::GetString("general.appname.override"); if (override) { aAppName = override; return NS_OK; } } aAppName.AssignLiteral("Netscape"); return NS_OK; }