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

Generated by: LCOV version 1.13-14-ga5dd952