LCOV - code coverage report
Current view: top level - dom/geolocation - nsGeolocation.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 2 625 0.3 %
Date: 2017-07-14 16:53:18 Functions: 2 117 1.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "nsGeolocation.h"
       8             : 
       9             : #include "mozilla/ClearOnShutdown.h"
      10             : #include "mozilla/dom/ContentChild.h"
      11             : #include "mozilla/dom/PermissionMessageUtils.h"
      12             : #include "mozilla/Preferences.h"
      13             : #include "mozilla/Services.h"
      14             : #include "mozilla/Telemetry.h"
      15             : #include "mozilla/UniquePtr.h"
      16             : #include "mozilla/Unused.h"
      17             : #include "mozilla/WeakPtr.h"
      18             : #include "nsComponentManagerUtils.h"
      19             : #include "nsContentPermissionHelper.h"
      20             : #include "nsContentUtils.h"
      21             : #include "nsDOMClassInfoID.h"
      22             : #include "nsGlobalWindow.h"
      23             : #include "nsIDocument.h"
      24             : #include "nsIObserverService.h"
      25             : #include "nsIScriptError.h"
      26             : #include "nsPIDOMWindow.h"
      27             : #include "nsServiceManagerUtils.h"
      28             : #include "nsThreadUtils.h"
      29             : #include "nsXULAppAPI.h"
      30             : 
      31             : class nsIPrincipal;
      32             : 
      33             : #ifdef MOZ_WIDGET_ANDROID
      34             : #include "AndroidLocationProvider.h"
      35             : #endif
      36             : 
      37             : #ifdef MOZ_WIDGET_GONK
      38             : #include "GonkGPSGeolocationProvider.h"
      39             : #endif
      40             : 
      41             : #ifdef MOZ_GPSD
      42             : #include "GpsdLocationProvider.h"
      43             : #endif
      44             : 
      45             : #ifdef MOZ_WIDGET_COCOA
      46             : #include "CoreLocationLocationProvider.h"
      47             : #endif
      48             : 
      49             : #ifdef XP_WIN
      50             : #include "WindowsLocationProvider.h"
      51             : #include "mozilla/WindowsVersion.h"
      52             : #endif
      53             : 
      54             : // Some limit to the number of get or watch geolocation requests
      55             : // that a window can make.
      56             : #define MAX_GEO_REQUESTS_PER_WINDOW  1500
      57             : 
      58             : // This preference allows to override the "secure context" by
      59             : // default policy.
      60             : #define PREF_GEO_SECURITY_ALLOWINSECURE "geo.security.allowinsecure"
      61             : 
      62             : using mozilla::Unused;          // <snicker>
      63             : using namespace mozilla;
      64             : using namespace mozilla::dom;
      65             : 
      66             : class nsGeolocationRequest final
      67             :  : public nsIContentPermissionRequest
      68             :  , public nsIGeolocationUpdate
      69             :  , public SupportsWeakPtr<nsGeolocationRequest>
      70             : {
      71             :  public:
      72             :   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
      73             :   NS_DECL_NSICONTENTPERMISSIONREQUEST
      74             :   NS_DECL_NSIGEOLOCATIONUPDATE
      75             : 
      76           0 :   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsGeolocationRequest, nsIContentPermissionRequest)
      77             : 
      78             :   nsGeolocationRequest(Geolocation* aLocator,
      79             :                        GeoPositionCallback aCallback,
      80             :                        GeoPositionErrorCallback aErrorCallback,
      81             :                        UniquePtr<PositionOptions>&& aOptions,
      82             :                        uint8_t aProtocolType,
      83             :                        bool aWatchPositionRequest = false,
      84             :                        int32_t aWatchId = 0);
      85             : 
      86           0 :   MOZ_DECLARE_WEAKREFERENCE_TYPENAME(nsGeolocationRequest)
      87             : 
      88             :   void Shutdown();
      89             : 
      90             :   void SendLocation(nsIDOMGeoPosition* aLocation);
      91           0 :   bool WantsHighAccuracy() {return !mShutdown && mOptions && mOptions->mEnableHighAccuracy;}
      92             :   void SetTimeoutTimer();
      93             :   void StopTimeoutTimer();
      94             :   void NotifyErrorAndShutdown(uint16_t);
      95             :   nsIPrincipal* GetPrincipal();
      96             : 
      97           0 :   bool IsWatch() { return mIsWatchPositionRequest; }
      98           0 :   int32_t WatchId() { return mWatchId; }
      99             :  private:
     100             :   virtual ~nsGeolocationRequest();
     101             : 
     102             :   class TimerCallbackHolder final : public nsITimerCallback
     103             :   {
     104             :   public:
     105             :     NS_DECL_ISUPPORTS
     106             :     NS_DECL_NSITIMERCALLBACK
     107             : 
     108           0 :     explicit TimerCallbackHolder(nsGeolocationRequest* aRequest)
     109           0 :       : mRequest(aRequest)
     110           0 :     {}
     111             : 
     112             :   private:
     113           0 :     ~TimerCallbackHolder() = default;
     114             :     WeakPtr<nsGeolocationRequest> mRequest;
     115             :   };
     116             : 
     117             :   void Notify();
     118             : 
     119             :   bool mIsWatchPositionRequest;
     120             : 
     121             :   nsCOMPtr<nsITimer> mTimeoutTimer;
     122             :   GeoPositionCallback mCallback;
     123             :   GeoPositionErrorCallback mErrorCallback;
     124             :   UniquePtr<PositionOptions> mOptions;
     125             : 
     126             :   RefPtr<Geolocation> mLocator;
     127             : 
     128             :   int32_t mWatchId;
     129             :   bool mShutdown;
     130             :   nsCOMPtr<nsIContentPermissionRequester> mRequester;
     131             :   uint8_t mProtocolType;
     132             : };
     133             : 
     134             : static UniquePtr<PositionOptions>
     135           0 : CreatePositionOptionsCopy(const PositionOptions& aOptions)
     136             : {
     137           0 :   UniquePtr<PositionOptions> geoOptions = MakeUnique<PositionOptions>();
     138             : 
     139           0 :   geoOptions->mEnableHighAccuracy = aOptions.mEnableHighAccuracy;
     140           0 :   geoOptions->mMaximumAge = aOptions.mMaximumAge;
     141           0 :   geoOptions->mTimeout = aOptions.mTimeout;
     142             : 
     143           0 :   return geoOptions;
     144             : }
     145             : 
     146           0 : class RequestPromptEvent : public Runnable
     147             : {
     148             : public:
     149           0 :   RequestPromptEvent(nsGeolocationRequest* aRequest, nsWeakPtr aWindow)
     150           0 :     : mozilla::Runnable("RequestPromptEvent")
     151             :     , mRequest(aRequest)
     152           0 :     , mWindow(aWindow)
     153             :   {
     154           0 :   }
     155             : 
     156           0 :   NS_IMETHOD Run() override
     157             :   {
     158           0 :     nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mWindow);
     159           0 :     nsContentPermissionUtils::AskPermission(mRequest, window);
     160           0 :     return NS_OK;
     161             :   }
     162             : 
     163             : private:
     164             :   RefPtr<nsGeolocationRequest> mRequest;
     165             :   nsWeakPtr mWindow;
     166             : };
     167             : 
     168           0 : class RequestAllowEvent : public Runnable
     169             : {
     170             : public:
     171           0 :   RequestAllowEvent(int allow, nsGeolocationRequest* request)
     172           0 :     : mozilla::Runnable("RequestAllowEvent")
     173             :     , mAllow(allow)
     174           0 :     , mRequest(request)
     175             :   {
     176           0 :   }
     177             : 
     178           0 :   NS_IMETHOD Run() override {
     179           0 :     if (mAllow) {
     180           0 :       mRequest->Allow(JS::UndefinedHandleValue);
     181             :     } else {
     182           0 :       mRequest->Cancel();
     183             :     }
     184           0 :     return NS_OK;
     185             :   }
     186             : 
     187             : private:
     188             :   bool mAllow;
     189             :   RefPtr<nsGeolocationRequest> mRequest;
     190             : };
     191             : 
     192           0 : class RequestSendLocationEvent : public Runnable
     193             : {
     194             : public:
     195           0 :   RequestSendLocationEvent(nsIDOMGeoPosition* aPosition,
     196             :                            nsGeolocationRequest* aRequest)
     197           0 :     : mozilla::Runnable("RequestSendLocationEvent")
     198             :     , mPosition(aPosition)
     199           0 :     , mRequest(aRequest)
     200             :   {
     201           0 :   }
     202             : 
     203           0 :   NS_IMETHOD Run() override {
     204           0 :     mRequest->SendLocation(mPosition);
     205           0 :     return NS_OK;
     206             :   }
     207             : 
     208             : private:
     209             :   nsCOMPtr<nsIDOMGeoPosition> mPosition;
     210             :   RefPtr<nsGeolocationRequest> mRequest;
     211             :   RefPtr<Geolocation> mLocator;
     212             : };
     213             : 
     214             : ////////////////////////////////////////////////////
     215             : // PositionError
     216             : ////////////////////////////////////////////////////
     217             : 
     218           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PositionError)
     219           0 :   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     220           0 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMGeoPositionError)
     221           0 :   NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPositionError)
     222           0 : NS_INTERFACE_MAP_END
     223             : 
     224           0 : NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PositionError, mParent)
     225           0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(PositionError)
     226           0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(PositionError)
     227             : 
     228           0 : PositionError::PositionError(Geolocation* aParent, int16_t aCode)
     229             :   : mCode(aCode)
     230           0 :   , mParent(aParent)
     231             : {
     232           0 : }
     233             : 
     234             : PositionError::~PositionError() = default;
     235             : 
     236             : 
     237             : NS_IMETHODIMP
     238           0 : PositionError::GetCode(int16_t *aCode)
     239             : {
     240           0 :   NS_ENSURE_ARG_POINTER(aCode);
     241           0 :   *aCode = Code();
     242           0 :   return NS_OK;
     243             : }
     244             : 
     245             : NS_IMETHODIMP
     246           0 : PositionError::GetMessage(nsAString& aMessage)
     247             : {
     248           0 :   switch (mCode)
     249             :   {
     250             :     case nsIDOMGeoPositionError::PERMISSION_DENIED:
     251           0 :       aMessage = NS_LITERAL_STRING("User denied geolocation prompt");
     252           0 :       break;
     253             :     case nsIDOMGeoPositionError::POSITION_UNAVAILABLE:
     254           0 :       aMessage = NS_LITERAL_STRING("Unknown error acquiring position");
     255           0 :       break;
     256             :     case nsIDOMGeoPositionError::TIMEOUT:
     257           0 :       aMessage = NS_LITERAL_STRING("Position acquisition timed out");
     258           0 :       break;
     259             :     default:
     260           0 :       break;
     261             :   }
     262           0 :   return NS_OK;
     263             : }
     264             : 
     265             : Geolocation*
     266           0 : PositionError::GetParentObject() const
     267             : {
     268           0 :   return mParent;
     269             : }
     270             : 
     271             : JSObject*
     272           0 : PositionError::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
     273             : {
     274           0 :   return PositionErrorBinding::Wrap(aCx, this, aGivenProto);
     275             : }
     276             : 
     277             : void
     278           0 : PositionError::NotifyCallback(const GeoPositionErrorCallback& aCallback)
     279             : {
     280           0 :   nsAutoMicroTask mt;
     281           0 :   if (aCallback.HasWebIDLCallback()) {
     282           0 :     PositionErrorCallback* callback = aCallback.GetWebIDLCallback();
     283             : 
     284           0 :     if (callback) {
     285           0 :       callback->Call(*this);
     286             :     }
     287             :   } else {
     288           0 :     nsIDOMGeoPositionErrorCallback* callback = aCallback.GetXPCOMCallback();
     289           0 :     if (callback) {
     290           0 :       callback->HandleEvent(this);
     291             :     }
     292             :   }
     293           0 : }
     294             : ////////////////////////////////////////////////////
     295             : // nsGeolocationRequest
     296             : ////////////////////////////////////////////////////
     297             : 
     298           0 : nsGeolocationRequest::nsGeolocationRequest(Geolocation* aLocator,
     299             :                                            GeoPositionCallback aCallback,
     300             :                                            GeoPositionErrorCallback aErrorCallback,
     301             :                                            UniquePtr<PositionOptions>&& aOptions,
     302             :                                            uint8_t aProtocolType,
     303             :                                            bool aWatchPositionRequest,
     304           0 :                                            int32_t aWatchId)
     305             :   : mIsWatchPositionRequest(aWatchPositionRequest),
     306           0 :     mCallback(Move(aCallback)),
     307           0 :     mErrorCallback(Move(aErrorCallback)),
     308           0 :     mOptions(Move(aOptions)),
     309             :     mLocator(aLocator),
     310             :     mWatchId(aWatchId),
     311             :     mShutdown(false),
     312           0 :     mProtocolType(aProtocolType)
     313             : {
     314           0 :   if (nsCOMPtr<nsPIDOMWindowInner> win =
     315           0 :       do_QueryReferent(mLocator->GetOwner())) {
     316           0 :     mRequester = new nsContentPermissionRequester(win);
     317             :   }
     318           0 : }
     319             : 
     320           0 : nsGeolocationRequest::~nsGeolocationRequest()
     321             : {
     322           0 :   StopTimeoutTimer();
     323           0 : }
     324             : 
     325           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGeolocationRequest)
     326           0 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
     327           0 :   NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
     328           0 :   NS_INTERFACE_MAP_ENTRY(nsIGeolocationUpdate)
     329           0 : NS_INTERFACE_MAP_END
     330             : 
     331           0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGeolocationRequest)
     332           0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGeolocationRequest)
     333           0 : NS_IMPL_CYCLE_COLLECTION(nsGeolocationRequest, mCallback, mErrorCallback, mLocator)
     334             : 
     335             : void
     336           0 : nsGeolocationRequest::Notify()
     337             : {
     338           0 :   SetTimeoutTimer();
     339           0 :   NotifyErrorAndShutdown(nsIDOMGeoPositionError::TIMEOUT);
     340           0 : }
     341             : 
     342             : void
     343           0 : nsGeolocationRequest::NotifyErrorAndShutdown(uint16_t aErrorCode)
     344             : {
     345           0 :   MOZ_ASSERT(!mShutdown, "timeout after shutdown");
     346           0 :   if (!mIsWatchPositionRequest) {
     347           0 :     Shutdown();
     348           0 :     mLocator->RemoveRequest(this);
     349             :   }
     350             : 
     351           0 :   NotifyError(aErrorCode);
     352           0 : }
     353             : 
     354             : NS_IMETHODIMP
     355           0 : nsGeolocationRequest::GetPrincipal(nsIPrincipal * *aRequestingPrincipal)
     356             : {
     357           0 :   NS_ENSURE_ARG_POINTER(aRequestingPrincipal);
     358             : 
     359           0 :   nsCOMPtr<nsIPrincipal> principal = mLocator->GetPrincipal();
     360           0 :   principal.forget(aRequestingPrincipal);
     361             : 
     362           0 :   return NS_OK;
     363             : }
     364             : 
     365             : NS_IMETHODIMP
     366           0 : nsGeolocationRequest::GetTypes(nsIArray** aTypes)
     367             : {
     368           0 :   nsTArray<nsString> emptyOptions;
     369           0 :   return nsContentPermissionUtils::CreatePermissionArray(NS_LITERAL_CSTRING("geolocation"),
     370           0 :                                                          NS_LITERAL_CSTRING("unused"),
     371             :                                                          emptyOptions,
     372           0 :                                                          aTypes);
     373             : }
     374             : 
     375             : NS_IMETHODIMP
     376           0 : nsGeolocationRequest::GetWindow(mozIDOMWindow** aRequestingWindow)
     377             : {
     378           0 :   NS_ENSURE_ARG_POINTER(aRequestingWindow);
     379             : 
     380           0 :   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mLocator->GetOwner());
     381           0 :   window.forget(aRequestingWindow);
     382             : 
     383           0 :   return NS_OK;
     384             : }
     385             : 
     386             : NS_IMETHODIMP
     387           0 : nsGeolocationRequest::GetElement(nsIDOMElement * *aRequestingElement)
     388             : {
     389           0 :   NS_ENSURE_ARG_POINTER(aRequestingElement);
     390           0 :   *aRequestingElement = nullptr;
     391           0 :   return NS_OK;
     392             : }
     393             : 
     394             : NS_IMETHODIMP
     395           0 : nsGeolocationRequest::Cancel()
     396             : {
     397           0 :   if (mRequester) {
     398             :     // Record the number of denied requests for regular web content.
     399             :     // This method is only called when the user explicitly denies the request,
     400             :     // and is not called when the page is simply unloaded, or similar.
     401           0 :     Telemetry::Accumulate(Telemetry::GEOLOCATION_REQUEST_GRANTED, mProtocolType);
     402             :   }
     403             : 
     404           0 :   if (mLocator->ClearPendingRequest(this)) {
     405           0 :     return NS_OK;
     406             :   }
     407             : 
     408           0 :   NotifyError(nsIDOMGeoPositionError::PERMISSION_DENIED);
     409           0 :   return NS_OK;
     410             : }
     411             : 
     412             : NS_IMETHODIMP
     413           0 : nsGeolocationRequest::Allow(JS::HandleValue aChoices)
     414             : {
     415           0 :   MOZ_ASSERT(aChoices.isUndefined());
     416             : 
     417           0 :   if (mRequester) {
     418             :     // Record the number of granted requests for regular web content.
     419           0 :     Telemetry::Accumulate(Telemetry::GEOLOCATION_REQUEST_GRANTED, mProtocolType + 10);
     420             : 
     421             :     // Record whether a location callback is fulfilled while the owner window
     422             :     // is not visible.
     423           0 :     bool isVisible = false;
     424           0 :     nsCOMPtr<nsPIDOMWindowInner> window = mLocator->GetParentObject();
     425             : 
     426           0 :     if (window) {
     427           0 :       nsCOMPtr<nsIDocument> doc = window->GetDoc();
     428           0 :       isVisible = doc && !doc->Hidden();
     429             :     }
     430             : 
     431           0 :     if (IsWatch()) {
     432           0 :       mozilla::Telemetry::Accumulate(mozilla::Telemetry::GEOLOCATION_WATCHPOSITION_VISIBLE, isVisible);
     433             :     } else {
     434           0 :       mozilla::Telemetry::Accumulate(mozilla::Telemetry::GEOLOCATION_GETCURRENTPOSITION_VISIBLE, isVisible);
     435             :     }
     436             :   }
     437             : 
     438           0 :   if (mLocator->ClearPendingRequest(this)) {
     439           0 :     return NS_OK;
     440             :   }
     441             : 
     442           0 :   RefPtr<nsGeolocationService> gs = nsGeolocationService::GetGeolocationService();
     443             : 
     444           0 :   bool canUseCache = false;
     445           0 :   CachedPositionAndAccuracy lastPosition = gs->GetCachedPosition();
     446           0 :   if (lastPosition.position) {
     447             :     DOMTimeStamp cachedPositionTime_ms;
     448           0 :     lastPosition.position->GetTimestamp(&cachedPositionTime_ms);
     449             :     // check to see if we can use a cached value
     450             :     // if the user has specified a maximumAge, return a cached value.
     451           0 :     if (mOptions && mOptions->mMaximumAge > 0) {
     452           0 :       uint32_t maximumAge_ms = mOptions->mMaximumAge;
     453           0 :       bool isCachedWithinRequestedAccuracy = WantsHighAccuracy() <= lastPosition.isHighAccuracy;
     454             :       bool isCachedWithinRequestedTime =
     455           0 :         DOMTimeStamp(PR_Now() / PR_USEC_PER_MSEC - maximumAge_ms) <= cachedPositionTime_ms;
     456           0 :       canUseCache = isCachedWithinRequestedAccuracy && isCachedWithinRequestedTime;
     457             :     }
     458             :   }
     459             : 
     460           0 :   gs->UpdateAccuracy(WantsHighAccuracy());
     461           0 :   if (canUseCache) {
     462             :     // okay, we can return a cached position
     463             :     // getCurrentPosition requests serviced by the cache
     464             :     // will now be owned by the RequestSendLocationEvent
     465           0 :     Update(lastPosition.position);
     466             : 
     467             :     // After Update is called, getCurrentPosition finishes it's job.
     468           0 :     if (!mIsWatchPositionRequest) {
     469           0 :       return NS_OK;
     470             :     }
     471             : 
     472             :   } else {
     473             :     // if it is not a watch request and timeout is 0,
     474             :     // invoke the errorCallback (if present) with TIMEOUT code
     475           0 :     if (mOptions && mOptions->mTimeout == 0 && !mIsWatchPositionRequest) {
     476           0 :       NotifyError(nsIDOMGeoPositionError::TIMEOUT);
     477           0 :       return NS_OK;
     478             :     }
     479             : 
     480             :   }
     481             : 
     482             :   // Kick off the geo device, if it isn't already running
     483           0 :   nsresult rv = gs->StartDevice(GetPrincipal());
     484             : 
     485           0 :   if (NS_FAILED(rv)) {
     486             :     // Location provider error
     487           0 :     NotifyError(nsIDOMGeoPositionError::POSITION_UNAVAILABLE);
     488           0 :     return NS_OK;
     489             :   }
     490             : 
     491           0 :   if (mIsWatchPositionRequest || !canUseCache) {
     492             :     // let the locator know we're pending
     493             :     // we will now be owned by the locator
     494           0 :     mLocator->NotifyAllowedRequest(this);
     495             :   }
     496             : 
     497           0 :   SetTimeoutTimer();
     498             : 
     499           0 :   return NS_OK;
     500             : }
     501             : 
     502             : NS_IMETHODIMP
     503           0 : nsGeolocationRequest::GetRequester(nsIContentPermissionRequester** aRequester)
     504             : {
     505           0 :   NS_ENSURE_ARG_POINTER(aRequester);
     506             : 
     507           0 :   nsCOMPtr<nsIContentPermissionRequester> requester = mRequester;
     508           0 :   requester.forget(aRequester);
     509             : 
     510           0 :   return NS_OK;
     511             : }
     512             : 
     513             : void
     514           0 : nsGeolocationRequest::SetTimeoutTimer()
     515             : {
     516           0 :   MOZ_ASSERT(!mShutdown, "set timeout after shutdown");
     517             : 
     518           0 :   StopTimeoutTimer();
     519             : 
     520           0 :   if (mOptions && mOptions->mTimeout != 0 && mOptions->mTimeout != 0x7fffffff) {
     521           0 :     mTimeoutTimer = do_CreateInstance("@mozilla.org/timer;1");
     522           0 :     RefPtr<TimerCallbackHolder> holder = new TimerCallbackHolder(this);
     523           0 :     mTimeoutTimer->InitWithCallback(holder, mOptions->mTimeout, nsITimer::TYPE_ONE_SHOT);
     524             :   }
     525           0 : }
     526             : 
     527             : void
     528           0 : nsGeolocationRequest::StopTimeoutTimer()
     529             : {
     530           0 :   if (mTimeoutTimer) {
     531           0 :     mTimeoutTimer->Cancel();
     532           0 :     mTimeoutTimer = nullptr;
     533             :   }
     534           0 : }
     535             : 
     536             : void
     537           0 : nsGeolocationRequest::SendLocation(nsIDOMGeoPosition* aPosition)
     538             : {
     539           0 :   if (mShutdown) {
     540             :     // Ignore SendLocationEvents issued before we were cleared.
     541           0 :     return;
     542             :   }
     543             : 
     544           0 :   if (mOptions && mOptions->mMaximumAge > 0) {
     545             :     DOMTimeStamp positionTime_ms;
     546           0 :     aPosition->GetTimestamp(&positionTime_ms);
     547           0 :     const uint32_t maximumAge_ms = mOptions->mMaximumAge;
     548             :     const bool isTooOld =
     549           0 :         DOMTimeStamp(PR_Now() / PR_USEC_PER_MSEC - maximumAge_ms) > positionTime_ms;
     550           0 :     if (isTooOld) {
     551           0 :       return;
     552             :     }
     553             :   }
     554             : 
     555           0 :   RefPtr<Position> wrapped;
     556             : 
     557           0 :   if (aPosition) {
     558           0 :     nsCOMPtr<nsIDOMGeoPositionCoords> coords;
     559           0 :     aPosition->GetCoords(getter_AddRefs(coords));
     560           0 :     if (coords) {
     561           0 :       wrapped = new Position(ToSupports(mLocator), aPosition);
     562             :     }
     563             :   }
     564             : 
     565           0 :   if (!wrapped) {
     566           0 :     NotifyError(nsIDOMGeoPositionError::POSITION_UNAVAILABLE);
     567           0 :     return;
     568             :   }
     569             : 
     570           0 :   if (!mIsWatchPositionRequest) {
     571             :     // Cancel timer and position updates in case the position
     572             :     // callback spins the event loop
     573           0 :     Shutdown();
     574             :   }
     575             : 
     576           0 :   nsAutoMicroTask mt;
     577           0 :   if (mCallback.HasWebIDLCallback()) {
     578           0 :     PositionCallback* callback = mCallback.GetWebIDLCallback();
     579             : 
     580           0 :     MOZ_ASSERT(callback);
     581           0 :     callback->Call(*wrapped);
     582             :   } else {
     583           0 :     nsIDOMGeoPositionCallback* callback = mCallback.GetXPCOMCallback();
     584           0 :     MOZ_ASSERT(callback);
     585           0 :     callback->HandleEvent(aPosition);
     586             :   }
     587             : 
     588           0 :   if (mIsWatchPositionRequest && !mShutdown) {
     589           0 :     SetTimeoutTimer();
     590             :   }
     591           0 :   MOZ_ASSERT(mShutdown || mIsWatchPositionRequest,
     592             :              "non-shutdown getCurrentPosition request after callback!");
     593             : }
     594             : 
     595             : nsIPrincipal*
     596           0 : nsGeolocationRequest::GetPrincipal()
     597             : {
     598           0 :   if (!mLocator) {
     599           0 :     return nullptr;
     600             :   }
     601           0 :   return mLocator->GetPrincipal();
     602             : }
     603             : 
     604             : NS_IMETHODIMP
     605           0 : nsGeolocationRequest::Update(nsIDOMGeoPosition* aPosition)
     606             : {
     607           0 :   nsCOMPtr<nsIRunnable> ev = new RequestSendLocationEvent(aPosition, this);
     608           0 :   NS_DispatchToMainThread(ev);
     609           0 :   return NS_OK;
     610             : }
     611             : 
     612             : NS_IMETHODIMP
     613           0 : nsGeolocationRequest::NotifyError(uint16_t aErrorCode)
     614             : {
     615           0 :   MOZ_ASSERT(NS_IsMainThread());
     616           0 :   RefPtr<PositionError> positionError = new PositionError(mLocator, aErrorCode);
     617           0 :   positionError->NotifyCallback(mErrorCallback);
     618           0 :   return NS_OK;
     619             : }
     620             : 
     621             : void
     622           0 : nsGeolocationRequest::Shutdown()
     623             : {
     624           0 :   MOZ_ASSERT(!mShutdown, "request shutdown twice");
     625           0 :   mShutdown = true;
     626             : 
     627           0 :   StopTimeoutTimer();
     628             : 
     629             :   // If there are no other high accuracy requests, the geolocation service will
     630             :   // notify the provider to switch to the default accuracy.
     631           0 :   if (mOptions && mOptions->mEnableHighAccuracy) {
     632           0 :     RefPtr<nsGeolocationService> gs = nsGeolocationService::GetGeolocationService();
     633           0 :     if (gs) {
     634           0 :       gs->UpdateAccuracy();
     635             :     }
     636             :   }
     637           0 : }
     638             : 
     639             : 
     640             : ////////////////////////////////////////////////////
     641             : // nsGeolocationRequest::TimerCallbackHolder
     642             : ////////////////////////////////////////////////////
     643             : 
     644           0 : NS_IMPL_ISUPPORTS(nsGeolocationRequest::TimerCallbackHolder, nsISupports, nsITimerCallback)
     645             : 
     646             : NS_IMETHODIMP
     647           0 : nsGeolocationRequest::TimerCallbackHolder::Notify(nsITimer*)
     648             : {
     649           0 :   if (mRequest && mRequest->mLocator) {
     650           0 :     RefPtr<nsGeolocationRequest> request(mRequest);
     651           0 :     request->Notify();
     652             :   }
     653             : 
     654           0 :   return NS_OK;
     655             : }
     656             : 
     657             : 
     658             : ////////////////////////////////////////////////////
     659             : // nsGeolocationService
     660             : ////////////////////////////////////////////////////
     661           0 : NS_INTERFACE_MAP_BEGIN(nsGeolocationService)
     662           0 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIGeolocationUpdate)
     663           0 :   NS_INTERFACE_MAP_ENTRY(nsIGeolocationUpdate)
     664           0 :   NS_INTERFACE_MAP_ENTRY(nsIObserver)
     665           0 : NS_INTERFACE_MAP_END
     666             : 
     667           0 : NS_IMPL_ADDREF(nsGeolocationService)
     668           0 : NS_IMPL_RELEASE(nsGeolocationService)
     669             : 
     670             : 
     671             : static bool sGeoEnabled = true;
     672             : static int32_t sProviderTimeout = 6000; // Time, in milliseconds, to wait for the location provider to spin up.
     673             : 
     674           0 : nsresult nsGeolocationService::Init()
     675             : {
     676           0 :   Preferences::AddIntVarCache(&sProviderTimeout, "geo.timeout", sProviderTimeout);
     677           0 :   Preferences::AddBoolVarCache(&sGeoEnabled, "geo.enabled", sGeoEnabled);
     678             : 
     679           0 :   if (!sGeoEnabled) {
     680           0 :     return NS_ERROR_FAILURE;
     681             :   }
     682             : 
     683           0 :   if (XRE_IsContentProcess()) {
     684           0 :     return NS_OK;
     685             :   }
     686             : 
     687             :   // geolocation service can be enabled -> now register observer
     688           0 :   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     689           0 :   if (!obs) {
     690           0 :     return NS_ERROR_FAILURE;
     691             :   }
     692             : 
     693           0 :   obs->AddObserver(this, "xpcom-shutdown", false);
     694             : 
     695             : #ifdef MOZ_WIDGET_ANDROID
     696             :   mProvider = new AndroidLocationProvider();
     697             : #endif
     698             : 
     699             : #ifdef MOZ_WIDGET_GTK
     700             : #ifdef MOZ_GPSD
     701             :   if (Preferences::GetBool("geo.provider.use_gpsd", false)) {
     702             :     mProvider = new GpsdLocationProvider();
     703             :   }
     704             : #endif
     705             : #endif
     706             : 
     707             : #ifdef MOZ_WIDGET_COCOA
     708             :   if (Preferences::GetBool("geo.provider.use_corelocation", true)) {
     709             :     mProvider = new CoreLocationLocationProvider();
     710             :   }
     711             : #endif
     712             : 
     713             : #ifdef XP_WIN
     714             :   if (Preferences::GetBool("geo.provider.ms-windows-location", false) &&
     715             :       IsWin8OrLater()) {
     716             :     mProvider = new WindowsLocationProvider();
     717             :   }
     718             : #endif
     719             : 
     720           0 :   if (Preferences::GetBool("geo.provider.use_mls", false)) {
     721           0 :     mProvider = do_CreateInstance("@mozilla.org/geolocation/mls-provider;1");
     722             :   }
     723             : 
     724             :   // Override platform-specific providers with the default (network)
     725             :   // provider while testing. Our tests are currently not meant to exercise
     726             :   // the provider, and some tests rely on the network provider being used.
     727             :   // "geo.provider.testing" is always set for all plain and browser chrome
     728             :   // mochitests, and also for xpcshell tests.
     729           0 :   if (!mProvider || Preferences::GetBool("geo.provider.testing", false)) {
     730             :     nsCOMPtr<nsIGeolocationProvider> geoTestProvider =
     731           0 :       do_GetService(NS_GEOLOCATION_PROVIDER_CONTRACTID);
     732             : 
     733           0 :     if (geoTestProvider) {
     734           0 :       mProvider = geoTestProvider;
     735             :     }
     736             :   }
     737             : 
     738           0 :   return NS_OK;
     739             : }
     740             : 
     741             : nsGeolocationService::~nsGeolocationService() = default;
     742             : 
     743             : NS_IMETHODIMP
     744           0 : nsGeolocationService::Observe(nsISupports* aSubject,
     745             :                               const char* aTopic,
     746             :                               const char16_t* aData)
     747             : {
     748           0 :   if (!strcmp("xpcom-shutdown", aTopic)) {
     749           0 :     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     750           0 :     if (obs) {
     751           0 :       obs->RemoveObserver(this, "xpcom-shutdown");
     752             :     }
     753             : 
     754           0 :     for (uint32_t i = 0; i< mGeolocators.Length(); i++) {
     755           0 :       mGeolocators[i]->Shutdown();
     756             :     }
     757           0 :     StopDevice();
     758             : 
     759           0 :     return NS_OK;
     760             :   }
     761             : 
     762           0 :   if (!strcmp("timer-callback", aTopic)) {
     763             :     // decide if we can close down the service.
     764           0 :     for (uint32_t i = 0; i< mGeolocators.Length(); i++)
     765           0 :       if (mGeolocators[i]->HasActiveCallbacks()) {
     766           0 :         SetDisconnectTimer();
     767           0 :         return NS_OK;
     768             :       }
     769             : 
     770             :     // okay to close up.
     771           0 :     StopDevice();
     772           0 :     Update(nullptr);
     773           0 :     return NS_OK;
     774             :   }
     775             : 
     776           0 :   return NS_ERROR_FAILURE;
     777             : }
     778             : 
     779             : NS_IMETHODIMP
     780           0 : nsGeolocationService::Update(nsIDOMGeoPosition *aSomewhere)
     781             : {
     782           0 :   if (aSomewhere) {
     783           0 :     SetCachedPosition(aSomewhere);
     784             :   }
     785             : 
     786           0 :   for (uint32_t i = 0; i< mGeolocators.Length(); i++) {
     787           0 :     mGeolocators[i]->Update(aSomewhere);
     788             :   }
     789             : 
     790           0 :   return NS_OK;
     791             : }
     792             : 
     793             : NS_IMETHODIMP
     794           0 : nsGeolocationService::NotifyError(uint16_t aErrorCode)
     795             : {
     796           0 :   for (uint32_t i = 0; i < mGeolocators.Length(); i++) {
     797           0 :     mGeolocators[i]->NotifyError(aErrorCode);
     798             :   }
     799           0 :   return NS_OK;
     800             : }
     801             : 
     802             : void
     803           0 : nsGeolocationService::SetCachedPosition(nsIDOMGeoPosition* aPosition)
     804             : {
     805           0 :   mLastPosition.position = aPosition;
     806           0 :   mLastPosition.isHighAccuracy = mHigherAccuracy;
     807           0 : }
     808             : 
     809             : CachedPositionAndAccuracy
     810           0 : nsGeolocationService::GetCachedPosition()
     811             : {
     812           0 :   return mLastPosition;
     813             : }
     814             : 
     815             : nsresult
     816           0 : nsGeolocationService::StartDevice(nsIPrincipal *aPrincipal)
     817             : {
     818           0 :   if (!sGeoEnabled) {
     819           0 :     return NS_ERROR_NOT_AVAILABLE;
     820             :   }
     821             : 
     822             :   // We do not want to keep the geolocation devices online
     823             :   // indefinitely.
     824             :   // Close them down after a reasonable period of inactivivity.
     825           0 :   SetDisconnectTimer();
     826             : 
     827           0 :   if (XRE_IsContentProcess()) {
     828           0 :     ContentChild* cpc = ContentChild::GetSingleton();
     829           0 :     cpc->SendAddGeolocationListener(IPC::Principal(aPrincipal),
     830           0 :                                     HighAccuracyRequested());
     831           0 :     return NS_OK;
     832             :   }
     833             : 
     834             :   // Start them up!
     835           0 :   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     836           0 :   if (!obs) {
     837           0 :     return NS_ERROR_FAILURE;
     838             :   }
     839             : 
     840           0 :   if (!mProvider) {
     841           0 :     return NS_ERROR_FAILURE;
     842             :   }
     843             : 
     844             :   nsresult rv;
     845             : 
     846           0 :   if (NS_FAILED(rv = mProvider->Startup()) ||
     847           0 :       NS_FAILED(rv = mProvider->Watch(this))) {
     848             : 
     849           0 :     NotifyError(nsIDOMGeoPositionError::POSITION_UNAVAILABLE);
     850           0 :     return rv;
     851             :   }
     852             : 
     853           0 :   obs->NotifyObservers(mProvider,
     854             :                        "geolocation-device-events",
     855           0 :                        u"starting");
     856             : 
     857           0 :   return NS_OK;
     858             : }
     859             : 
     860             : void
     861           0 : nsGeolocationService::SetDisconnectTimer()
     862             : {
     863           0 :   if (!mDisconnectTimer) {
     864           0 :     mDisconnectTimer = do_CreateInstance("@mozilla.org/timer;1");
     865             :   } else {
     866           0 :     mDisconnectTimer->Cancel();
     867             :   }
     868             : 
     869           0 :   mDisconnectTimer->Init(this,
     870             :                          sProviderTimeout,
     871           0 :                          nsITimer::TYPE_ONE_SHOT);
     872           0 : }
     873             : 
     874             : bool
     875           0 : nsGeolocationService::HighAccuracyRequested()
     876             : {
     877           0 :   for (uint32_t i = 0; i < mGeolocators.Length(); i++) {
     878           0 :     if (mGeolocators[i]->HighAccuracyRequested()) {
     879           0 :       return true;
     880             :     }
     881             :   }
     882             : 
     883           0 :   return false;
     884             : }
     885             : 
     886             : void
     887           0 : nsGeolocationService::UpdateAccuracy(bool aForceHigh)
     888             : {
     889           0 :   bool highRequired = aForceHigh || HighAccuracyRequested();
     890             : 
     891           0 :   if (XRE_IsContentProcess()) {
     892           0 :     ContentChild* cpc = ContentChild::GetSingleton();
     893           0 :     if (cpc->IsAlive()) {
     894           0 :       cpc->SendSetGeolocationHigherAccuracy(highRequired);
     895             :     }
     896             : 
     897           0 :     return;
     898             :   }
     899             : 
     900           0 :   mProvider->SetHighAccuracy(!mHigherAccuracy && highRequired);
     901           0 :   mHigherAccuracy = highRequired;
     902             : }
     903             : 
     904             : void
     905           0 : nsGeolocationService::StopDevice()
     906             : {
     907           0 :   if (mDisconnectTimer) {
     908           0 :     mDisconnectTimer->Cancel();
     909           0 :     mDisconnectTimer = nullptr;
     910             :   }
     911             : 
     912           0 :   if (XRE_IsContentProcess()) {
     913           0 :     ContentChild* cpc = ContentChild::GetSingleton();
     914           0 :     cpc->SendRemoveGeolocationListener();
     915             : 
     916           0 :     return; // bail early
     917             :   }
     918             : 
     919           0 :   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     920           0 :   if (!obs) {
     921           0 :     return;
     922             :   }
     923             : 
     924           0 :   if (!mProvider) {
     925           0 :     return;
     926             :   }
     927             : 
     928           0 :   mHigherAccuracy = false;
     929             : 
     930           0 :   mProvider->Shutdown();
     931           0 :   obs->NotifyObservers(mProvider,
     932             :                        "geolocation-device-events",
     933           0 :                        u"shutdown");
     934             : }
     935             : 
     936           3 : StaticRefPtr<nsGeolocationService> nsGeolocationService::sService;
     937             : 
     938             : already_AddRefed<nsGeolocationService>
     939           0 : nsGeolocationService::GetGeolocationService()
     940             : {
     941           0 :   RefPtr<nsGeolocationService> result;
     942           0 :   if (nsGeolocationService::sService) {
     943           0 :     result = nsGeolocationService::sService;
     944             : 
     945           0 :     return result.forget();
     946             :   }
     947             : 
     948           0 :   result = new nsGeolocationService();
     949           0 :   if (NS_FAILED(result->Init())) {
     950           0 :     return nullptr;
     951             :   }
     952             : 
     953           0 :   ClearOnShutdown(&nsGeolocationService::sService);
     954           0 :   nsGeolocationService::sService = result;
     955           0 :   return result.forget();
     956             : }
     957             : 
     958             : void
     959           0 : nsGeolocationService::AddLocator(Geolocation* aLocator)
     960             : {
     961           0 :   mGeolocators.AppendElement(aLocator);
     962           0 : }
     963             : 
     964             : void
     965           0 : nsGeolocationService::RemoveLocator(Geolocation* aLocator)
     966             : {
     967           0 :   mGeolocators.RemoveElement(aLocator);
     968           0 : }
     969             : 
     970             : ////////////////////////////////////////////////////
     971             : // Geolocation
     972             : ////////////////////////////////////////////////////
     973             : 
     974           0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Geolocation)
     975           0 :   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     976           0 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMGeoGeolocation)
     977           0 :   NS_INTERFACE_MAP_ENTRY(nsIDOMGeoGeolocation)
     978           0 :   NS_INTERFACE_MAP_ENTRY(nsIGeolocationUpdate)
     979           0 : NS_INTERFACE_MAP_END
     980             : 
     981           0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(Geolocation)
     982           0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(Geolocation)
     983             : 
     984           0 : NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Geolocation,
     985             :                                       mPendingCallbacks,
     986             :                                       mWatchingCallbacks,
     987             :                                       mPendingRequests)
     988             : 
     989           0 : Geolocation::Geolocation()
     990             : : mProtocolType(ProtocolType::OTHER)
     991           0 : , mLastWatchId(0)
     992             : {
     993           0 : }
     994             : 
     995           0 : Geolocation::~Geolocation()
     996             : {
     997           0 :   if (mService) {
     998           0 :     Shutdown();
     999             :   }
    1000           0 : }
    1001             : 
    1002             : nsresult
    1003           0 : Geolocation::Init(nsPIDOMWindowInner* aContentDom)
    1004             : {
    1005             :   // Remember the window
    1006           0 :   if (aContentDom) {
    1007           0 :     mOwner = do_GetWeakReference(aContentDom);
    1008           0 :     if (!mOwner) {
    1009           0 :       return NS_ERROR_FAILURE;
    1010             :     }
    1011             : 
    1012             :     // Grab the principal of the document
    1013           0 :     nsCOMPtr<nsIDocument> doc = aContentDom->GetDoc();
    1014           0 :     if (!doc) {
    1015           0 :       return NS_ERROR_FAILURE;
    1016             :     }
    1017             : 
    1018           0 :     mPrincipal = doc->NodePrincipal();
    1019             : 
    1020           0 :     nsCOMPtr<nsIURI> uri;
    1021           0 :     nsresult rv = mPrincipal->GetURI(getter_AddRefs(uri));
    1022           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1023             : 
    1024           0 :     if (uri) {
    1025             :       bool isHttp;
    1026           0 :       rv = uri->SchemeIs("http", &isHttp);
    1027           0 :       NS_ENSURE_SUCCESS(rv, rv);
    1028             : 
    1029             :       bool isHttps;
    1030           0 :       rv = uri->SchemeIs("https", &isHttps);
    1031           0 :       NS_ENSURE_SUCCESS(rv, rv);
    1032             : 
    1033             :       // Store the protocol to send via telemetry later.
    1034           0 :       if (isHttp) {
    1035           0 :         mProtocolType = ProtocolType::HTTP;
    1036           0 :       } else if (isHttps) {
    1037           0 :         mProtocolType = ProtocolType::HTTPS;
    1038             :       }
    1039             :     }
    1040             :   }
    1041             : 
    1042             :   // If no aContentDom was passed into us, we are being used
    1043             :   // by chrome/c++ and have no mOwner, no mPrincipal, and no need
    1044             :   // to prompt.
    1045           0 :   mService = nsGeolocationService::GetGeolocationService();
    1046           0 :   if (mService) {
    1047           0 :     mService->AddLocator(this);
    1048             :   }
    1049             : 
    1050           0 :   return NS_OK;
    1051             : }
    1052             : 
    1053             : void
    1054           0 : Geolocation::Shutdown()
    1055             : {
    1056             :   // Release all callbacks
    1057           0 :   mPendingCallbacks.Clear();
    1058           0 :   mWatchingCallbacks.Clear();
    1059             : 
    1060           0 :   if (mService) {
    1061           0 :     mService->RemoveLocator(this);
    1062           0 :     mService->UpdateAccuracy();
    1063             :   }
    1064             : 
    1065           0 :   mService = nullptr;
    1066           0 :   mPrincipal = nullptr;
    1067           0 : }
    1068             : 
    1069             : nsPIDOMWindowInner*
    1070           0 : Geolocation::GetParentObject() const {
    1071           0 :   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mOwner);
    1072           0 :   return window.get();
    1073             : }
    1074             : 
    1075             : bool
    1076           0 : Geolocation::HasActiveCallbacks()
    1077             : {
    1078           0 :   return mPendingCallbacks.Length() || mWatchingCallbacks.Length();
    1079             : }
    1080             : 
    1081             : bool
    1082           0 : Geolocation::HighAccuracyRequested()
    1083             : {
    1084           0 :   for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) {
    1085           0 :     if (mWatchingCallbacks[i]->WantsHighAccuracy()) {
    1086           0 :       return true;
    1087             :     }
    1088             :   }
    1089             : 
    1090           0 :   for (uint32_t i = 0; i < mPendingCallbacks.Length(); i++) {
    1091           0 :     if (mPendingCallbacks[i]->WantsHighAccuracy()) {
    1092           0 :       return true;
    1093             :     }
    1094             :   }
    1095             : 
    1096           0 :   return false;
    1097             : }
    1098             : 
    1099             : void
    1100           0 : Geolocation::RemoveRequest(nsGeolocationRequest* aRequest)
    1101             : {
    1102             :   bool requestWasKnown =
    1103           0 :     (mPendingCallbacks.RemoveElement(aRequest) !=
    1104           0 :      mWatchingCallbacks.RemoveElement(aRequest));
    1105             : 
    1106             :   Unused << requestWasKnown;
    1107           0 : }
    1108             : 
    1109             : NS_IMETHODIMP
    1110           0 : Geolocation::Update(nsIDOMGeoPosition *aSomewhere)
    1111             : {
    1112           0 :   if (!WindowOwnerStillExists()) {
    1113           0 :     Shutdown();
    1114           0 :     return NS_OK;
    1115             :   }
    1116             : 
    1117           0 :   if (aSomewhere) {
    1118           0 :     nsCOMPtr<nsIDOMGeoPositionCoords> coords;
    1119           0 :     aSomewhere->GetCoords(getter_AddRefs(coords));
    1120           0 :     if (coords) {
    1121           0 :       double accuracy = -1;
    1122           0 :       coords->GetAccuracy(&accuracy);
    1123           0 :       mozilla::Telemetry::Accumulate(mozilla::Telemetry::GEOLOCATION_ACCURACY_EXPONENTIAL, accuracy);
    1124             :     }
    1125             :   }
    1126             : 
    1127           0 :   for (uint32_t i = mPendingCallbacks.Length(); i > 0; i--) {
    1128           0 :     mPendingCallbacks[i-1]->Update(aSomewhere);
    1129           0 :     RemoveRequest(mPendingCallbacks[i-1]);
    1130             :   }
    1131             : 
    1132             :   // notify everyone that is watching
    1133           0 :   for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) {
    1134           0 :     mWatchingCallbacks[i]->Update(aSomewhere);
    1135             :   }
    1136             : 
    1137           0 :   return NS_OK;
    1138             : }
    1139             : 
    1140             : NS_IMETHODIMP
    1141           0 : Geolocation::NotifyError(uint16_t aErrorCode)
    1142             : {
    1143           0 :   if (!WindowOwnerStillExists()) {
    1144           0 :     Shutdown();
    1145           0 :     return NS_OK;
    1146             :   }
    1147             : 
    1148           0 :   mozilla::Telemetry::Accumulate(mozilla::Telemetry::GEOLOCATION_ERROR, true);
    1149             : 
    1150           0 :   for (uint32_t i = mPendingCallbacks.Length(); i > 0; i--) {
    1151           0 :     mPendingCallbacks[i-1]->NotifyErrorAndShutdown(aErrorCode);
    1152             :     //NotifyErrorAndShutdown() removes the request from the array
    1153             :   }
    1154             : 
    1155             :   // notify everyone that is watching
    1156           0 :   for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) {
    1157           0 :     mWatchingCallbacks[i]->NotifyErrorAndShutdown(aErrorCode);
    1158             :   }
    1159             : 
    1160           0 :   return NS_OK;
    1161             : }
    1162             : 
    1163             : bool
    1164           0 : Geolocation::IsAlreadyCleared(nsGeolocationRequest* aRequest)
    1165             : {
    1166           0 :   for (uint32_t i = 0, length = mClearedWatchIDs.Length(); i < length; ++i) {
    1167           0 :     if (mClearedWatchIDs[i] == aRequest->WatchId()) {
    1168           0 :       return true;
    1169             :     }
    1170             :   }
    1171             : 
    1172           0 :   return false;
    1173             : }
    1174             : 
    1175             : bool
    1176           0 : Geolocation::ShouldBlockInsecureRequests() const
    1177             : {
    1178           0 :   if (Preferences::GetBool(PREF_GEO_SECURITY_ALLOWINSECURE, false)) {
    1179           0 :     return false;
    1180             :   }
    1181             : 
    1182           0 :   nsCOMPtr<nsPIDOMWindowInner> win = do_QueryReferent(mOwner);
    1183           0 :   if (!win) {
    1184           0 :     return false;
    1185             :   }
    1186             : 
    1187           0 :   nsCOMPtr<nsIDocument> doc = win->GetDoc();
    1188           0 :   if (!doc) {
    1189           0 :     return false;
    1190             :   }
    1191             : 
    1192           0 :   if (!nsGlobalWindow::Cast(win)->IsSecureContextIfOpenerIgnored()) {
    1193           0 :     nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
    1194           0 :                                     NS_LITERAL_CSTRING("DOM"), doc,
    1195             :                                     nsContentUtils::eDOM_PROPERTIES,
    1196           0 :                                     "GeolocationInsecureRequestIsForbidden");
    1197           0 :     return true;
    1198             :   }
    1199             : 
    1200           0 :   return false;
    1201             : }
    1202             : 
    1203             : bool
    1204           0 : Geolocation::ClearPendingRequest(nsGeolocationRequest* aRequest)
    1205             : {
    1206           0 :   if (aRequest->IsWatch() && this->IsAlreadyCleared(aRequest)) {
    1207           0 :     this->NotifyAllowedRequest(aRequest);
    1208           0 :     this->ClearWatch(aRequest->WatchId());
    1209           0 :     return true;
    1210             :   }
    1211             : 
    1212           0 :   return false;
    1213             : }
    1214             : 
    1215             : void
    1216           0 : Geolocation::GetCurrentPosition(PositionCallback& aCallback,
    1217             :                                 PositionErrorCallback* aErrorCallback,
    1218             :                                 const PositionOptions& aOptions,
    1219             :                                 CallerType aCallerType,
    1220             :                                 ErrorResult& aRv)
    1221             : {
    1222           0 :   nsresult rv = GetCurrentPosition(GeoPositionCallback(&aCallback),
    1223           0 :                                    GeoPositionErrorCallback(aErrorCallback),
    1224           0 :                                    Move(CreatePositionOptionsCopy(aOptions)),
    1225           0 :                                    aCallerType);
    1226             : 
    1227           0 :   if (NS_FAILED(rv)) {
    1228           0 :     aRv.Throw(rv);
    1229             :   }
    1230           0 : }
    1231             : 
    1232             : nsresult
    1233           0 : Geolocation::GetCurrentPosition(GeoPositionCallback callback,
    1234             :                                 GeoPositionErrorCallback errorCallback,
    1235             :                                 UniquePtr<PositionOptions>&& options,
    1236             :                                 CallerType aCallerType)
    1237             : {
    1238           0 :   if (mPendingCallbacks.Length() > MAX_GEO_REQUESTS_PER_WINDOW) {
    1239           0 :     return NS_ERROR_NOT_AVAILABLE;
    1240             :   }
    1241             : 
    1242             :   // After this we hand over ownership of options to our nsGeolocationRequest.
    1243             : 
    1244             :   // Count the number of requests per protocol/scheme.
    1245           0 :   Telemetry::Accumulate(Telemetry::GEOLOCATION_GETCURRENTPOSITION_SECURE_ORIGIN,
    1246           0 :                         static_cast<uint8_t>(mProtocolType));
    1247             : 
    1248             :   RefPtr<nsGeolocationRequest> request =
    1249           0 :     new nsGeolocationRequest(this, Move(callback), Move(errorCallback),
    1250           0 :                              Move(options), static_cast<uint8_t>(mProtocolType),
    1251           0 :                              false);
    1252             : 
    1253           0 :   if (!sGeoEnabled || ShouldBlockInsecureRequests()) {
    1254           0 :     nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(false, request);
    1255           0 :     NS_DispatchToMainThread(ev);
    1256           0 :     return NS_OK;
    1257             :   }
    1258             : 
    1259           0 :   if (!mOwner && aCallerType != CallerType::System) {
    1260           0 :     return NS_ERROR_FAILURE;
    1261             :   }
    1262             : 
    1263           0 :   if (mOwner) {
    1264           0 :     if (!RegisterRequestWithPrompt(request)) {
    1265           0 :       return NS_ERROR_NOT_AVAILABLE;
    1266             :     }
    1267             : 
    1268           0 :     return NS_OK;
    1269             :   }
    1270             : 
    1271           0 :   if (aCallerType != CallerType::System) {
    1272           0 :     return NS_ERROR_FAILURE;
    1273             :   }
    1274             : 
    1275           0 :   nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(true, request);
    1276           0 :   NS_DispatchToMainThread(ev);
    1277             : 
    1278           0 :   return NS_OK;
    1279             : }
    1280             : 
    1281             : int32_t
    1282           0 : Geolocation::WatchPosition(PositionCallback& aCallback,
    1283             :                            PositionErrorCallback* aErrorCallback,
    1284             :                            const PositionOptions& aOptions,
    1285             :                            CallerType aCallerType,
    1286             :                            ErrorResult& aRv)
    1287             : {
    1288           0 :   int32_t ret = 0;
    1289           0 :   nsresult rv = WatchPosition(GeoPositionCallback(&aCallback),
    1290           0 :                               GeoPositionErrorCallback(aErrorCallback),
    1291           0 :                               Move(CreatePositionOptionsCopy(aOptions)),
    1292             :                               aCallerType,
    1293           0 :                               &ret);
    1294             : 
    1295           0 :   if (NS_FAILED(rv)) {
    1296           0 :     aRv.Throw(rv);
    1297             :   }
    1298             : 
    1299           0 :   return ret;
    1300             : }
    1301             : 
    1302             : NS_IMETHODIMP
    1303           0 : Geolocation::WatchPosition(nsIDOMGeoPositionCallback *aCallback,
    1304             :                            nsIDOMGeoPositionErrorCallback *aErrorCallback,
    1305             :                            UniquePtr<PositionOptions>&& aOptions,
    1306             :                            int32_t* aRv)
    1307             : {
    1308           0 :   NS_ENSURE_ARG_POINTER(aCallback);
    1309             : 
    1310           0 :   return WatchPosition(GeoPositionCallback(aCallback),
    1311           0 :                        GeoPositionErrorCallback(aErrorCallback),
    1312           0 :                        Move(aOptions), CallerType::System,
    1313           0 :                        aRv);
    1314             : }
    1315             : 
    1316             : nsresult
    1317           0 : Geolocation::WatchPosition(GeoPositionCallback aCallback,
    1318             :                            GeoPositionErrorCallback aErrorCallback,
    1319             :                            UniquePtr<PositionOptions>&& aOptions,
    1320             :                            CallerType aCallerType,
    1321             :                            int32_t* aRv)
    1322             : {
    1323           0 :   if (mWatchingCallbacks.Length() > MAX_GEO_REQUESTS_PER_WINDOW) {
    1324           0 :     return NS_ERROR_NOT_AVAILABLE;
    1325             :   }
    1326             : 
    1327             :   // Count the number of requests per protocol/scheme.
    1328           0 :   Telemetry::Accumulate(Telemetry::GEOLOCATION_WATCHPOSITION_SECURE_ORIGIN,
    1329           0 :                         static_cast<uint8_t>(mProtocolType));
    1330             : 
    1331             :   // The watch ID:
    1332           0 :   *aRv = mLastWatchId++;
    1333             : 
    1334             :   RefPtr<nsGeolocationRequest> request =
    1335           0 :     new nsGeolocationRequest(this, Move(aCallback), Move(aErrorCallback),
    1336             :                              Move(aOptions),
    1337           0 :                              static_cast<uint8_t>(mProtocolType), true, *aRv);
    1338             : 
    1339           0 :   if (!sGeoEnabled || ShouldBlockInsecureRequests()) {
    1340           0 :     nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(false, request);
    1341           0 :     NS_DispatchToMainThread(ev);
    1342           0 :     return NS_OK;
    1343             :   }
    1344             : 
    1345           0 :   if (!mOwner && aCallerType != CallerType::System) {
    1346           0 :     return NS_ERROR_FAILURE;
    1347             :   }
    1348             : 
    1349           0 :   if (mOwner) {
    1350           0 :     if (!RegisterRequestWithPrompt(request))
    1351           0 :       return NS_ERROR_NOT_AVAILABLE;
    1352             : 
    1353           0 :     return NS_OK;
    1354             :   }
    1355             : 
    1356           0 :   if (aCallerType != CallerType::System) {
    1357           0 :     return NS_ERROR_FAILURE;
    1358             :   }
    1359             : 
    1360           0 :   request->Allow(JS::UndefinedHandleValue);
    1361             : 
    1362           0 :   return NS_OK;
    1363             : }
    1364             : 
    1365             : NS_IMETHODIMP
    1366           0 : Geolocation::ClearWatch(int32_t aWatchId)
    1367             : {
    1368           0 :   if (aWatchId < 0) {
    1369           0 :     return NS_OK;
    1370             :   }
    1371             : 
    1372           0 :   if (!mClearedWatchIDs.Contains(aWatchId)) {
    1373           0 :     mClearedWatchIDs.AppendElement(aWatchId);
    1374             :   }
    1375             : 
    1376           0 :   for (uint32_t i = 0, length = mWatchingCallbacks.Length(); i < length; ++i) {
    1377           0 :     if (mWatchingCallbacks[i]->WatchId() == aWatchId) {
    1378           0 :       mWatchingCallbacks[i]->Shutdown();
    1379           0 :       RemoveRequest(mWatchingCallbacks[i]);
    1380           0 :       mClearedWatchIDs.RemoveElement(aWatchId);
    1381           0 :       break;
    1382             :     }
    1383             :   }
    1384             : 
    1385             :   // make sure we also search through the pending requests lists for
    1386             :   // watches to clear...
    1387           0 :   for (uint32_t i = 0, length = mPendingRequests.Length(); i < length; ++i) {
    1388           0 :     if (mPendingRequests[i]->IsWatch() &&
    1389           0 :         (mPendingRequests[i]->WatchId() == aWatchId)) {
    1390           0 :       mPendingRequests[i]->Shutdown();
    1391           0 :       mPendingRequests.RemoveElementAt(i);
    1392           0 :       break;
    1393             :     }
    1394             :   }
    1395             : 
    1396           0 :   return NS_OK;
    1397             : }
    1398             : 
    1399             : bool
    1400           0 : Geolocation::WindowOwnerStillExists()
    1401             : {
    1402             :   // an owner was never set when Geolocation
    1403             :   // was created, which means that this object
    1404             :   // is being used without a window.
    1405           0 :   if (mOwner == nullptr) {
    1406           0 :     return true;
    1407             :   }
    1408             : 
    1409           0 :   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mOwner);
    1410             : 
    1411           0 :   if (window) {
    1412           0 :     nsPIDOMWindowOuter* outer = window->GetOuterWindow();
    1413           0 :     if (!outer || outer->GetCurrentInnerWindow() != window ||
    1414           0 :         outer->Closed()) {
    1415           0 :       return false;
    1416             :     }
    1417             :   }
    1418             : 
    1419           0 :   return true;
    1420             : }
    1421             : 
    1422             : void
    1423           0 : Geolocation::NotifyAllowedRequest(nsGeolocationRequest* aRequest)
    1424             : {
    1425           0 :   if (aRequest->IsWatch()) {
    1426           0 :     mWatchingCallbacks.AppendElement(aRequest);
    1427             :   } else {
    1428           0 :     mPendingCallbacks.AppendElement(aRequest);
    1429             :   }
    1430           0 : }
    1431             : 
    1432             : bool
    1433           0 : Geolocation::RegisterRequestWithPrompt(nsGeolocationRequest* request)
    1434             : {
    1435           0 :   if (Preferences::GetBool("geo.prompt.testing", false)) {
    1436           0 :     bool allow = Preferences::GetBool("geo.prompt.testing.allow", false);
    1437           0 :     nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(allow, request);
    1438           0 :     NS_DispatchToMainThread(ev);
    1439           0 :     return true;
    1440             :   }
    1441             : 
    1442           0 :   nsCOMPtr<nsIRunnable> ev  = new RequestPromptEvent(request, mOwner);
    1443           0 :   NS_DispatchToMainThread(ev);
    1444           0 :   return true;
    1445             : }
    1446             : 
    1447             : JSObject*
    1448           0 : Geolocation::WrapObject(JSContext *aCtx, JS::Handle<JSObject*> aGivenProto)
    1449             : {
    1450           0 :   return GeolocationBinding::Wrap(aCtx, this, aGivenProto);
    1451           9 : }

Generated by: LCOV version 1.13