michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsXULAppAPI.h" michael@0: michael@0: #include "mozilla/dom/ContentChild.h" michael@0: #include "mozilla/dom/TabChild.h" michael@0: #include "mozilla/Telemetry.h" michael@0: michael@0: #include "nsISettingsService.h" michael@0: michael@0: #include "nsGeolocation.h" michael@0: #include "nsDOMClassInfoID.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsContentPermissionHelper.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsPIDOMWindow.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "mozilla/Services.h" michael@0: #include "mozilla/unused.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/ClearOnShutdown.h" michael@0: #include "PCOMContentPermissionRequestChild.h" michael@0: #include "mozilla/dom/PermissionMessageUtils.h" michael@0: michael@0: class nsIPrincipal; michael@0: michael@0: #ifdef MOZ_ENABLE_QT5GEOPOSITION michael@0: #include "QTMLocationProvider.h" michael@0: #endif michael@0: michael@0: #ifdef MOZ_WIDGET_ANDROID michael@0: #include "AndroidLocationProvider.h" michael@0: #endif michael@0: michael@0: #ifdef MOZ_WIDGET_GONK michael@0: #include "GonkGPSGeolocationProvider.h" michael@0: #endif michael@0: michael@0: #ifdef MOZ_WIDGET_COCOA michael@0: #include "CoreLocationLocationProvider.h" michael@0: #endif michael@0: michael@0: // Some limit to the number of get or watch geolocation requests michael@0: // that a window can make. michael@0: #define MAX_GEO_REQUESTS_PER_WINDOW 1500 michael@0: michael@0: // The settings key. michael@0: #define GEO_SETINGS_ENABLED "geolocation.enabled" michael@0: michael@0: using mozilla::unused; // michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: class nsGeolocationRequest michael@0: : public nsIContentPermissionRequest michael@0: , public nsITimerCallback michael@0: , public nsIGeolocationUpdate michael@0: , public PCOMContentPermissionRequestChild michael@0: { michael@0: public: michael@0: NS_DECL_CYCLE_COLLECTING_ISUPPORTS michael@0: NS_DECL_NSICONTENTPERMISSIONREQUEST michael@0: NS_DECL_NSITIMERCALLBACK michael@0: NS_DECL_NSIGEOLOCATIONUPDATE michael@0: michael@0: NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsGeolocationRequest, nsIContentPermissionRequest) michael@0: michael@0: nsGeolocationRequest(Geolocation* aLocator, michael@0: const GeoPositionCallback& aCallback, michael@0: const GeoPositionErrorCallback& aErrorCallback, michael@0: PositionOptions* aOptions, michael@0: bool aWatchPositionRequest = false, michael@0: int32_t aWatchId = 0); michael@0: void Shutdown(); michael@0: michael@0: void SendLocation(nsIDOMGeoPosition* location); michael@0: bool WantsHighAccuracy() {return !mShutdown && mOptions && mOptions->mEnableHighAccuracy;} michael@0: void SetTimeoutTimer(); michael@0: void StopTimeoutTimer(); michael@0: void NotifyErrorAndShutdown(uint16_t); michael@0: nsIPrincipal* GetPrincipal(); michael@0: michael@0: ~nsGeolocationRequest(); michael@0: michael@0: virtual bool Recv__delete__(const bool& allow, michael@0: const InfallibleTArray& choices) MOZ_OVERRIDE; michael@0: virtual void IPDLRelease() MOZ_OVERRIDE { Release(); } michael@0: michael@0: bool IsWatch() { return mIsWatchPositionRequest; } michael@0: int32_t WatchId() { return mWatchId; } michael@0: private: michael@0: bool mIsWatchPositionRequest; michael@0: michael@0: nsCOMPtr mTimeoutTimer; michael@0: GeoPositionCallback mCallback; michael@0: GeoPositionErrorCallback mErrorCallback; michael@0: nsAutoPtr mOptions; michael@0: michael@0: nsRefPtr mLocator; michael@0: michael@0: int32_t mWatchId; michael@0: bool mShutdown; michael@0: }; michael@0: michael@0: static PositionOptions* michael@0: CreatePositionOptionsCopy(const PositionOptions& aOptions) michael@0: { michael@0: nsAutoPtr geoOptions(new PositionOptions()); michael@0: michael@0: geoOptions->mEnableHighAccuracy = aOptions.mEnableHighAccuracy; michael@0: geoOptions->mMaximumAge = aOptions.mMaximumAge; michael@0: geoOptions->mTimeout = aOptions.mTimeout; michael@0: michael@0: return geoOptions.forget(); michael@0: } michael@0: michael@0: class GeolocationSettingsCallback : public nsISettingsServiceCallback michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: michael@0: GeolocationSettingsCallback() { michael@0: MOZ_COUNT_CTOR(GeolocationSettingsCallback); michael@0: } michael@0: michael@0: virtual ~GeolocationSettingsCallback() { michael@0: MOZ_COUNT_DTOR(GeolocationSettingsCallback); michael@0: } michael@0: michael@0: NS_IMETHOD Handle(const nsAString& aName, JS::Handle aResult) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: // The geolocation is enabled by default: michael@0: bool value = true; michael@0: if (aResult.isBoolean()) { michael@0: value = aResult.toBoolean(); michael@0: } michael@0: michael@0: MozSettingValue(value); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHOD HandleError(const nsAString& aName) michael@0: { michael@0: NS_WARNING("Unable to get value for '" GEO_SETINGS_ENABLED "'"); michael@0: michael@0: // Default it's enabled: michael@0: MozSettingValue(true); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void MozSettingValue(const bool aValue) michael@0: { michael@0: nsRefPtr gs = nsGeolocationService::GetGeolocationService(); michael@0: if (gs) { michael@0: gs->HandleMozsettingValue(aValue); michael@0: } michael@0: } michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(GeolocationSettingsCallback, nsISettingsServiceCallback) michael@0: michael@0: class RequestPromptEvent : public nsRunnable michael@0: { michael@0: public: michael@0: RequestPromptEvent(nsGeolocationRequest* request) michael@0: : mRequest(request) michael@0: { michael@0: } michael@0: michael@0: NS_IMETHOD Run() { michael@0: nsCOMPtr prompt = do_CreateInstance(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID); michael@0: if (prompt) { michael@0: prompt->Prompt(mRequest); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mRequest; michael@0: }; michael@0: michael@0: class RequestAllowEvent : public nsRunnable michael@0: { michael@0: public: michael@0: RequestAllowEvent(int allow, nsGeolocationRequest* request) michael@0: : mAllow(allow), michael@0: mRequest(request) michael@0: { michael@0: } michael@0: michael@0: NS_IMETHOD Run() { michael@0: if (mAllow) { michael@0: mRequest->Allow(JS::UndefinedHandleValue); michael@0: } else { michael@0: mRequest->Cancel(); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: bool mAllow; michael@0: nsRefPtr mRequest; michael@0: }; michael@0: michael@0: class RequestSendLocationEvent : public nsRunnable michael@0: { michael@0: public: michael@0: RequestSendLocationEvent(nsIDOMGeoPosition* aPosition, michael@0: nsGeolocationRequest* aRequest) michael@0: : mPosition(aPosition), michael@0: mRequest(aRequest) michael@0: { michael@0: } michael@0: michael@0: NS_IMETHOD Run() { michael@0: mRequest->SendLocation(mPosition); michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: nsCOMPtr mPosition; michael@0: nsRefPtr mRequest; michael@0: nsRefPtr mLocator; michael@0: }; michael@0: michael@0: //////////////////////////////////////////////////// michael@0: // PositionError michael@0: //////////////////////////////////////////////////// michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PositionError) michael@0: NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMGeoPositionError) michael@0: NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPositionError) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(PositionError, mParent) michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(PositionError) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(PositionError) michael@0: michael@0: PositionError::PositionError(Geolocation* aParent, int16_t aCode) michael@0: : mCode(aCode) michael@0: , mParent(aParent) michael@0: { michael@0: SetIsDOMBinding(); michael@0: } michael@0: michael@0: PositionError::~PositionError(){} michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: PositionError::GetCode(int16_t *aCode) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aCode); michael@0: *aCode = Code(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: PositionError::GetMessage(nsAString& aMessage) michael@0: { michael@0: switch (mCode) michael@0: { michael@0: case nsIDOMGeoPositionError::PERMISSION_DENIED: michael@0: aMessage = NS_LITERAL_STRING("User denied geolocation prompt"); michael@0: break; michael@0: case nsIDOMGeoPositionError::POSITION_UNAVAILABLE: michael@0: aMessage = NS_LITERAL_STRING("Unknown error acquiring position"); michael@0: break; michael@0: case nsIDOMGeoPositionError::TIMEOUT: michael@0: aMessage = NS_LITERAL_STRING("Position acquisition timed out"); michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: Geolocation* michael@0: PositionError::GetParentObject() const michael@0: { michael@0: return mParent; michael@0: } michael@0: michael@0: JSObject* michael@0: PositionError::WrapObject(JSContext* aCx) michael@0: { michael@0: return PositionErrorBinding::Wrap(aCx, this); michael@0: } michael@0: michael@0: void michael@0: PositionError::NotifyCallback(const GeoPositionErrorCallback& aCallback) michael@0: { michael@0: nsAutoMicroTask mt; michael@0: if (aCallback.HasWebIDLCallback()) { michael@0: PositionErrorCallback* callback = aCallback.GetWebIDLCallback(); michael@0: michael@0: if (callback) { michael@0: ErrorResult err; michael@0: callback->Call(*this, err); michael@0: } michael@0: } else { michael@0: nsIDOMGeoPositionErrorCallback* callback = aCallback.GetXPCOMCallback(); michael@0: if (callback) { michael@0: callback->HandleEvent(this); michael@0: } michael@0: } michael@0: } michael@0: //////////////////////////////////////////////////// michael@0: // nsGeolocationRequest michael@0: //////////////////////////////////////////////////// michael@0: michael@0: nsGeolocationRequest::nsGeolocationRequest(Geolocation* aLocator, michael@0: const GeoPositionCallback& aCallback, michael@0: const GeoPositionErrorCallback& aErrorCallback, michael@0: PositionOptions* aOptions, michael@0: bool aWatchPositionRequest, michael@0: int32_t aWatchId) michael@0: : mIsWatchPositionRequest(aWatchPositionRequest), michael@0: mCallback(aCallback), michael@0: mErrorCallback(aErrorCallback), michael@0: mOptions(aOptions), michael@0: mLocator(aLocator), michael@0: mWatchId(aWatchId), michael@0: mShutdown(false) michael@0: { michael@0: } michael@0: michael@0: nsGeolocationRequest::~nsGeolocationRequest() michael@0: { michael@0: } michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGeolocationRequest) michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest) michael@0: NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest) michael@0: NS_INTERFACE_MAP_ENTRY(nsITimerCallback) michael@0: NS_INTERFACE_MAP_ENTRY(nsIGeolocationUpdate) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGeolocationRequest) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGeolocationRequest) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION(nsGeolocationRequest, mCallback, mErrorCallback, mLocator) michael@0: michael@0: NS_IMETHODIMP michael@0: nsGeolocationRequest::Notify(nsITimer* aTimer) michael@0: { michael@0: StopTimeoutTimer(); michael@0: NotifyErrorAndShutdown(nsIDOMGeoPositionError::TIMEOUT); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsGeolocationRequest::NotifyErrorAndShutdown(uint16_t aErrorCode) michael@0: { michael@0: MOZ_ASSERT(!mShutdown, "timeout after shutdown"); michael@0: michael@0: if (!mIsWatchPositionRequest) { michael@0: Shutdown(); michael@0: mLocator->RemoveRequest(this); michael@0: } michael@0: michael@0: NotifyError(aErrorCode); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGeolocationRequest::GetPrincipal(nsIPrincipal * *aRequestingPrincipal) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aRequestingPrincipal); michael@0: michael@0: nsCOMPtr principal = mLocator->GetPrincipal(); michael@0: principal.forget(aRequestingPrincipal); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGeolocationRequest::GetTypes(nsIArray** aTypes) michael@0: { michael@0: nsTArray emptyOptions; michael@0: return CreatePermissionArray(NS_LITERAL_CSTRING("geolocation"), michael@0: NS_LITERAL_CSTRING("unused"), michael@0: emptyOptions, michael@0: aTypes); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGeolocationRequest::GetWindow(nsIDOMWindow * *aRequestingWindow) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aRequestingWindow); michael@0: michael@0: nsCOMPtr window = do_QueryReferent(mLocator->GetOwner()); michael@0: window.forget(aRequestingWindow); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGeolocationRequest::GetElement(nsIDOMElement * *aRequestingElement) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aRequestingElement); michael@0: *aRequestingElement = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGeolocationRequest::Cancel() michael@0: { michael@0: NotifyError(nsIDOMGeoPositionError::PERMISSION_DENIED); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGeolocationRequest::Allow(JS::HandleValue aChoices) michael@0: { michael@0: MOZ_ASSERT(aChoices.isUndefined()); michael@0: michael@0: // Kick off the geo device, if it isn't already running michael@0: nsRefPtr gs = nsGeolocationService::GetGeolocationService(); michael@0: nsresult rv = gs->StartDevice(GetPrincipal()); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: // Location provider error michael@0: NotifyError(nsIDOMGeoPositionError::POSITION_UNAVAILABLE); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCOMPtr lastPosition = gs->GetCachedPosition(); michael@0: DOMTimeStamp cachedPositionTime; michael@0: if (lastPosition) { michael@0: lastPosition->GetTimestamp(&cachedPositionTime); michael@0: } michael@0: michael@0: // check to see if we can use a cached value michael@0: // if the user has specified a maximumAge, return a cached value. michael@0: michael@0: uint32_t maximumAge = 0; michael@0: if (mOptions) { michael@0: if (mOptions->mMaximumAge > 0) { michael@0: maximumAge = mOptions->mMaximumAge; michael@0: } michael@0: } michael@0: gs->UpdateAccuracy(WantsHighAccuracy()); michael@0: michael@0: bool canUseCache = lastPosition && maximumAge > 0 && michael@0: (PRTime(PR_Now() / PR_USEC_PER_MSEC) - maximumAge <= michael@0: PRTime(cachedPositionTime)); michael@0: michael@0: if (canUseCache) { michael@0: // okay, we can return a cached position michael@0: // getCurrentPosition requests serviced by the cache michael@0: // will now be owned by the RequestSendLocationEvent michael@0: Update(lastPosition); michael@0: } michael@0: michael@0: if (mIsWatchPositionRequest || !canUseCache) { michael@0: // let the locator know we're pending michael@0: // we will now be owned by the locator michael@0: mLocator->NotifyAllowedRequest(this); michael@0: } michael@0: michael@0: SetTimeoutTimer(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsGeolocationRequest::SetTimeoutTimer() michael@0: { michael@0: StopTimeoutTimer(); michael@0: michael@0: int32_t timeout; michael@0: if (mOptions && (timeout = mOptions->mTimeout) != 0) { michael@0: michael@0: if (timeout < 0) { michael@0: timeout = 0; michael@0: } else if (timeout < 10) { michael@0: timeout = 10; michael@0: } michael@0: michael@0: mTimeoutTimer = do_CreateInstance("@mozilla.org/timer;1"); michael@0: mTimeoutTimer->InitWithCallback(this, timeout, nsITimer::TYPE_ONE_SHOT); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsGeolocationRequest::StopTimeoutTimer() michael@0: { michael@0: if (mTimeoutTimer) { michael@0: mTimeoutTimer->Cancel(); michael@0: mTimeoutTimer = nullptr; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsGeolocationRequest::SendLocation(nsIDOMGeoPosition* aPosition) michael@0: { michael@0: if (mShutdown) { michael@0: // Ignore SendLocationEvents issued before we were cleared. michael@0: return; michael@0: } michael@0: michael@0: nsRefPtr wrapped, cachedWrapper = mLocator->GetCachedPosition(); michael@0: if (cachedWrapper && aPosition == cachedWrapper->GetWrappedGeoPosition()) { michael@0: wrapped = cachedWrapper; michael@0: } else if (aPosition) { michael@0: nsCOMPtr coords; michael@0: aPosition->GetCoords(getter_AddRefs(coords)); michael@0: if (coords) { michael@0: wrapped = new Position(ToSupports(mLocator), aPosition); michael@0: } michael@0: } michael@0: michael@0: if (!wrapped) { michael@0: NotifyError(nsIDOMGeoPositionError::POSITION_UNAVAILABLE); michael@0: return; michael@0: } michael@0: michael@0: mLocator->SetCachedPosition(wrapped); michael@0: if (!mIsWatchPositionRequest) { michael@0: // Cancel timer and position updates in case the position michael@0: // callback spins the event loop michael@0: Shutdown(); michael@0: } michael@0: michael@0: nsAutoMicroTask mt; michael@0: if (mCallback.HasWebIDLCallback()) { michael@0: ErrorResult err; michael@0: PositionCallback* callback = mCallback.GetWebIDLCallback(); michael@0: michael@0: MOZ_ASSERT(callback); michael@0: callback->Call(*wrapped, err); michael@0: } else { michael@0: nsIDOMGeoPositionCallback* callback = mCallback.GetXPCOMCallback(); michael@0: michael@0: MOZ_ASSERT(callback); michael@0: callback->HandleEvent(aPosition); michael@0: } michael@0: michael@0: StopTimeoutTimer(); michael@0: MOZ_ASSERT(mShutdown || mIsWatchPositionRequest, michael@0: "non-shutdown getCurrentPosition request after callback!"); michael@0: } michael@0: michael@0: nsIPrincipal* michael@0: nsGeolocationRequest::GetPrincipal() michael@0: { michael@0: if (!mLocator) { michael@0: return nullptr; michael@0: } michael@0: return mLocator->GetPrincipal(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGeolocationRequest::Update(nsIDOMGeoPosition* aPosition) michael@0: { michael@0: nsCOMPtr ev = new RequestSendLocationEvent(aPosition, this); michael@0: NS_DispatchToMainThread(ev); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGeolocationRequest::LocationUpdatePending() michael@0: { michael@0: if (!mTimeoutTimer) { michael@0: SetTimeoutTimer(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGeolocationRequest::NotifyError(uint16_t aErrorCode) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsRefPtr positionError = new PositionError(mLocator, aErrorCode); michael@0: positionError->NotifyCallback(mErrorCallback); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsGeolocationRequest::Shutdown() michael@0: { michael@0: MOZ_ASSERT(!mShutdown, "request shutdown twice"); michael@0: mShutdown = true; michael@0: michael@0: if (mTimeoutTimer) { michael@0: mTimeoutTimer->Cancel(); michael@0: mTimeoutTimer = nullptr; michael@0: } michael@0: michael@0: // If there are no other high accuracy requests, the geolocation service will michael@0: // notify the provider to switch to the default accuracy. michael@0: if (mOptions && mOptions->mEnableHighAccuracy) { michael@0: nsRefPtr gs = nsGeolocationService::GetGeolocationService(); michael@0: if (gs) { michael@0: gs->UpdateAccuracy(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: bool nsGeolocationRequest::Recv__delete__(const bool& allow, michael@0: const InfallibleTArray& choices) michael@0: { michael@0: MOZ_ASSERT(choices.IsEmpty(), "Geolocation doesn't support permission choice"); michael@0: michael@0: if (allow) { michael@0: (void) Allow(JS::UndefinedHandleValue); michael@0: } else { michael@0: (void) Cancel(); michael@0: } michael@0: return true; michael@0: } michael@0: //////////////////////////////////////////////////// michael@0: // nsGeolocationService michael@0: //////////////////////////////////////////////////// michael@0: NS_INTERFACE_MAP_BEGIN(nsGeolocationService) michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIGeolocationUpdate) michael@0: NS_INTERFACE_MAP_ENTRY(nsIGeolocationUpdate) michael@0: NS_INTERFACE_MAP_ENTRY(nsIObserver) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_ADDREF(nsGeolocationService) michael@0: NS_IMPL_RELEASE(nsGeolocationService) michael@0: michael@0: michael@0: static bool sGeoEnabled = true; michael@0: static bool sGeoInitPending = true; michael@0: static int32_t sProviderTimeout = 6000; // Time, in milliseconds, to wait for the location provider to spin up. michael@0: michael@0: nsresult nsGeolocationService::Init() michael@0: { michael@0: Preferences::AddIntVarCache(&sProviderTimeout, "geo.timeout", sProviderTimeout); michael@0: Preferences::AddBoolVarCache(&sGeoEnabled, "geo.enabled", sGeoEnabled); michael@0: michael@0: if (!sGeoEnabled) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (XRE_GetProcessType() == GeckoProcessType_Content) { michael@0: sGeoInitPending = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // check if the geolocation service is enable from settings michael@0: nsCOMPtr settings = michael@0: do_GetService("@mozilla.org/settingsService;1"); michael@0: michael@0: if (settings) { michael@0: nsCOMPtr settingsLock; michael@0: nsresult rv = settings->CreateLock(nullptr, getter_AddRefs(settingsLock)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsRefPtr callback = new GeolocationSettingsCallback(); michael@0: rv = settingsLock->Get(GEO_SETINGS_ENABLED, callback); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } else { michael@0: // If we cannot obtain the settings service, we continue michael@0: // assuming that the geolocation is enabled: michael@0: sGeoInitPending = false; michael@0: } michael@0: michael@0: // geolocation service can be enabled -> now register observer michael@0: nsCOMPtr obs = services::GetObserverService(); michael@0: if (!obs) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: obs->AddObserver(this, "quit-application", false); michael@0: obs->AddObserver(this, "mozsettings-changed", false); michael@0: michael@0: #ifdef MOZ_ENABLE_QT5GEOPOSITION michael@0: mProvider = new QTMLocationProvider(); michael@0: #endif michael@0: michael@0: #ifdef MOZ_WIDGET_ANDROID michael@0: mProvider = new AndroidLocationProvider(); michael@0: #endif michael@0: michael@0: #ifdef MOZ_WIDGET_GONK michael@0: // GonkGPSGeolocationProvider can be started at boot up time for initialization reasons. michael@0: // do_getService gets hold of the already initialized component and starts michael@0: // processing location requests immediately. michael@0: // do_Createinstance will create multiple instances of the provider which is not right. michael@0: // bug 993041 michael@0: mProvider = do_GetService(GONK_GPS_GEOLOCATION_PROVIDER_CONTRACTID); michael@0: #endif michael@0: michael@0: #ifdef MOZ_WIDGET_COCOA michael@0: if (Preferences::GetBool("geo.provider.use_corelocation", false)) { michael@0: mProvider = new CoreLocationLocationProvider(); michael@0: } michael@0: #endif michael@0: michael@0: if (Preferences::GetBool("geo.provider.use_mls", false)) { michael@0: mProvider = do_CreateInstance("@mozilla.org/geolocation/mls-provider;1"); michael@0: } michael@0: michael@0: // Override platform-specific providers with the default (network) michael@0: // provider while testing. Our tests are currently not meant to exercise michael@0: // the provider, and some tests rely on the network provider being used. michael@0: // "geo.provider.testing" is always set for all plain and browser chrome michael@0: // mochitests, and also for xpcshell tests. michael@0: if (!mProvider || Preferences::GetBool("geo.provider.testing", false)) { michael@0: nsCOMPtr override = michael@0: do_GetService(NS_GEOLOCATION_PROVIDER_CONTRACTID); michael@0: michael@0: if (override) { michael@0: mProvider = override; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsGeolocationService::~nsGeolocationService() michael@0: { michael@0: } michael@0: michael@0: void michael@0: nsGeolocationService::HandleMozsettingChanged(const char16_t* aData) michael@0: { michael@0: // The string that we're interested in will be a JSON string that looks like: michael@0: // {"key":"gelocation.enabled","value":true} michael@0: michael@0: AutoSafeJSContext cx; michael@0: michael@0: nsDependentString dataStr(aData); michael@0: JS::Rooted val(cx); michael@0: if (!JS_ParseJSON(cx, dataStr.get(), dataStr.Length(), &val) || !val.isObject()) { michael@0: return; michael@0: } michael@0: michael@0: JS::Rooted obj(cx, &val.toObject()); michael@0: JS::Rooted key(cx); michael@0: if (!JS_GetProperty(cx, obj, "key", &key) || !key.isString()) { michael@0: return; michael@0: } michael@0: michael@0: bool match; michael@0: if (!JS_StringEqualsAscii(cx, key.toString(), GEO_SETINGS_ENABLED, &match) || !match) { michael@0: return; michael@0: } michael@0: michael@0: JS::Rooted value(cx); michael@0: if (!JS_GetProperty(cx, obj, "value", &value) || !value.isBoolean()) { michael@0: return; michael@0: } michael@0: michael@0: HandleMozsettingValue(value.toBoolean()); michael@0: } michael@0: michael@0: void michael@0: nsGeolocationService::HandleMozsettingValue(const bool aValue) michael@0: { michael@0: if (!aValue) { michael@0: // turn things off michael@0: StopDevice(); michael@0: Update(nullptr); michael@0: mLastPosition = nullptr; michael@0: sGeoEnabled = false; michael@0: } else { michael@0: sGeoEnabled = true; michael@0: } michael@0: michael@0: if (sGeoInitPending) { michael@0: sGeoInitPending = false; michael@0: for (uint32_t i = 0, length = mGeolocators.Length(); i < length; ++i) { michael@0: mGeolocators[i]->ServiceReady(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGeolocationService::Observe(nsISupports* aSubject, michael@0: const char* aTopic, michael@0: const char16_t* aData) michael@0: { michael@0: if (!strcmp("quit-application", aTopic)) { michael@0: nsCOMPtr obs = services::GetObserverService(); michael@0: if (obs) { michael@0: obs->RemoveObserver(this, "quit-application"); michael@0: obs->RemoveObserver(this, "mozsettings-changed"); michael@0: } michael@0: michael@0: for (uint32_t i = 0; i< mGeolocators.Length(); i++) { michael@0: mGeolocators[i]->Shutdown(); michael@0: } michael@0: StopDevice(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!strcmp("mozsettings-changed", aTopic)) { michael@0: HandleMozsettingChanged(aData); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!strcmp("timer-callback", aTopic)) { michael@0: // decide if we can close down the service. michael@0: for (uint32_t i = 0; i< mGeolocators.Length(); i++) michael@0: if (mGeolocators[i]->HasActiveCallbacks()) { michael@0: SetDisconnectTimer(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // okay to close up. michael@0: StopDevice(); michael@0: Update(nullptr); michael@0: return NS_OK; michael@0: } michael@0: michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGeolocationService::Update(nsIDOMGeoPosition *aSomewhere) michael@0: { michael@0: SetCachedPosition(aSomewhere); michael@0: michael@0: for (uint32_t i = 0; i< mGeolocators.Length(); i++) { michael@0: mGeolocators[i]->Update(aSomewhere); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGeolocationService::LocationUpdatePending() michael@0: { michael@0: for (uint32_t i = 0; i< mGeolocators.Length(); i++) { michael@0: mGeolocators[i]->LocationUpdatePending(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsGeolocationService::NotifyError(uint16_t aErrorCode) michael@0: { michael@0: for (uint32_t i = 0; i < mGeolocators.Length(); i++) { michael@0: mGeolocators[i]->NotifyError(aErrorCode); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsGeolocationService::SetCachedPosition(nsIDOMGeoPosition* aPosition) michael@0: { michael@0: mLastPosition = aPosition; michael@0: } michael@0: michael@0: nsIDOMGeoPosition* michael@0: nsGeolocationService::GetCachedPosition() michael@0: { michael@0: return mLastPosition; michael@0: } michael@0: michael@0: nsresult michael@0: nsGeolocationService::StartDevice(nsIPrincipal *aPrincipal) michael@0: { michael@0: if (!sGeoEnabled || sGeoInitPending) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: // we do not want to keep the geolocation devices online michael@0: // indefinitely. Close them down after a reasonable period of michael@0: // inactivivity michael@0: SetDisconnectTimer(); michael@0: michael@0: if (XRE_GetProcessType() == GeckoProcessType_Content) { michael@0: ContentChild* cpc = ContentChild::GetSingleton(); michael@0: cpc->SendAddGeolocationListener(IPC::Principal(aPrincipal), michael@0: HighAccuracyRequested()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Start them up! michael@0: nsCOMPtr obs = services::GetObserverService(); michael@0: if (!obs) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (!mProvider) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsresult rv; michael@0: michael@0: if (NS_FAILED(rv = mProvider->Startup()) || michael@0: NS_FAILED(rv = mProvider->Watch(this))) { michael@0: michael@0: NotifyError(nsIDOMGeoPositionError::POSITION_UNAVAILABLE); michael@0: return rv; michael@0: } michael@0: michael@0: obs->NotifyObservers(mProvider, michael@0: "geolocation-device-events", michael@0: MOZ_UTF16("starting")); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsGeolocationService::SetDisconnectTimer() michael@0: { michael@0: if (!mDisconnectTimer) { michael@0: mDisconnectTimer = do_CreateInstance("@mozilla.org/timer;1"); michael@0: } else { michael@0: mDisconnectTimer->Cancel(); michael@0: } michael@0: michael@0: mDisconnectTimer->Init(this, michael@0: sProviderTimeout, michael@0: nsITimer::TYPE_ONE_SHOT); michael@0: } michael@0: michael@0: bool michael@0: nsGeolocationService::HighAccuracyRequested() michael@0: { michael@0: for (uint32_t i = 0; i < mGeolocators.Length(); i++) { michael@0: if (mGeolocators[i]->HighAccuracyRequested()) { michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: nsGeolocationService::UpdateAccuracy(bool aForceHigh) michael@0: { michael@0: bool highRequired = aForceHigh || HighAccuracyRequested(); michael@0: michael@0: if (XRE_GetProcessType() == GeckoProcessType_Content) { michael@0: ContentChild* cpc = ContentChild::GetSingleton(); michael@0: cpc->SendSetGeolocationHigherAccuracy(highRequired); michael@0: return; michael@0: } michael@0: michael@0: if (!mHigherAccuracy && highRequired) { michael@0: mProvider->SetHighAccuracy(true); michael@0: } michael@0: michael@0: if (mHigherAccuracy && !highRequired) { michael@0: mProvider->SetHighAccuracy(false); michael@0: } michael@0: michael@0: mHigherAccuracy = highRequired; michael@0: } michael@0: michael@0: void michael@0: nsGeolocationService::StopDevice() michael@0: { michael@0: if(mDisconnectTimer) { michael@0: mDisconnectTimer->Cancel(); michael@0: mDisconnectTimer = nullptr; michael@0: } michael@0: michael@0: if (XRE_GetProcessType() == GeckoProcessType_Content) { michael@0: ContentChild* cpc = ContentChild::GetSingleton(); michael@0: cpc->SendRemoveGeolocationListener(); michael@0: return; // bail early michael@0: } michael@0: michael@0: nsCOMPtr obs = services::GetObserverService(); michael@0: if (!obs) { michael@0: return; michael@0: } michael@0: michael@0: if (!mProvider) { michael@0: return; michael@0: } michael@0: michael@0: mHigherAccuracy = false; michael@0: michael@0: mProvider->Shutdown(); michael@0: obs->NotifyObservers(mProvider, michael@0: "geolocation-device-events", michael@0: MOZ_UTF16("shutdown")); michael@0: } michael@0: michael@0: StaticRefPtr nsGeolocationService::sService; michael@0: michael@0: already_AddRefed michael@0: nsGeolocationService::GetGeolocationService() michael@0: { michael@0: nsRefPtr result; michael@0: if (nsGeolocationService::sService) { michael@0: result = nsGeolocationService::sService; michael@0: return result.forget(); michael@0: } michael@0: michael@0: result = new nsGeolocationService(); michael@0: if (NS_FAILED(result->Init())) { michael@0: return nullptr; michael@0: } michael@0: ClearOnShutdown(&nsGeolocationService::sService); michael@0: nsGeolocationService::sService = result; michael@0: return result.forget(); michael@0: } michael@0: michael@0: void michael@0: nsGeolocationService::AddLocator(Geolocation* aLocator) michael@0: { michael@0: mGeolocators.AppendElement(aLocator); michael@0: } michael@0: michael@0: void michael@0: nsGeolocationService::RemoveLocator(Geolocation* aLocator) michael@0: { michael@0: mGeolocators.RemoveElement(aLocator); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////// michael@0: // Geolocation michael@0: //////////////////////////////////////////////////// michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Geolocation) michael@0: NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMGeoGeolocation) michael@0: NS_INTERFACE_MAP_ENTRY(nsIDOMGeoGeolocation) michael@0: NS_INTERFACE_MAP_ENTRY(nsIGeolocationUpdate) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(Geolocation) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(Geolocation) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_4(Geolocation, michael@0: mCachedPosition, michael@0: mPendingCallbacks, michael@0: mWatchingCallbacks, michael@0: mPendingRequests) michael@0: michael@0: Geolocation::Geolocation() michael@0: : mLastWatchId(0) michael@0: { michael@0: SetIsDOMBinding(); michael@0: } michael@0: michael@0: Geolocation::~Geolocation() michael@0: { michael@0: if (mService) { michael@0: Shutdown(); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: Geolocation::Init(nsIDOMWindow* aContentDom) michael@0: { michael@0: // Remember the window michael@0: if (aContentDom) { michael@0: nsCOMPtr window = do_QueryInterface(aContentDom); michael@0: if (!window) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: mOwner = do_GetWeakReference(window->GetCurrentInnerWindow()); michael@0: if (!mOwner) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // Grab the principal of the document michael@0: nsCOMPtr doc = window->GetDoc(); michael@0: if (!doc) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: mPrincipal = doc->NodePrincipal(); michael@0: } michael@0: michael@0: // If no aContentDom was passed into us, we are being used michael@0: // by chrome/c++ and have no mOwner, no mPrincipal, and no need michael@0: // to prompt. michael@0: mService = nsGeolocationService::GetGeolocationService(); michael@0: if (mService) { michael@0: mService->AddLocator(this); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: Geolocation::Shutdown() michael@0: { michael@0: // Release all callbacks michael@0: mPendingCallbacks.Clear(); michael@0: mWatchingCallbacks.Clear(); michael@0: michael@0: if (mService) { michael@0: mService->RemoveLocator(this); michael@0: mService->UpdateAccuracy(); michael@0: } michael@0: michael@0: mService = nullptr; michael@0: mPrincipal = nullptr; michael@0: } michael@0: michael@0: nsIDOMWindow* michael@0: Geolocation::GetParentObject() const { michael@0: nsCOMPtr window = do_QueryReferent(mOwner); michael@0: return window.get(); michael@0: } michael@0: michael@0: bool michael@0: Geolocation::HasActiveCallbacks() michael@0: { michael@0: return mPendingCallbacks.Length() || mWatchingCallbacks.Length(); michael@0: } michael@0: michael@0: bool michael@0: Geolocation::HighAccuracyRequested() michael@0: { michael@0: for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) { michael@0: if (mWatchingCallbacks[i]->WantsHighAccuracy()) { michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: for (uint32_t i = 0; i < mPendingCallbacks.Length(); i++) { michael@0: if (mPendingCallbacks[i]->WantsHighAccuracy()) { michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: Geolocation::RemoveRequest(nsGeolocationRequest* aRequest) michael@0: { michael@0: bool requestWasKnown = michael@0: (mPendingCallbacks.RemoveElement(aRequest) != michael@0: mWatchingCallbacks.RemoveElement(aRequest)); michael@0: michael@0: unused << requestWasKnown; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Geolocation::Update(nsIDOMGeoPosition *aSomewhere) michael@0: { michael@0: if (!WindowOwnerStillExists()) { michael@0: Shutdown(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (aSomewhere) { michael@0: nsCOMPtr coords; michael@0: aSomewhere->GetCoords(getter_AddRefs(coords)); michael@0: if (coords) { michael@0: double accuracy = -1; michael@0: coords->GetAccuracy(&accuracy); michael@0: mozilla::Telemetry::Accumulate(mozilla::Telemetry::GEOLOCATION_ACCURACY, accuracy); michael@0: } michael@0: } michael@0: michael@0: for (uint32_t i = mPendingCallbacks.Length(); i > 0; i--) { michael@0: mPendingCallbacks[i-1]->Update(aSomewhere); michael@0: RemoveRequest(mPendingCallbacks[i-1]); michael@0: } michael@0: michael@0: // notify everyone that is watching michael@0: for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) { michael@0: mWatchingCallbacks[i]->Update(aSomewhere); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Geolocation::LocationUpdatePending() michael@0: { michael@0: // this event is only really interesting for watch callbacks michael@0: for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) { michael@0: mWatchingCallbacks[i]->LocationUpdatePending(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Geolocation::NotifyError(uint16_t aErrorCode) michael@0: { michael@0: if (!WindowOwnerStillExists()) { michael@0: Shutdown(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: mozilla::Telemetry::Accumulate(mozilla::Telemetry::GEOLOCATION_ERROR, true); michael@0: michael@0: for (uint32_t i = mPendingCallbacks.Length(); i > 0; i--) { michael@0: mPendingCallbacks[i-1]->NotifyErrorAndShutdown(aErrorCode); michael@0: //NotifyErrorAndShutdown() removes the request from the array michael@0: } michael@0: michael@0: // notify everyone that is watching michael@0: for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) { michael@0: mWatchingCallbacks[i]->NotifyErrorAndShutdown(aErrorCode); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: Geolocation::SetCachedPosition(Position* aPosition) michael@0: { michael@0: mCachedPosition = aPosition; michael@0: } michael@0: michael@0: Position* michael@0: Geolocation::GetCachedPosition() michael@0: { michael@0: return mCachedPosition; michael@0: } michael@0: michael@0: void michael@0: Geolocation::GetCurrentPosition(PositionCallback& aCallback, michael@0: PositionErrorCallback* aErrorCallback, michael@0: const PositionOptions& aOptions, michael@0: ErrorResult& aRv) michael@0: { michael@0: GeoPositionCallback successCallback(&aCallback); michael@0: GeoPositionErrorCallback errorCallback(aErrorCallback); michael@0: michael@0: nsresult rv = GetCurrentPosition(successCallback, errorCallback, michael@0: CreatePositionOptionsCopy(aOptions)); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: aRv.Throw(rv); michael@0: } michael@0: michael@0: return; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Geolocation::GetCurrentPosition(nsIDOMGeoPositionCallback* aCallback, michael@0: nsIDOMGeoPositionErrorCallback* aErrorCallback, michael@0: PositionOptions* aOptions) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aCallback); michael@0: michael@0: GeoPositionCallback successCallback(aCallback); michael@0: GeoPositionErrorCallback errorCallback(aErrorCallback); michael@0: michael@0: return GetCurrentPosition(successCallback, errorCallback, aOptions); michael@0: } michael@0: michael@0: nsresult michael@0: Geolocation::GetCurrentPosition(GeoPositionCallback& callback, michael@0: GeoPositionErrorCallback& errorCallback, michael@0: PositionOptions *options) michael@0: { michael@0: if (mPendingCallbacks.Length() > MAX_GEO_REQUESTS_PER_WINDOW) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: nsRefPtr request = new nsGeolocationRequest(this, michael@0: callback, michael@0: errorCallback, michael@0: options, michael@0: false); michael@0: michael@0: if (!sGeoEnabled) { michael@0: nsCOMPtr ev = new RequestAllowEvent(false, request); michael@0: NS_DispatchToMainThread(ev); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!mOwner && !nsContentUtils::IsCallerChrome()) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (sGeoInitPending) { michael@0: mPendingRequests.AppendElement(request); michael@0: return NS_OK; michael@0: } michael@0: michael@0: return GetCurrentPositionReady(request); michael@0: } michael@0: michael@0: nsresult michael@0: Geolocation::GetCurrentPositionReady(nsGeolocationRequest* aRequest) michael@0: { michael@0: if (mOwner) { michael@0: if (!RegisterRequestWithPrompt(aRequest)) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!nsContentUtils::IsCallerChrome()) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsCOMPtr ev = new RequestAllowEvent(true, aRequest); michael@0: NS_DispatchToMainThread(ev); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: int32_t michael@0: Geolocation::WatchPosition(PositionCallback& aCallback, michael@0: PositionErrorCallback* aErrorCallback, michael@0: const PositionOptions& aOptions, michael@0: ErrorResult& aRv) michael@0: { michael@0: int32_t ret; michael@0: GeoPositionCallback successCallback(&aCallback); michael@0: GeoPositionErrorCallback errorCallback(aErrorCallback); michael@0: michael@0: nsresult rv = WatchPosition(successCallback, errorCallback, michael@0: CreatePositionOptionsCopy(aOptions), &ret); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: aRv.Throw(rv); michael@0: } michael@0: michael@0: return ret; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Geolocation::WatchPosition(nsIDOMGeoPositionCallback *aCallback, michael@0: nsIDOMGeoPositionErrorCallback *aErrorCallback, michael@0: PositionOptions *aOptions, michael@0: int32_t* aRv) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aCallback); michael@0: michael@0: GeoPositionCallback successCallback(aCallback); michael@0: GeoPositionErrorCallback errorCallback(aErrorCallback); michael@0: michael@0: return WatchPosition(successCallback, errorCallback, aOptions, aRv); michael@0: } michael@0: michael@0: nsresult michael@0: Geolocation::WatchPosition(GeoPositionCallback& aCallback, michael@0: GeoPositionErrorCallback& aErrorCallback, michael@0: PositionOptions* aOptions, michael@0: int32_t* aRv) michael@0: { michael@0: if (mWatchingCallbacks.Length() > MAX_GEO_REQUESTS_PER_WINDOW) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: // The watch ID: michael@0: *aRv = mLastWatchId++; michael@0: michael@0: nsRefPtr request = new nsGeolocationRequest(this, michael@0: aCallback, michael@0: aErrorCallback, michael@0: aOptions, michael@0: true, michael@0: *aRv); michael@0: michael@0: if (!sGeoEnabled) { michael@0: nsCOMPtr ev = new RequestAllowEvent(false, request); michael@0: NS_DispatchToMainThread(ev); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!mOwner && !nsContentUtils::IsCallerChrome()) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (sGeoInitPending) { michael@0: mPendingRequests.AppendElement(request); michael@0: return NS_OK; michael@0: } michael@0: michael@0: return WatchPositionReady(request); michael@0: } michael@0: michael@0: nsresult michael@0: Geolocation::WatchPositionReady(nsGeolocationRequest* aRequest) michael@0: { michael@0: if (mOwner) { michael@0: if (!RegisterRequestWithPrompt(aRequest)) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!nsContentUtils::IsCallerChrome()) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: aRequest->Allow(JS::UndefinedHandleValue); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Geolocation::ClearWatch(int32_t aWatchId) michael@0: { michael@0: if (aWatchId < 0) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: for (uint32_t i = 0, length = mWatchingCallbacks.Length(); i < length; ++i) { michael@0: if (mWatchingCallbacks[i]->WatchId() == aWatchId) { michael@0: mWatchingCallbacks[i]->Shutdown(); michael@0: RemoveRequest(mWatchingCallbacks[i]); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: // make sure we also search through the pending requests lists for michael@0: // watches to clear... michael@0: for (uint32_t i = 0, length = mPendingRequests.Length(); i < length; ++i) { michael@0: if (mPendingRequests[i]->IsWatch() && michael@0: (mPendingRequests[i]->WatchId() == aWatchId)) { michael@0: mPendingRequests[i]->Shutdown(); michael@0: mPendingRequests.RemoveElementAt(i); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: Geolocation::ServiceReady() michael@0: { michael@0: for (uint32_t length = mPendingRequests.Length(); length > 0; --length) { michael@0: if (mPendingRequests[0]->IsWatch()) { michael@0: WatchPositionReady(mPendingRequests[0]); michael@0: } else { michael@0: GetCurrentPositionReady(mPendingRequests[0]); michael@0: } michael@0: michael@0: mPendingRequests.RemoveElementAt(0); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: Geolocation::WindowOwnerStillExists() michael@0: { michael@0: // an owner was never set when Geolocation michael@0: // was created, which means that this object michael@0: // is being used without a window. michael@0: if (mOwner == nullptr) { michael@0: return true; michael@0: } michael@0: michael@0: nsCOMPtr window = do_QueryReferent(mOwner); michael@0: michael@0: if (window) { michael@0: bool closed = false; michael@0: window->GetClosed(&closed); michael@0: if (closed) { michael@0: return false; michael@0: } michael@0: michael@0: nsPIDOMWindow* outer = window->GetOuterWindow(); michael@0: if (!outer || outer->GetCurrentInnerWindow() != window) { michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: Geolocation::NotifyAllowedRequest(nsGeolocationRequest* aRequest) michael@0: { michael@0: if (aRequest->IsWatch()) { michael@0: mWatchingCallbacks.AppendElement(aRequest); michael@0: } else { michael@0: mPendingCallbacks.AppendElement(aRequest); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: Geolocation::RegisterRequestWithPrompt(nsGeolocationRequest* request) michael@0: { michael@0: if (Preferences::GetBool("geo.prompt.testing", false)) { michael@0: bool allow = Preferences::GetBool("geo.prompt.testing.allow", false); michael@0: nsCOMPtr ev = new RequestAllowEvent(allow, michael@0: request); michael@0: NS_DispatchToMainThread(ev); michael@0: return true; michael@0: } michael@0: michael@0: if (XRE_GetProcessType() == GeckoProcessType_Content) { michael@0: nsCOMPtr window = do_QueryReferent(mOwner); michael@0: if (!window) { michael@0: return true; michael@0: } michael@0: michael@0: // because owner implements nsITabChild, we can assume that it is michael@0: // the one and only TabChild. michael@0: TabChild* child = TabChild::GetFrom(window->GetDocShell()); michael@0: if (!child) { michael@0: return false; michael@0: } michael@0: michael@0: nsTArray permArray; michael@0: nsTArray emptyOptions; michael@0: permArray.AppendElement(PermissionRequest(NS_LITERAL_CSTRING("geolocation"), michael@0: NS_LITERAL_CSTRING("unused"), michael@0: emptyOptions)); michael@0: michael@0: // Retain a reference so the object isn't deleted without IPDL's knowledge. michael@0: // Corresponding release occurs in DeallocPContentPermissionRequest. michael@0: request->AddRef(); michael@0: child->SendPContentPermissionRequestConstructor(request, michael@0: permArray, michael@0: IPC::Principal(mPrincipal)); michael@0: michael@0: request->Sendprompt(); michael@0: return true; michael@0: } michael@0: michael@0: nsCOMPtr ev = new RequestPromptEvent(request); michael@0: NS_DispatchToMainThread(ev); michael@0: return true; michael@0: } michael@0: michael@0: JSObject* michael@0: Geolocation::WrapObject(JSContext *aCtx) michael@0: { michael@0: return GeolocationBinding::Wrap(aCtx, this); michael@0: }