12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* 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/. */
- #include "nsXULAppAPI.h"
- #include "mozilla/dom/ContentChild.h"
- #include "mozilla/UniquePtr.h"
- #include "nsGeolocation.h"
- #include "nsDOMClassInfoID.h"
- #include "nsComponentManagerUtils.h"
- #include "nsServiceManagerUtils.h"
- #include "nsContentUtils.h"
- #include "nsContentPermissionHelper.h"
- #include "nsIDocument.h"
- #include "nsIObserverService.h"
- #include "nsPIDOMWindow.h"
- #include "nsThreadUtils.h"
- #include "mozilla/Services.h"
- #include "mozilla/Unused.h"
- #include "mozilla/Preferences.h"
- #include "mozilla/ClearOnShutdown.h"
- #include "mozilla/WeakPtr.h"
- #include "mozilla/dom/PermissionMessageUtils.h"
- class nsIPrincipal;
- #ifdef MOZ_GPSD
- #include "GpsdLocationProvider.h"
- #endif
- #ifdef MOZ_WIDGET_COCOA
- #include "CoreLocationLocationProvider.h"
- #endif
- #ifdef XP_WIN
- #include "WindowsLocationProvider.h"
- #include "mozilla/WindowsVersion.h"
- #endif
- // Some limit to the number of get or watch geolocation requests
- // that a window can make.
- #define MAX_GEO_REQUESTS_PER_WINDOW 1500
- using mozilla::Unused; // <snicker>
- using namespace mozilla;
- using namespace mozilla::dom;
- class nsGeolocationRequest final
- : public nsIContentPermissionRequest
- , public nsIGeolocationUpdate
- , public SupportsWeakPtr<nsGeolocationRequest>
- {
- public:
- NS_DECL_CYCLE_COLLECTING_ISUPPORTS
- NS_DECL_NSICONTENTPERMISSIONREQUEST
- NS_DECL_NSIGEOLOCATIONUPDATE
- NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsGeolocationRequest, nsIContentPermissionRequest)
- nsGeolocationRequest(Geolocation* aLocator,
- GeoPositionCallback aCallback,
- GeoPositionErrorCallback aErrorCallback,
- UniquePtr<PositionOptions>&& aOptions,
- bool aWatchPositionRequest = false,
- int32_t aWatchId = 0);
- MOZ_DECLARE_WEAKREFERENCE_TYPENAME(nsGeolocationRequest)
- void Shutdown();
- void SendLocation(nsIDOMGeoPosition* aLocation);
- bool WantsHighAccuracy() {return !mShutdown && mOptions && mOptions->mEnableHighAccuracy;}
- void SetTimeoutTimer();
- void StopTimeoutTimer();
- void NotifyErrorAndShutdown(uint16_t);
- nsIPrincipal* GetPrincipal();
- bool IsWatch() { return mIsWatchPositionRequest; }
- int32_t WatchId() { return mWatchId; }
- private:
- virtual ~nsGeolocationRequest();
- class TimerCallbackHolder final : public nsITimerCallback
- {
- public:
- NS_DECL_ISUPPORTS
- NS_DECL_NSITIMERCALLBACK
- explicit TimerCallbackHolder(nsGeolocationRequest* aRequest)
- : mRequest(aRequest)
- {}
- private:
- ~TimerCallbackHolder() {}
- WeakPtr<nsGeolocationRequest> mRequest;
- };
- void Notify();
- bool mIsWatchPositionRequest;
- nsCOMPtr<nsITimer> mTimeoutTimer;
- GeoPositionCallback mCallback;
- GeoPositionErrorCallback mErrorCallback;
- UniquePtr<PositionOptions> mOptions;
- RefPtr<Geolocation> mLocator;
- int32_t mWatchId;
- bool mShutdown;
- nsCOMPtr<nsIContentPermissionRequester> mRequester;
- };
- static UniquePtr<PositionOptions>
- CreatePositionOptionsCopy(const PositionOptions& aOptions)
- {
- UniquePtr<PositionOptions> geoOptions = MakeUnique<PositionOptions>();
- geoOptions->mEnableHighAccuracy = aOptions.mEnableHighAccuracy;
- geoOptions->mMaximumAge = aOptions.mMaximumAge;
- geoOptions->mTimeout = aOptions.mTimeout;
- return geoOptions;
- }
- class RequestPromptEvent : public Runnable
- {
- public:
- RequestPromptEvent(nsGeolocationRequest* aRequest, nsWeakPtr aWindow)
- : mRequest(aRequest)
- , mWindow(aWindow)
- {
- }
- NS_IMETHOD Run() override
- {
- nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mWindow);
- nsContentPermissionUtils::AskPermission(mRequest, window);
- return NS_OK;
- }
- private:
- RefPtr<nsGeolocationRequest> mRequest;
- nsWeakPtr mWindow;
- };
- class RequestAllowEvent : public Runnable
- {
- public:
- RequestAllowEvent(int allow, nsGeolocationRequest* request)
- : mAllow(allow),
- mRequest(request)
- {
- }
- NS_IMETHOD Run() override {
- if (mAllow) {
- mRequest->Allow(JS::UndefinedHandleValue);
- } else {
- mRequest->Cancel();
- }
- return NS_OK;
- }
- private:
- bool mAllow;
- RefPtr<nsGeolocationRequest> mRequest;
- };
- class RequestSendLocationEvent : public Runnable
- {
- public:
- RequestSendLocationEvent(nsIDOMGeoPosition* aPosition,
- nsGeolocationRequest* aRequest)
- : mPosition(aPosition),
- mRequest(aRequest)
- {
- }
- NS_IMETHOD Run() override {
- mRequest->SendLocation(mPosition);
- return NS_OK;
- }
- private:
- nsCOMPtr<nsIDOMGeoPosition> mPosition;
- RefPtr<nsGeolocationRequest> mRequest;
- RefPtr<Geolocation> mLocator;
- };
- ////////////////////////////////////////////////////
- // PositionError
- ////////////////////////////////////////////////////
- NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PositionError)
- NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
- NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMGeoPositionError)
- NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPositionError)
- NS_INTERFACE_MAP_END
- NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PositionError, mParent)
- NS_IMPL_CYCLE_COLLECTING_ADDREF(PositionError)
- NS_IMPL_CYCLE_COLLECTING_RELEASE(PositionError)
- PositionError::PositionError(Geolocation* aParent, int16_t aCode)
- : mCode(aCode)
- , mParent(aParent)
- {
- }
- PositionError::~PositionError(){}
- NS_IMETHODIMP
- PositionError::GetCode(int16_t *aCode)
- {
- NS_ENSURE_ARG_POINTER(aCode);
- *aCode = Code();
- return NS_OK;
- }
- NS_IMETHODIMP
- PositionError::GetMessage(nsAString& aMessage)
- {
- switch (mCode)
- {
- case nsIDOMGeoPositionError::PERMISSION_DENIED:
- aMessage = NS_LITERAL_STRING("User denied geolocation prompt");
- break;
- case nsIDOMGeoPositionError::POSITION_UNAVAILABLE:
- aMessage = NS_LITERAL_STRING("Unknown error acquiring position");
- break;
- case nsIDOMGeoPositionError::TIMEOUT:
- aMessage = NS_LITERAL_STRING("Position acquisition timed out");
- break;
- default:
- break;
- }
- return NS_OK;
- }
- Geolocation*
- PositionError::GetParentObject() const
- {
- return mParent;
- }
- JSObject*
- PositionError::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
- {
- return PositionErrorBinding::Wrap(aCx, this, aGivenProto);
- }
- void
- PositionError::NotifyCallback(const GeoPositionErrorCallback& aCallback)
- {
- nsAutoMicroTask mt;
- if (aCallback.HasWebIDLCallback()) {
- PositionErrorCallback* callback = aCallback.GetWebIDLCallback();
- if (callback) {
- callback->Call(*this);
- }
- } else {
- nsIDOMGeoPositionErrorCallback* callback = aCallback.GetXPCOMCallback();
- if (callback) {
- callback->HandleEvent(this);
- }
- }
- }
- ////////////////////////////////////////////////////
- // nsGeolocationRequest
- ////////////////////////////////////////////////////
- nsGeolocationRequest::nsGeolocationRequest(Geolocation* aLocator,
- GeoPositionCallback aCallback,
- GeoPositionErrorCallback aErrorCallback,
- UniquePtr<PositionOptions>&& aOptions,
- bool aWatchPositionRequest,
- int32_t aWatchId)
- : mIsWatchPositionRequest(aWatchPositionRequest),
- mCallback(Move(aCallback)),
- mErrorCallback(Move(aErrorCallback)),
- mOptions(Move(aOptions)),
- mLocator(aLocator),
- mWatchId(aWatchId),
- mShutdown(false)
- {
- if (nsCOMPtr<nsPIDOMWindowInner> win =
- do_QueryReferent(mLocator->GetOwner())) {
- mRequester = new nsContentPermissionRequester(win);
- }
- }
- nsGeolocationRequest::~nsGeolocationRequest()
- {
- StopTimeoutTimer();
- }
- NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGeolocationRequest)
- NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
- NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
- NS_INTERFACE_MAP_ENTRY(nsIGeolocationUpdate)
- NS_INTERFACE_MAP_END
- NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGeolocationRequest)
- NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGeolocationRequest)
- NS_IMPL_CYCLE_COLLECTION(nsGeolocationRequest, mCallback, mErrorCallback, mLocator)
- void
- nsGeolocationRequest::Notify()
- {
- SetTimeoutTimer();
- NotifyErrorAndShutdown(nsIDOMGeoPositionError::TIMEOUT);
- }
- void
- nsGeolocationRequest::NotifyErrorAndShutdown(uint16_t aErrorCode)
- {
- MOZ_ASSERT(!mShutdown, "timeout after shutdown");
- if (!mIsWatchPositionRequest) {
- Shutdown();
- mLocator->RemoveRequest(this);
- }
- NotifyError(aErrorCode);
- }
- NS_IMETHODIMP
- nsGeolocationRequest::GetPrincipal(nsIPrincipal * *aRequestingPrincipal)
- {
- NS_ENSURE_ARG_POINTER(aRequestingPrincipal);
- nsCOMPtr<nsIPrincipal> principal = mLocator->GetPrincipal();
- principal.forget(aRequestingPrincipal);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsGeolocationRequest::GetTypes(nsIArray** aTypes)
- {
- nsTArray<nsString> emptyOptions;
- return nsContentPermissionUtils::CreatePermissionArray(NS_LITERAL_CSTRING("geolocation"),
- NS_LITERAL_CSTRING("unused"),
- emptyOptions,
- aTypes);
- }
- NS_IMETHODIMP
- nsGeolocationRequest::GetWindow(mozIDOMWindow** aRequestingWindow)
- {
- NS_ENSURE_ARG_POINTER(aRequestingWindow);
- nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mLocator->GetOwner());
- window.forget(aRequestingWindow);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsGeolocationRequest::GetElement(nsIDOMElement * *aRequestingElement)
- {
- NS_ENSURE_ARG_POINTER(aRequestingElement);
- *aRequestingElement = nullptr;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsGeolocationRequest::Cancel()
- {
- if (mLocator->ClearPendingRequest(this)) {
- return NS_OK;
- }
- NotifyError(nsIDOMGeoPositionError::PERMISSION_DENIED);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsGeolocationRequest::Allow(JS::HandleValue aChoices)
- {
- MOZ_ASSERT(aChoices.isUndefined());
- if (mLocator->ClearPendingRequest(this)) {
- return NS_OK;
- }
- RefPtr<nsGeolocationService> gs = nsGeolocationService::GetGeolocationService();
- bool canUseCache = false;
- CachedPositionAndAccuracy lastPosition = gs->GetCachedPosition();
- if (lastPosition.position) {
- DOMTimeStamp cachedPositionTime_ms;
- lastPosition.position->GetTimestamp(&cachedPositionTime_ms);
- // check to see if we can use a cached value
- // if the user has specified a maximumAge, return a cached value.
- if (mOptions && mOptions->mMaximumAge > 0) {
- uint32_t maximumAge_ms = mOptions->mMaximumAge;
- bool isCachedWithinRequestedAccuracy = WantsHighAccuracy() <= lastPosition.isHighAccuracy;
- bool isCachedWithinRequestedTime =
- DOMTimeStamp(PR_Now() / PR_USEC_PER_MSEC - maximumAge_ms) <= cachedPositionTime_ms;
- canUseCache = isCachedWithinRequestedAccuracy && isCachedWithinRequestedTime;
- }
- }
- gs->UpdateAccuracy(WantsHighAccuracy());
- if (canUseCache) {
- // okay, we can return a cached position
- // getCurrentPosition requests serviced by the cache
- // will now be owned by the RequestSendLocationEvent
- Update(lastPosition.position);
- // After Update is called, getCurrentPosition finishes it's job.
- if (!mIsWatchPositionRequest) {
- return NS_OK;
- }
- } else {
- // if it is not a watch request and timeout is 0,
- // invoke the errorCallback (if present) with TIMEOUT code
- if (mOptions && mOptions->mTimeout == 0 && !mIsWatchPositionRequest) {
- NotifyError(nsIDOMGeoPositionError::TIMEOUT);
- return NS_OK;
- }
- }
- // Kick off the geo device, if it isn't already running
- nsresult rv = gs->StartDevice(GetPrincipal());
- if (NS_FAILED(rv)) {
- // Location provider error
- NotifyError(nsIDOMGeoPositionError::POSITION_UNAVAILABLE);
- return NS_OK;
- }
- if (mIsWatchPositionRequest || !canUseCache) {
- // let the locator know we're pending
- // we will now be owned by the locator
- mLocator->NotifyAllowedRequest(this);
- }
- SetTimeoutTimer();
- return NS_OK;
- }
- NS_IMETHODIMP
- nsGeolocationRequest::GetRequester(nsIContentPermissionRequester** aRequester)
- {
- NS_ENSURE_ARG_POINTER(aRequester);
- nsCOMPtr<nsIContentPermissionRequester> requester = mRequester;
- requester.forget(aRequester);
- return NS_OK;
- }
- void
- nsGeolocationRequest::SetTimeoutTimer()
- {
- MOZ_ASSERT(!mShutdown, "set timeout after shutdown");
- StopTimeoutTimer();
- if (mOptions && mOptions->mTimeout != 0 && mOptions->mTimeout != 0x7fffffff) {
- mTimeoutTimer = do_CreateInstance("@mozilla.org/timer;1");
- RefPtr<TimerCallbackHolder> holder = new TimerCallbackHolder(this);
- mTimeoutTimer->InitWithCallback(holder, mOptions->mTimeout, nsITimer::TYPE_ONE_SHOT);
- }
- }
- void
- nsGeolocationRequest::StopTimeoutTimer()
- {
- if (mTimeoutTimer) {
- mTimeoutTimer->Cancel();
- mTimeoutTimer = nullptr;
- }
- }
- void
- nsGeolocationRequest::SendLocation(nsIDOMGeoPosition* aPosition)
- {
- if (mShutdown) {
- // Ignore SendLocationEvents issued before we were cleared.
- return;
- }
- if (mOptions && mOptions->mMaximumAge > 0) {
- DOMTimeStamp positionTime_ms;
- aPosition->GetTimestamp(&positionTime_ms);
- const uint32_t maximumAge_ms = mOptions->mMaximumAge;
- const bool isTooOld =
- DOMTimeStamp(PR_Now() / PR_USEC_PER_MSEC - maximumAge_ms) > positionTime_ms;
- if (isTooOld) {
- return;
- }
- }
- RefPtr<Position> wrapped;
- if (aPosition) {
- nsCOMPtr<nsIDOMGeoPositionCoords> coords;
- aPosition->GetCoords(getter_AddRefs(coords));
- if (coords) {
- wrapped = new Position(ToSupports(mLocator), aPosition);
- }
- }
- if (!wrapped) {
- NotifyError(nsIDOMGeoPositionError::POSITION_UNAVAILABLE);
- return;
- }
- if (!mIsWatchPositionRequest) {
- // Cancel timer and position updates in case the position
- // callback spins the event loop
- Shutdown();
- }
- nsAutoMicroTask mt;
- if (mCallback.HasWebIDLCallback()) {
- PositionCallback* callback = mCallback.GetWebIDLCallback();
- MOZ_ASSERT(callback);
- callback->Call(*wrapped);
- } else {
- nsIDOMGeoPositionCallback* callback = mCallback.GetXPCOMCallback();
- MOZ_ASSERT(callback);
- callback->HandleEvent(aPosition);
- }
- if (mIsWatchPositionRequest && !mShutdown) {
- SetTimeoutTimer();
- }
- MOZ_ASSERT(mShutdown || mIsWatchPositionRequest,
- "non-shutdown getCurrentPosition request after callback!");
- }
- nsIPrincipal*
- nsGeolocationRequest::GetPrincipal()
- {
- if (!mLocator) {
- return nullptr;
- }
- return mLocator->GetPrincipal();
- }
- NS_IMETHODIMP
- nsGeolocationRequest::Update(nsIDOMGeoPosition* aPosition)
- {
- nsCOMPtr<nsIRunnable> ev = new RequestSendLocationEvent(aPosition, this);
- NS_DispatchToMainThread(ev);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsGeolocationRequest::NotifyError(uint16_t aErrorCode)
- {
- MOZ_ASSERT(NS_IsMainThread());
- RefPtr<PositionError> positionError = new PositionError(mLocator, aErrorCode);
- positionError->NotifyCallback(mErrorCallback);
- return NS_OK;
- }
- void
- nsGeolocationRequest::Shutdown()
- {
- MOZ_ASSERT(!mShutdown, "request shutdown twice");
- mShutdown = true;
- StopTimeoutTimer();
- // If there are no other high accuracy requests, the geolocation service will
- // notify the provider to switch to the default accuracy.
- if (mOptions && mOptions->mEnableHighAccuracy) {
- RefPtr<nsGeolocationService> gs = nsGeolocationService::GetGeolocationService();
- if (gs) {
- gs->UpdateAccuracy();
- }
- }
- }
- ////////////////////////////////////////////////////
- // nsGeolocationRequest::TimerCallbackHolder
- ////////////////////////////////////////////////////
- NS_IMPL_ISUPPORTS(nsGeolocationRequest::TimerCallbackHolder, nsISupports, nsITimerCallback)
- NS_IMETHODIMP
- nsGeolocationRequest::TimerCallbackHolder::Notify(nsITimer*)
- {
- if (mRequest && mRequest->mLocator) {
- RefPtr<nsGeolocationRequest> request(mRequest);
- request->Notify();
- }
- return NS_OK;
- }
- ////////////////////////////////////////////////////
- // nsGeolocationService
- ////////////////////////////////////////////////////
- NS_INTERFACE_MAP_BEGIN(nsGeolocationService)
- NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIGeolocationUpdate)
- NS_INTERFACE_MAP_ENTRY(nsIGeolocationUpdate)
- NS_INTERFACE_MAP_ENTRY(nsIObserver)
- NS_INTERFACE_MAP_END
- NS_IMPL_ADDREF(nsGeolocationService)
- NS_IMPL_RELEASE(nsGeolocationService)
- static bool sGeoEnabled = true;
- static int32_t sProviderTimeout = 6000; // Time, in milliseconds, to wait for the location provider to spin up.
- nsresult nsGeolocationService::Init()
- {
- Preferences::AddIntVarCache(&sProviderTimeout, "geo.timeout", sProviderTimeout);
- Preferences::AddBoolVarCache(&sGeoEnabled, "geo.enabled", sGeoEnabled);
- if (!sGeoEnabled) {
- return NS_ERROR_FAILURE;
- }
- if (XRE_IsContentProcess()) {
- return NS_OK;
- }
- // geolocation service can be enabled -> now register observer
- nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
- if (!obs) {
- return NS_ERROR_FAILURE;
- }
- obs->AddObserver(this, "xpcom-shutdown", false);
- #ifdef MOZ_WIDGET_GTK
- #ifdef MOZ_GPSD
- if (Preferences::GetBool("geo.provider.use_gpsd", false)) {
- mProvider = new GpsdLocationProvider();
- }
- #endif
- #endif
- #ifdef MOZ_WIDGET_COCOA
- if (Preferences::GetBool("geo.provider.use_corelocation", true)) {
- mProvider = new CoreLocationLocationProvider();
- }
- #endif
- #ifdef XP_WIN
- if (Preferences::GetBool("geo.provider.ms-windows-location", false) &&
- IsWin8OrLater()) {
- mProvider = new WindowsLocationProvider();
- }
- #endif
- if (Preferences::GetBool("geo.provider.use_mls", false)) {
- mProvider = do_CreateInstance("@mozilla.org/geolocation/mls-provider;1");
- }
- // Override platform-specific providers with the default (network)
- // provider while testing. Our tests are currently not meant to exercise
- // the provider, and some tests rely on the network provider being used.
- // "geo.provider.testing" is always set for all plain and browser chrome
- // mochitests, and also for xpcshell tests.
- if (!mProvider || Preferences::GetBool("geo.provider.testing", false)) {
- nsCOMPtr<nsIGeolocationProvider> override =
- do_GetService(NS_GEOLOCATION_PROVIDER_CONTRACTID);
- if (override) {
- mProvider = override;
- }
- }
- return NS_OK;
- }
- nsGeolocationService::~nsGeolocationService()
- {
- }
- NS_IMETHODIMP
- nsGeolocationService::Observe(nsISupports* aSubject,
- const char* aTopic,
- const char16_t* aData)
- {
- if (!strcmp("xpcom-shutdown", aTopic)) {
- nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
- if (obs) {
- obs->RemoveObserver(this, "xpcom-shutdown");
- }
- for (uint32_t i = 0; i< mGeolocators.Length(); i++) {
- mGeolocators[i]->Shutdown();
- }
- StopDevice();
- return NS_OK;
- }
- if (!strcmp("timer-callback", aTopic)) {
- // decide if we can close down the service.
- for (uint32_t i = 0; i< mGeolocators.Length(); i++)
- if (mGeolocators[i]->HasActiveCallbacks()) {
- SetDisconnectTimer();
- return NS_OK;
- }
- // okay to close up.
- StopDevice();
- Update(nullptr);
- return NS_OK;
- }
- return NS_ERROR_FAILURE;
- }
- NS_IMETHODIMP
- nsGeolocationService::Update(nsIDOMGeoPosition *aSomewhere)
- {
- if (aSomewhere) {
- SetCachedPosition(aSomewhere);
- }
- for (uint32_t i = 0; i< mGeolocators.Length(); i++) {
- mGeolocators[i]->Update(aSomewhere);
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- nsGeolocationService::NotifyError(uint16_t aErrorCode)
- {
- for (uint32_t i = 0; i < mGeolocators.Length(); i++) {
- mGeolocators[i]->NotifyError(aErrorCode);
- }
- return NS_OK;
- }
- void
- nsGeolocationService::SetCachedPosition(nsIDOMGeoPosition* aPosition)
- {
- mLastPosition.position = aPosition;
- mLastPosition.isHighAccuracy = mHigherAccuracy;
- }
- CachedPositionAndAccuracy
- nsGeolocationService::GetCachedPosition()
- {
- return mLastPosition;
- }
- nsresult
- nsGeolocationService::StartDevice(nsIPrincipal *aPrincipal)
- {
- if (!sGeoEnabled) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- // We do not want to keep the geolocation devices online
- // indefinitely.
- // Close them down after a reasonable period of inactivivity.
- SetDisconnectTimer();
- if (XRE_IsContentProcess()) {
- ContentChild* cpc = ContentChild::GetSingleton();
- cpc->SendAddGeolocationListener(IPC::Principal(aPrincipal),
- HighAccuracyRequested());
- return NS_OK;
- }
- // Start them up!
- nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
- if (!obs) {
- return NS_ERROR_FAILURE;
- }
- if (!mProvider) {
- return NS_ERROR_FAILURE;
- }
- nsresult rv;
- if (NS_FAILED(rv = mProvider->Startup()) ||
- NS_FAILED(rv = mProvider->Watch(this))) {
- NotifyError(nsIDOMGeoPositionError::POSITION_UNAVAILABLE);
- return rv;
- }
- obs->NotifyObservers(mProvider,
- "geolocation-device-events",
- u"starting");
- return NS_OK;
- }
- void
- nsGeolocationService::SetDisconnectTimer()
- {
- if (!mDisconnectTimer) {
- mDisconnectTimer = do_CreateInstance("@mozilla.org/timer;1");
- } else {
- mDisconnectTimer->Cancel();
- }
- mDisconnectTimer->Init(this,
- sProviderTimeout,
- nsITimer::TYPE_ONE_SHOT);
- }
- bool
- nsGeolocationService::HighAccuracyRequested()
- {
- for (uint32_t i = 0; i < mGeolocators.Length(); i++) {
- if (mGeolocators[i]->HighAccuracyRequested()) {
- return true;
- }
- }
- return false;
- }
- void
- nsGeolocationService::UpdateAccuracy(bool aForceHigh)
- {
- bool highRequired = aForceHigh || HighAccuracyRequested();
- if (XRE_IsContentProcess()) {
- ContentChild* cpc = ContentChild::GetSingleton();
- if (cpc->IsAlive()) {
- cpc->SendSetGeolocationHigherAccuracy(highRequired);
- }
- return;
- }
- mProvider->SetHighAccuracy(!mHigherAccuracy && highRequired);
- mHigherAccuracy = highRequired;
- }
- void
- nsGeolocationService::StopDevice()
- {
- if (mDisconnectTimer) {
- mDisconnectTimer->Cancel();
- mDisconnectTimer = nullptr;
- }
- if (XRE_IsContentProcess()) {
- ContentChild* cpc = ContentChild::GetSingleton();
- cpc->SendRemoveGeolocationListener();
- return; // bail early
- }
- nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
- if (!obs) {
- return;
- }
- if (!mProvider) {
- return;
- }
- mHigherAccuracy = false;
- mProvider->Shutdown();
- obs->NotifyObservers(mProvider,
- "geolocation-device-events",
- u"shutdown");
- }
- StaticRefPtr<nsGeolocationService> nsGeolocationService::sService;
- already_AddRefed<nsGeolocationService>
- nsGeolocationService::GetGeolocationService()
- {
- RefPtr<nsGeolocationService> result;
- if (nsGeolocationService::sService) {
- result = nsGeolocationService::sService;
- return result.forget();
- }
- result = new nsGeolocationService();
- if (NS_FAILED(result->Init())) {
- return nullptr;
- }
- ClearOnShutdown(&nsGeolocationService::sService);
- nsGeolocationService::sService = result;
- return result.forget();
- }
- void
- nsGeolocationService::AddLocator(Geolocation* aLocator)
- {
- mGeolocators.AppendElement(aLocator);
- }
- void
- nsGeolocationService::RemoveLocator(Geolocation* aLocator)
- {
- mGeolocators.RemoveElement(aLocator);
- }
- ////////////////////////////////////////////////////
- // Geolocation
- ////////////////////////////////////////////////////
- NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Geolocation)
- NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
- NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMGeoGeolocation)
- NS_INTERFACE_MAP_ENTRY(nsIDOMGeoGeolocation)
- NS_INTERFACE_MAP_ENTRY(nsIGeolocationUpdate)
- NS_INTERFACE_MAP_END
- NS_IMPL_CYCLE_COLLECTING_ADDREF(Geolocation)
- NS_IMPL_CYCLE_COLLECTING_RELEASE(Geolocation)
- NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Geolocation,
- mPendingCallbacks,
- mWatchingCallbacks,
- mPendingRequests)
- Geolocation::Geolocation()
- : mLastWatchId(0)
- {
- }
- Geolocation::~Geolocation()
- {
- if (mService) {
- Shutdown();
- }
- }
- nsresult
- Geolocation::Init(nsPIDOMWindowInner* aContentDom)
- {
- // Remember the window
- if (aContentDom) {
- mOwner = do_GetWeakReference(aContentDom);
- if (!mOwner) {
- return NS_ERROR_FAILURE;
- }
- // Grab the principal of the document
- nsCOMPtr<nsIDocument> doc = aContentDom->GetDoc();
- if (!doc) {
- return NS_ERROR_FAILURE;
- }
- mPrincipal = doc->NodePrincipal();
- nsCOMPtr<nsIURI> uri;
- nsresult rv = mPrincipal->GetURI(getter_AddRefs(uri));
- NS_ENSURE_SUCCESS(rv, rv);
- }
- // If no aContentDom was passed into us, we are being used
- // by chrome/c++ and have no mOwner, no mPrincipal, and no need
- // to prompt.
- mService = nsGeolocationService::GetGeolocationService();
- if (mService) {
- mService->AddLocator(this);
- }
- return NS_OK;
- }
- void
- Geolocation::Shutdown()
- {
- // Release all callbacks
- mPendingCallbacks.Clear();
- mWatchingCallbacks.Clear();
- if (mService) {
- mService->RemoveLocator(this);
- mService->UpdateAccuracy();
- }
- mService = nullptr;
- mPrincipal = nullptr;
- }
- nsPIDOMWindowInner*
- Geolocation::GetParentObject() const {
- nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mOwner);
- return window.get();
- }
- bool
- Geolocation::HasActiveCallbacks()
- {
- return mPendingCallbacks.Length() || mWatchingCallbacks.Length();
- }
- bool
- Geolocation::HighAccuracyRequested()
- {
- for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) {
- if (mWatchingCallbacks[i]->WantsHighAccuracy()) {
- return true;
- }
- }
- for (uint32_t i = 0; i < mPendingCallbacks.Length(); i++) {
- if (mPendingCallbacks[i]->WantsHighAccuracy()) {
- return true;
- }
- }
- return false;
- }
- void
- Geolocation::RemoveRequest(nsGeolocationRequest* aRequest)
- {
- bool requestWasKnown =
- (mPendingCallbacks.RemoveElement(aRequest) !=
- mWatchingCallbacks.RemoveElement(aRequest));
- Unused << requestWasKnown;
- }
- NS_IMETHODIMP
- Geolocation::Update(nsIDOMGeoPosition *aSomewhere)
- {
- if (!WindowOwnerStillExists()) {
- Shutdown();
- return NS_OK;
- }
- if (aSomewhere) {
- nsCOMPtr<nsIDOMGeoPositionCoords> coords;
- aSomewhere->GetCoords(getter_AddRefs(coords));
- if (coords) {
- double accuracy = -1;
- coords->GetAccuracy(&accuracy);
- }
- }
- for (uint32_t i = mPendingCallbacks.Length(); i > 0; i--) {
- mPendingCallbacks[i-1]->Update(aSomewhere);
- RemoveRequest(mPendingCallbacks[i-1]);
- }
- // notify everyone that is watching
- for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) {
- mWatchingCallbacks[i]->Update(aSomewhere);
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- Geolocation::NotifyError(uint16_t aErrorCode)
- {
- if (!WindowOwnerStillExists()) {
- Shutdown();
- return NS_OK;
- }
- for (uint32_t i = mPendingCallbacks.Length(); i > 0; i--) {
- mPendingCallbacks[i-1]->NotifyErrorAndShutdown(aErrorCode);
- //NotifyErrorAndShutdown() removes the request from the array
- }
- // notify everyone that is watching
- for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) {
- mWatchingCallbacks[i]->NotifyErrorAndShutdown(aErrorCode);
- }
- return NS_OK;
- }
- bool
- Geolocation::IsAlreadyCleared(nsGeolocationRequest* aRequest)
- {
- for (uint32_t i = 0, length = mClearedWatchIDs.Length(); i < length; ++i) {
- if (mClearedWatchIDs[i] == aRequest->WatchId()) {
- return true;
- }
- }
- return false;
- }
- bool
- Geolocation::ClearPendingRequest(nsGeolocationRequest* aRequest)
- {
- if (aRequest->IsWatch() && this->IsAlreadyCleared(aRequest)) {
- this->NotifyAllowedRequest(aRequest);
- this->ClearWatch(aRequest->WatchId());
- return true;
- }
- return false;
- }
- void
- Geolocation::GetCurrentPosition(PositionCallback& aCallback,
- PositionErrorCallback* aErrorCallback,
- const PositionOptions& aOptions,
- ErrorResult& aRv)
- {
- nsresult rv = GetCurrentPosition(GeoPositionCallback(&aCallback),
- GeoPositionErrorCallback(aErrorCallback),
- Move(CreatePositionOptionsCopy(aOptions)));
- if (NS_FAILED(rv)) {
- aRv.Throw(rv);
- }
- return;
- }
- NS_IMETHODIMP
- Geolocation::GetCurrentPosition(nsIDOMGeoPositionCallback* aCallback,
- nsIDOMGeoPositionErrorCallback* aErrorCallback,
- UniquePtr<PositionOptions>&& aOptions)
- {
- NS_ENSURE_ARG_POINTER(aCallback);
- return GetCurrentPosition(GeoPositionCallback(aCallback),
- GeoPositionErrorCallback(aErrorCallback),
- Move(aOptions));
- }
- nsresult
- Geolocation::GetCurrentPosition(GeoPositionCallback callback,
- GeoPositionErrorCallback errorCallback,
- UniquePtr<PositionOptions>&& options)
- {
- if (mPendingCallbacks.Length() > MAX_GEO_REQUESTS_PER_WINDOW) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- // After this we hand over ownership of options to our nsGeolocationRequest.
- RefPtr<nsGeolocationRequest> request =
- new nsGeolocationRequest(this, Move(callback), Move(errorCallback),
- Move(options),
- false);
- if (!sGeoEnabled) {
- nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(false, request);
- NS_DispatchToMainThread(ev);
- return NS_OK;
- }
- if (!mOwner && !nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
- return NS_ERROR_FAILURE;
- }
- if (mOwner) {
- if (!RegisterRequestWithPrompt(request)) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- return NS_OK;
- }
- if (!nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
- return NS_ERROR_FAILURE;
- }
- nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(true, request);
- NS_DispatchToMainThread(ev);
- return NS_OK;
- }
- int32_t
- Geolocation::WatchPosition(PositionCallback& aCallback,
- PositionErrorCallback* aErrorCallback,
- const PositionOptions& aOptions,
- ErrorResult& aRv)
- {
- int32_t ret = 0;
- nsresult rv = WatchPosition(GeoPositionCallback(&aCallback),
- GeoPositionErrorCallback(aErrorCallback),
- Move(CreatePositionOptionsCopy(aOptions)), &ret);
- if (NS_FAILED(rv)) {
- aRv.Throw(rv);
- }
- return ret;
- }
- NS_IMETHODIMP
- Geolocation::WatchPosition(nsIDOMGeoPositionCallback *aCallback,
- nsIDOMGeoPositionErrorCallback *aErrorCallback,
- UniquePtr<PositionOptions>&& aOptions,
- int32_t* aRv)
- {
- NS_ENSURE_ARG_POINTER(aCallback);
- return WatchPosition(GeoPositionCallback(aCallback),
- GeoPositionErrorCallback(aErrorCallback),
- Move(aOptions), aRv);
- }
- nsresult
- Geolocation::WatchPosition(GeoPositionCallback aCallback,
- GeoPositionErrorCallback aErrorCallback,
- UniquePtr<PositionOptions>&& aOptions,
- int32_t* aRv)
- {
- if (mWatchingCallbacks.Length() > MAX_GEO_REQUESTS_PER_WINDOW) {
- return NS_ERROR_NOT_AVAILABLE;
- }
- // The watch ID:
- *aRv = mLastWatchId++;
- RefPtr<nsGeolocationRequest> request =
- new nsGeolocationRequest(this, Move(aCallback), Move(aErrorCallback),
- Move(aOptions),
- true, *aRv);
- if (!sGeoEnabled) {
- nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(false, request);
- NS_DispatchToMainThread(ev);
- return NS_OK;
- }
- if (!mOwner && !nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
- return NS_ERROR_FAILURE;
- }
- if (mOwner) {
- if (!RegisterRequestWithPrompt(request))
- return NS_ERROR_NOT_AVAILABLE;
- return NS_OK;
- }
- if (!nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
- return NS_ERROR_FAILURE;
- }
- request->Allow(JS::UndefinedHandleValue);
- return NS_OK;
- }
- NS_IMETHODIMP
- Geolocation::ClearWatch(int32_t aWatchId)
- {
- if (aWatchId < 0) {
- return NS_OK;
- }
- if (!mClearedWatchIDs.Contains(aWatchId)) {
- mClearedWatchIDs.AppendElement(aWatchId);
- }
- for (uint32_t i = 0, length = mWatchingCallbacks.Length(); i < length; ++i) {
- if (mWatchingCallbacks[i]->WatchId() == aWatchId) {
- mWatchingCallbacks[i]->Shutdown();
- RemoveRequest(mWatchingCallbacks[i]);
- mClearedWatchIDs.RemoveElement(aWatchId);
- break;
- }
- }
- // make sure we also search through the pending requests lists for
- // watches to clear...
- for (uint32_t i = 0, length = mPendingRequests.Length(); i < length; ++i) {
- if (mPendingRequests[i]->IsWatch() &&
- (mPendingRequests[i]->WatchId() == aWatchId)) {
- mPendingRequests[i]->Shutdown();
- mPendingRequests.RemoveElementAt(i);
- break;
- }
- }
- return NS_OK;
- }
- bool
- Geolocation::WindowOwnerStillExists()
- {
- // an owner was never set when Geolocation
- // was created, which means that this object
- // is being used without a window.
- if (mOwner == nullptr) {
- return true;
- }
- nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mOwner);
- if (window) {
- nsPIDOMWindowOuter* outer = window->GetOuterWindow();
- if (!outer || outer->GetCurrentInnerWindow() != window ||
- outer->Closed()) {
- return false;
- }
- }
- return true;
- }
- void
- Geolocation::NotifyAllowedRequest(nsGeolocationRequest* aRequest)
- {
- if (aRequest->IsWatch()) {
- mWatchingCallbacks.AppendElement(aRequest);
- } else {
- mPendingCallbacks.AppendElement(aRequest);
- }
- }
- bool
- Geolocation::RegisterRequestWithPrompt(nsGeolocationRequest* request)
- {
- if (Preferences::GetBool("geo.prompt.testing", false)) {
- bool allow = Preferences::GetBool("geo.prompt.testing.allow", false);
- nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(allow, request);
- NS_DispatchToMainThread(ev);
- return true;
- }
- nsCOMPtr<nsIRunnable> ev = new RequestPromptEvent(request, mOwner);
- NS_DispatchToMainThread(ev);
- return true;
- }
- JSObject*
- Geolocation::WrapObject(JSContext *aCtx, JS::Handle<JSObject*> aGivenProto)
- {
- return GeolocationBinding::Wrap(aCtx, this, aGivenProto);
- }
|