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 "Geolocation.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/GeolocationPositionError.h"
14 #include "mozilla/dom/GeolocationPositionErrorBinding.h"
15 #include "mozilla/Preferences.h"
16 #include "mozilla/Services.h"
17 #include "mozilla/StaticPrefs_geo.h"
18 #include "mozilla/Telemetry.h"
19 #include "mozilla/UniquePtr.h"
20 #include "mozilla/Unused.h"
21 #include "mozilla/WeakPtr.h"
22 #include "mozilla/EventStateManager.h"
23 #include "nsComponentManagerUtils.h"
24 #include "nsContentPermissionHelper.h"
25 #include "nsContentUtils.h"
26 #include "nsGlobalWindow.h"
27 #include "mozilla/dom/Document.h"
28 #include "nsINamed.h"
29 #include "nsIObserverService.h"
30 #include "nsIScriptError.h"
31 #include "nsPIDOMWindow.h"
32 #include "nsServiceManagerUtils.h"
33 #include "nsThreadUtils.h"
34 #include "nsXULAppAPI.h"
35 
36 class nsIPrincipal;
37 
38 #ifdef MOZ_WIDGET_ANDROID
39 #  include "AndroidLocationProvider.h"
40 #endif
41 
42 #ifdef MOZ_GPSD
43 #  include "GpsdLocationProvider.h"
44 #endif
45 
46 #ifdef MOZ_WIDGET_COCOA
47 #  include "CoreLocationLocationProvider.h"
48 #endif
49 
50 #ifdef XP_WIN
51 #  include "WindowsLocationProvider.h"
52 #  include "mozilla/WindowsVersion.h"
53 #endif
54 
55 // Some limit to the number of get or watch geolocation requests
56 // that a window can make.
57 #define MAX_GEO_REQUESTS_PER_WINDOW 1500
58 
59 // This preference allows to override the "secure context" by
60 // default policy.
61 #define PREF_GEO_SECURITY_ALLOWINSECURE "geo.security.allowinsecure"
62 
63 using mozilla::Unused;  // <snicker>
64 using namespace mozilla;
65 using namespace mozilla::dom;
66 
67 class nsGeolocationRequest final : public ContentPermissionRequestBase,
68                                    public nsIGeolocationUpdate,
69                                    public SupportsWeakPtr {
70  public:
71   NS_DECL_ISUPPORTS_INHERITED
72   NS_DECL_NSIGEOLOCATIONUPDATE
73 
74   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsGeolocationRequest,
75                                            ContentPermissionRequestBase)
76 
77   nsGeolocationRequest(Geolocation* aLocator, GeoPositionCallback aCallback,
78                        GeoPositionErrorCallback aErrorCallback,
79                        UniquePtr<PositionOptions>&& aOptions,
80                        nsIEventTarget* aMainThreadTarget,
81                        bool aWatchPositionRequest = false,
82                        int32_t aWatchId = 0);
83 
84   // nsIContentPermissionRequest
85   MOZ_CAN_RUN_SCRIPT NS_IMETHOD Cancel(void) override;
86   MOZ_CAN_RUN_SCRIPT NS_IMETHOD Allow(JS::HandleValue choices) override;
87 
88   void Shutdown();
89 
90   // MOZ_CAN_RUN_SCRIPT_BOUNDARY is OK here because we're always called from a
91   // runnable.  Ideally nsIRunnable::Run and its overloads would just be
92   // MOZ_CAN_RUN_SCRIPT and then we could be too...
93   MOZ_CAN_RUN_SCRIPT_BOUNDARY
94   void SendLocation(nsIDOMGeoPosition* aLocation);
WantsHighAccuracy()95   bool WantsHighAccuracy() {
96     return !mShutdown && mOptions && mOptions->mEnableHighAccuracy;
97   }
98   void SetTimeoutTimer();
99   void StopTimeoutTimer();
100   MOZ_CAN_RUN_SCRIPT
101   void NotifyErrorAndShutdown(uint16_t);
102   using ContentPermissionRequestBase::GetPrincipal;
103   nsIPrincipal* GetPrincipal();
104 
IsWatch()105   bool IsWatch() { return mIsWatchPositionRequest; }
WatchId()106   int32_t WatchId() { return mWatchId; }
107 
108  private:
109   virtual ~nsGeolocationRequest();
110 
111   class TimerCallbackHolder final : public nsITimerCallback, public nsINamed {
112    public:
113     NS_DECL_ISUPPORTS
114     NS_DECL_NSITIMERCALLBACK
115 
TimerCallbackHolder(nsGeolocationRequest * aRequest)116     explicit TimerCallbackHolder(nsGeolocationRequest* aRequest)
117         : mRequest(aRequest) {}
118 
GetName(nsACString & aName)119     NS_IMETHOD GetName(nsACString& aName) override {
120       aName.AssignLiteral("nsGeolocationRequest::TimerCallbackHolder");
121       return NS_OK;
122     }
123 
124    private:
125     ~TimerCallbackHolder() = default;
126     WeakPtr<nsGeolocationRequest> mRequest;
127   };
128 
129   // Only called from a timer, so MOZ_CAN_RUN_SCRIPT_BOUNDARY ok for now.
130   MOZ_CAN_RUN_SCRIPT_BOUNDARY void Notify();
131 
132   bool mIsWatchPositionRequest;
133 
134   nsCOMPtr<nsITimer> mTimeoutTimer;
135   GeoPositionCallback mCallback;
136   GeoPositionErrorCallback mErrorCallback;
137   UniquePtr<PositionOptions> mOptions;
138 
139   RefPtr<Geolocation> mLocator;
140 
141   int32_t mWatchId;
142   bool mShutdown;
143   nsCOMPtr<nsIEventTarget> mMainThreadTarget;
144 };
145 
CreatePositionOptionsCopy(const PositionOptions & aOptions)146 static UniquePtr<PositionOptions> CreatePositionOptionsCopy(
147     const PositionOptions& aOptions) {
148   UniquePtr<PositionOptions> geoOptions = MakeUnique<PositionOptions>();
149 
150   geoOptions->mEnableHighAccuracy = aOptions.mEnableHighAccuracy;
151   geoOptions->mMaximumAge = aOptions.mMaximumAge;
152   geoOptions->mTimeout = aOptions.mTimeout;
153 
154   return geoOptions;
155 }
156 
157 class RequestSendLocationEvent : public Runnable {
158  public:
RequestSendLocationEvent(nsIDOMGeoPosition * aPosition,nsGeolocationRequest * aRequest)159   RequestSendLocationEvent(nsIDOMGeoPosition* aPosition,
160                            nsGeolocationRequest* aRequest)
161       : mozilla::Runnable("RequestSendLocationEvent"),
162         mPosition(aPosition),
163         mRequest(aRequest) {}
164 
Run()165   NS_IMETHOD Run() override {
166     mRequest->SendLocation(mPosition);
167     return NS_OK;
168   }
169 
170  private:
171   nsCOMPtr<nsIDOMGeoPosition> mPosition;
172   RefPtr<nsGeolocationRequest> mRequest;
173   RefPtr<Geolocation> mLocator;
174 };
175 
176 ////////////////////////////////////////////////////
177 // nsGeolocationRequest
178 ////////////////////////////////////////////////////
179 
ConvertWeakReferenceToWindow(nsIWeakReference * aWeakPtr)180 static nsPIDOMWindowInner* ConvertWeakReferenceToWindow(
181     nsIWeakReference* aWeakPtr) {
182   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(aWeakPtr);
183   // This isn't usually safe, but here we're just extracting a raw pointer in
184   // order to pass it to a base class constructor which will in turn convert it
185   // into a strong pointer for us.
186   nsPIDOMWindowInner* raw = window.get();
187   return raw;
188 }
189 
nsGeolocationRequest(Geolocation * aLocator,GeoPositionCallback aCallback,GeoPositionErrorCallback aErrorCallback,UniquePtr<PositionOptions> && aOptions,nsIEventTarget * aMainThreadTarget,bool aWatchPositionRequest,int32_t aWatchId)190 nsGeolocationRequest::nsGeolocationRequest(
191     Geolocation* aLocator, GeoPositionCallback aCallback,
192     GeoPositionErrorCallback aErrorCallback,
193     UniquePtr<PositionOptions>&& aOptions, nsIEventTarget* aMainThreadTarget,
194     bool aWatchPositionRequest, int32_t aWatchId)
195     : ContentPermissionRequestBase(
196           aLocator->GetPrincipal(),
197           ConvertWeakReferenceToWindow(aLocator->GetOwner()), "geo"_ns,
198           "geolocation"_ns),
199       mIsWatchPositionRequest(aWatchPositionRequest),
200       mCallback(std::move(aCallback)),
201       mErrorCallback(std::move(aErrorCallback)),
202       mOptions(std::move(aOptions)),
203       mLocator(aLocator),
204       mWatchId(aWatchId),
205       mShutdown(false),
206       mMainThreadTarget(aMainThreadTarget) {
207   if (nsCOMPtr<nsPIDOMWindowInner> win =
208           do_QueryReferent(mLocator->GetOwner())) {
209   }
210 }
211 
~nsGeolocationRequest()212 nsGeolocationRequest::~nsGeolocationRequest() { StopTimeoutTimer(); }
213 
NS_IMPL_QUERY_INTERFACE_CYCLE_COLLECTION_INHERITED(nsGeolocationRequest,ContentPermissionRequestBase,nsIGeolocationUpdate)214 NS_IMPL_QUERY_INTERFACE_CYCLE_COLLECTION_INHERITED(nsGeolocationRequest,
215                                                    ContentPermissionRequestBase,
216                                                    nsIGeolocationUpdate)
217 
218 NS_IMPL_ADDREF_INHERITED(nsGeolocationRequest, ContentPermissionRequestBase)
219 NS_IMPL_RELEASE_INHERITED(nsGeolocationRequest, ContentPermissionRequestBase)
220 NS_IMPL_CYCLE_COLLECTION_WEAK_PTR_INHERITED(nsGeolocationRequest,
221                                             ContentPermissionRequestBase,
222                                             mCallback, mErrorCallback, mLocator)
223 
224 void nsGeolocationRequest::Notify() {
225   SetTimeoutTimer();
226   NotifyErrorAndShutdown(GeolocationPositionError_Binding::TIMEOUT);
227 }
228 
NotifyErrorAndShutdown(uint16_t aErrorCode)229 void nsGeolocationRequest::NotifyErrorAndShutdown(uint16_t aErrorCode) {
230   MOZ_ASSERT(!mShutdown, "timeout after shutdown");
231   if (!mIsWatchPositionRequest) {
232     Shutdown();
233     mLocator->RemoveRequest(this);
234   }
235 
236   NotifyError(aErrorCode);
237 }
238 
239 NS_IMETHODIMP
Cancel()240 nsGeolocationRequest::Cancel() {
241   if (mLocator->ClearPendingRequest(this)) {
242     return NS_OK;
243   }
244 
245   NotifyError(GeolocationPositionError_Binding::PERMISSION_DENIED);
246   return NS_OK;
247 }
248 
249 NS_IMETHODIMP
Allow(JS::HandleValue aChoices)250 nsGeolocationRequest::Allow(JS::HandleValue aChoices) {
251   MOZ_ASSERT(aChoices.isUndefined());
252 
253   if (mLocator->ClearPendingRequest(this)) {
254     return NS_OK;
255   }
256 
257   RefPtr<nsGeolocationService> gs =
258       nsGeolocationService::GetGeolocationService();
259 
260   bool canUseCache = false;
261   CachedPositionAndAccuracy lastPosition = gs->GetCachedPosition();
262   if (lastPosition.position) {
263     EpochTimeStamp cachedPositionTime_ms;
264     lastPosition.position->GetTimestamp(&cachedPositionTime_ms);
265     // check to see if we can use a cached value
266     // if the user has specified a maximumAge, return a cached value.
267     if (mOptions && mOptions->mMaximumAge > 0) {
268       uint32_t maximumAge_ms = mOptions->mMaximumAge;
269       bool isCachedWithinRequestedAccuracy =
270           WantsHighAccuracy() <= lastPosition.isHighAccuracy;
271       bool isCachedWithinRequestedTime =
272           EpochTimeStamp(PR_Now() / PR_USEC_PER_MSEC - maximumAge_ms) <=
273           cachedPositionTime_ms;
274       canUseCache =
275           isCachedWithinRequestedAccuracy && isCachedWithinRequestedTime;
276     }
277   }
278 
279   gs->UpdateAccuracy(WantsHighAccuracy());
280   if (canUseCache) {
281     // okay, we can return a cached position
282     // getCurrentPosition requests serviced by the cache
283     // will now be owned by the RequestSendLocationEvent
284     Update(lastPosition.position);
285 
286     // After Update is called, getCurrentPosition finishes it's job.
287     if (!mIsWatchPositionRequest) {
288       return NS_OK;
289     }
290 
291   } else {
292     // if it is not a watch request and timeout is 0,
293     // invoke the errorCallback (if present) with TIMEOUT code
294     if (mOptions && mOptions->mTimeout == 0 && !mIsWatchPositionRequest) {
295       NotifyError(GeolocationPositionError_Binding::TIMEOUT);
296       return NS_OK;
297     }
298   }
299 
300   // Kick off the geo device, if it isn't already running
301   nsCOMPtr<nsIPrincipal> principal = GetPrincipal();
302   nsresult rv = gs->StartDevice(principal);
303 
304   if (NS_FAILED(rv)) {
305     // Location provider error
306     NotifyError(GeolocationPositionError_Binding::POSITION_UNAVAILABLE);
307     return NS_OK;
308   }
309 
310   if (mIsWatchPositionRequest || !canUseCache) {
311     // let the locator know we're pending
312     // we will now be owned by the locator
313     mLocator->NotifyAllowedRequest(this);
314   }
315 
316   SetTimeoutTimer();
317 
318   return NS_OK;
319 }
320 
SetTimeoutTimer()321 void nsGeolocationRequest::SetTimeoutTimer() {
322   MOZ_ASSERT(!mShutdown, "set timeout after shutdown");
323 
324   StopTimeoutTimer();
325 
326   if (mOptions && mOptions->mTimeout != 0 && mOptions->mTimeout != 0x7fffffff) {
327     RefPtr<TimerCallbackHolder> holder = new TimerCallbackHolder(this);
328     NS_NewTimerWithCallback(getter_AddRefs(mTimeoutTimer), holder,
329                             mOptions->mTimeout, nsITimer::TYPE_ONE_SHOT);
330   }
331 }
332 
StopTimeoutTimer()333 void nsGeolocationRequest::StopTimeoutTimer() {
334   if (mTimeoutTimer) {
335     mTimeoutTimer->Cancel();
336     mTimeoutTimer = nullptr;
337   }
338 }
339 
SendLocation(nsIDOMGeoPosition * aPosition)340 void nsGeolocationRequest::SendLocation(nsIDOMGeoPosition* aPosition) {
341   if (mShutdown) {
342     // Ignore SendLocationEvents issued before we were cleared.
343     return;
344   }
345 
346   if (mOptions && mOptions->mMaximumAge > 0) {
347     EpochTimeStamp positionTime_ms;
348     aPosition->GetTimestamp(&positionTime_ms);
349     const uint32_t maximumAge_ms = mOptions->mMaximumAge;
350     const bool isTooOld = EpochTimeStamp(PR_Now() / PR_USEC_PER_MSEC -
351                                          maximumAge_ms) > positionTime_ms;
352     if (isTooOld) {
353       return;
354     }
355   }
356 
357   RefPtr<mozilla::dom::GeolocationPosition> wrapped;
358 
359   if (aPosition) {
360     nsCOMPtr<nsIDOMGeoPositionCoords> coords;
361     aPosition->GetCoords(getter_AddRefs(coords));
362     if (coords) {
363       wrapped = new mozilla::dom::GeolocationPosition(ToSupports(mLocator),
364                                                       aPosition);
365     }
366   }
367 
368   if (!wrapped) {
369     NotifyError(GeolocationPositionError_Binding::POSITION_UNAVAILABLE);
370     return;
371   }
372 
373   if (!mIsWatchPositionRequest) {
374     // Cancel timer and position updates in case the position
375     // callback spins the event loop
376     Shutdown();
377   }
378 
379   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
380   obs->NotifyObservers(wrapped, "geolocation-position-events",
381                        u"location-updated");
382 
383   nsAutoMicroTask mt;
384   if (mCallback.HasWebIDLCallback()) {
385     RefPtr<PositionCallback> callback = mCallback.GetWebIDLCallback();
386 
387     MOZ_ASSERT(callback);
388     callback->Call(*wrapped);
389   } else {
390     nsIDOMGeoPositionCallback* callback = mCallback.GetXPCOMCallback();
391     MOZ_ASSERT(callback);
392     callback->HandleEvent(aPosition);
393   }
394 
395   if (mIsWatchPositionRequest && !mShutdown) {
396     SetTimeoutTimer();
397   }
398   MOZ_ASSERT(mShutdown || mIsWatchPositionRequest,
399              "non-shutdown getCurrentPosition request after callback!");
400 }
401 
GetPrincipal()402 nsIPrincipal* nsGeolocationRequest::GetPrincipal() {
403   if (!mLocator) {
404     return nullptr;
405   }
406   return mLocator->GetPrincipal();
407 }
408 
409 NS_IMETHODIMP
Update(nsIDOMGeoPosition * aPosition)410 nsGeolocationRequest::Update(nsIDOMGeoPosition* aPosition) {
411   nsCOMPtr<nsIRunnable> ev = new RequestSendLocationEvent(aPosition, this);
412   mMainThreadTarget->Dispatch(ev.forget());
413   return NS_OK;
414 }
415 
416 NS_IMETHODIMP
NotifyError(uint16_t aErrorCode)417 nsGeolocationRequest::NotifyError(uint16_t aErrorCode) {
418   MOZ_ASSERT(NS_IsMainThread());
419   RefPtr<GeolocationPositionError> positionError =
420       new GeolocationPositionError(mLocator, aErrorCode);
421   positionError->NotifyCallback(mErrorCallback);
422   return NS_OK;
423 }
424 
Shutdown()425 void nsGeolocationRequest::Shutdown() {
426   MOZ_ASSERT(!mShutdown, "request shutdown twice");
427   mShutdown = true;
428 
429   StopTimeoutTimer();
430 
431   // If there are no other high accuracy requests, the geolocation service will
432   // notify the provider to switch to the default accuracy.
433   if (mOptions && mOptions->mEnableHighAccuracy) {
434     RefPtr<nsGeolocationService> gs =
435         nsGeolocationService::GetGeolocationService();
436     if (gs) {
437       gs->UpdateAccuracy();
438     }
439   }
440 }
441 
442 ////////////////////////////////////////////////////
443 // nsGeolocationRequest::TimerCallbackHolder
444 ////////////////////////////////////////////////////
445 
NS_IMPL_ISUPPORTS(nsGeolocationRequest::TimerCallbackHolder,nsITimerCallback,nsINamed)446 NS_IMPL_ISUPPORTS(nsGeolocationRequest::TimerCallbackHolder, nsITimerCallback,
447                   nsINamed)
448 
449 NS_IMETHODIMP
450 nsGeolocationRequest::TimerCallbackHolder::Notify(nsITimer*) {
451   if (mRequest && mRequest->mLocator) {
452     RefPtr<nsGeolocationRequest> request(mRequest);
453     request->Notify();
454   }
455 
456   return NS_OK;
457 }
458 
459 ////////////////////////////////////////////////////
460 // nsGeolocationService
461 ////////////////////////////////////////////////////
462 NS_INTERFACE_MAP_BEGIN(nsGeolocationService)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports,nsIGeolocationUpdate)463   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIGeolocationUpdate)
464   NS_INTERFACE_MAP_ENTRY(nsIGeolocationUpdate)
465   NS_INTERFACE_MAP_ENTRY(nsIObserver)
466 NS_INTERFACE_MAP_END
467 
468 NS_IMPL_ADDREF(nsGeolocationService)
469 NS_IMPL_RELEASE(nsGeolocationService)
470 
471 nsresult nsGeolocationService::Init() {
472   if (!StaticPrefs::geo_enabled()) {
473     return NS_ERROR_FAILURE;
474   }
475 
476   if (XRE_IsContentProcess()) {
477     return NS_OK;
478   }
479 
480   // geolocation service can be enabled -> now register observer
481   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
482   if (!obs) {
483     return NS_ERROR_FAILURE;
484   }
485 
486   obs->AddObserver(this, "xpcom-shutdown", false);
487 
488 #ifdef MOZ_WIDGET_ANDROID
489   mProvider = new AndroidLocationProvider();
490 #endif
491 
492 #ifdef MOZ_WIDGET_GTK
493 #  ifdef MOZ_GPSD
494   if (Preferences::GetBool("geo.provider.use_gpsd", false)) {
495     mProvider = new GpsdLocationProvider();
496   }
497 #  endif
498 #endif
499 
500 #ifdef MOZ_WIDGET_COCOA
501   if (Preferences::GetBool("geo.provider.use_corelocation", true)) {
502     mProvider = new CoreLocationLocationProvider();
503   }
504 #endif
505 
506 #ifdef XP_WIN
507   if (Preferences::GetBool("geo.provider.ms-windows-location", false) &&
508       IsWin8OrLater()) {
509     mProvider = new WindowsLocationProvider();
510   }
511 #endif
512 
513   if (Preferences::GetBool("geo.provider.use_mls", false)) {
514     mProvider = do_CreateInstance("@mozilla.org/geolocation/mls-provider;1");
515   }
516 
517   // Override platform-specific providers with the default (network)
518   // provider while testing. Our tests are currently not meant to exercise
519   // the provider, and some tests rely on the network provider being used.
520   // "geo.provider.testing" is always set for all plain and browser chrome
521   // mochitests, and also for xpcshell tests.
522   if (!mProvider || Preferences::GetBool("geo.provider.testing", false)) {
523     nsCOMPtr<nsIGeolocationProvider> geoTestProvider =
524         do_GetService(NS_GEOLOCATION_PROVIDER_CONTRACTID);
525 
526     if (geoTestProvider) {
527       mProvider = geoTestProvider;
528     }
529   }
530 
531   return NS_OK;
532 }
533 
534 nsGeolocationService::~nsGeolocationService() = default;
535 
536 NS_IMETHODIMP
Observe(nsISupports * aSubject,const char * aTopic,const char16_t * aData)537 nsGeolocationService::Observe(nsISupports* aSubject, const char* aTopic,
538                               const char16_t* aData) {
539   if (!strcmp("xpcom-shutdown", aTopic)) {
540     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
541     if (obs) {
542       obs->RemoveObserver(this, "xpcom-shutdown");
543     }
544 
545     for (uint32_t i = 0; i < mGeolocators.Length(); i++) {
546       mGeolocators[i]->Shutdown();
547     }
548     StopDevice();
549 
550     return NS_OK;
551   }
552 
553   if (!strcmp("timer-callback", aTopic)) {
554     // decide if we can close down the service.
555     for (uint32_t i = 0; i < mGeolocators.Length(); i++)
556       if (mGeolocators[i]->HasActiveCallbacks()) {
557         SetDisconnectTimer();
558         return NS_OK;
559       }
560 
561     // okay to close up.
562     StopDevice();
563     Update(nullptr);
564     return NS_OK;
565   }
566 
567   return NS_ERROR_FAILURE;
568 }
569 
570 NS_IMETHODIMP
Update(nsIDOMGeoPosition * aSomewhere)571 nsGeolocationService::Update(nsIDOMGeoPosition* aSomewhere) {
572   if (aSomewhere) {
573     SetCachedPosition(aSomewhere);
574   }
575 
576   for (uint32_t i = 0; i < mGeolocators.Length(); i++) {
577     mGeolocators[i]->Update(aSomewhere);
578   }
579 
580   return NS_OK;
581 }
582 
583 NS_IMETHODIMP
NotifyError(uint16_t aErrorCode)584 nsGeolocationService::NotifyError(uint16_t aErrorCode) {
585   // nsTArray doesn't have a constructors that takes a different-type TArray.
586   nsTArray<RefPtr<Geolocation>> geolocators;
587   geolocators.AppendElements(mGeolocators);
588   for (uint32_t i = 0; i < geolocators.Length(); i++) {
589     // MOZ_KnownLive because the stack array above keeps it alive.
590     MOZ_KnownLive(geolocators[i])->NotifyError(aErrorCode);
591   }
592   return NS_OK;
593 }
594 
SetCachedPosition(nsIDOMGeoPosition * aPosition)595 void nsGeolocationService::SetCachedPosition(nsIDOMGeoPosition* aPosition) {
596   mLastPosition.position = aPosition;
597   mLastPosition.isHighAccuracy = mHigherAccuracy;
598 }
599 
GetCachedPosition()600 CachedPositionAndAccuracy nsGeolocationService::GetCachedPosition() {
601   return mLastPosition;
602 }
603 
StartDevice(nsIPrincipal * aPrincipal)604 nsresult nsGeolocationService::StartDevice(nsIPrincipal* aPrincipal) {
605   if (!StaticPrefs::geo_enabled()) {
606     return NS_ERROR_NOT_AVAILABLE;
607   }
608 
609   // We do not want to keep the geolocation devices online
610   // indefinitely.
611   // Close them down after a reasonable period of inactivivity.
612   SetDisconnectTimer();
613 
614   if (XRE_IsContentProcess()) {
615     ContentChild* cpc = ContentChild::GetSingleton();
616     cpc->SendAddGeolocationListener(HighAccuracyRequested());
617     return NS_OK;
618   }
619 
620   // Start them up!
621   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
622   if (!obs) {
623     return NS_ERROR_FAILURE;
624   }
625 
626   if (!mProvider) {
627     return NS_ERROR_FAILURE;
628   }
629 
630   nsresult rv;
631 
632   if (NS_FAILED(rv = mProvider->Startup()) ||
633       NS_FAILED(rv = mProvider->Watch(this))) {
634     NotifyError(GeolocationPositionError_Binding::POSITION_UNAVAILABLE);
635     return rv;
636   }
637 
638   obs->NotifyObservers(mProvider, "geolocation-device-events", u"starting");
639 
640   return NS_OK;
641 }
642 
SetDisconnectTimer()643 void nsGeolocationService::SetDisconnectTimer() {
644   if (!mDisconnectTimer) {
645     mDisconnectTimer = NS_NewTimer();
646   } else {
647     mDisconnectTimer->Cancel();
648   }
649 
650   mDisconnectTimer->Init(this, StaticPrefs::geo_timeout(),
651                          nsITimer::TYPE_ONE_SHOT);
652 }
653 
HighAccuracyRequested()654 bool nsGeolocationService::HighAccuracyRequested() {
655   for (uint32_t i = 0; i < mGeolocators.Length(); i++) {
656     if (mGeolocators[i]->HighAccuracyRequested()) {
657       return true;
658     }
659   }
660 
661   return false;
662 }
663 
UpdateAccuracy(bool aForceHigh)664 void nsGeolocationService::UpdateAccuracy(bool aForceHigh) {
665   bool highRequired = aForceHigh || HighAccuracyRequested();
666 
667   if (XRE_IsContentProcess()) {
668     ContentChild* cpc = ContentChild::GetSingleton();
669     if (cpc->IsAlive()) {
670       cpc->SendSetGeolocationHigherAccuracy(highRequired);
671     }
672 
673     return;
674   }
675 
676   mProvider->SetHighAccuracy(!mHigherAccuracy && highRequired);
677   mHigherAccuracy = highRequired;
678 }
679 
StopDevice()680 void nsGeolocationService::StopDevice() {
681   if (mDisconnectTimer) {
682     mDisconnectTimer->Cancel();
683     mDisconnectTimer = nullptr;
684   }
685 
686   if (XRE_IsContentProcess()) {
687     ContentChild* cpc = ContentChild::GetSingleton();
688     cpc->SendRemoveGeolocationListener();
689 
690     return;  // bail early
691   }
692 
693   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
694   if (!obs) {
695     return;
696   }
697 
698   if (!mProvider) {
699     return;
700   }
701 
702   mHigherAccuracy = false;
703 
704   mProvider->Shutdown();
705   obs->NotifyObservers(mProvider, "geolocation-device-events", u"shutdown");
706 }
707 
708 StaticRefPtr<nsGeolocationService> nsGeolocationService::sService;
709 
710 already_AddRefed<nsGeolocationService>
GetGeolocationService()711 nsGeolocationService::GetGeolocationService() {
712   RefPtr<nsGeolocationService> result;
713   if (nsGeolocationService::sService) {
714     result = nsGeolocationService::sService;
715 
716     return result.forget();
717   }
718 
719   result = new nsGeolocationService();
720   if (NS_FAILED(result->Init())) {
721     return nullptr;
722   }
723 
724   ClearOnShutdown(&nsGeolocationService::sService);
725   nsGeolocationService::sService = result;
726   return result.forget();
727 }
728 
AddLocator(Geolocation * aLocator)729 void nsGeolocationService::AddLocator(Geolocation* aLocator) {
730   mGeolocators.AppendElement(aLocator);
731 }
732 
RemoveLocator(Geolocation * aLocator)733 void nsGeolocationService::RemoveLocator(Geolocation* aLocator) {
734   mGeolocators.RemoveElement(aLocator);
735 }
736 
737 ////////////////////////////////////////////////////
738 // Geolocation
739 ////////////////////////////////////////////////////
740 
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Geolocation)741 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Geolocation)
742   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
743   NS_INTERFACE_MAP_ENTRY(nsISupports)
744   NS_INTERFACE_MAP_ENTRY(nsIGeolocationUpdate)
745 NS_INTERFACE_MAP_END
746 
747 NS_IMPL_CYCLE_COLLECTING_ADDREF(Geolocation)
748 NS_IMPL_CYCLE_COLLECTING_RELEASE(Geolocation)
749 
750 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Geolocation, mPendingCallbacks,
751                                       mWatchingCallbacks, mPendingRequests)
752 
753 Geolocation::Geolocation()
754     : mProtocolType(ProtocolType::OTHER), mLastWatchId(1) {}
755 
~Geolocation()756 Geolocation::~Geolocation() {
757   if (mService) {
758     Shutdown();
759   }
760 }
761 
762 StaticRefPtr<Geolocation> Geolocation::sNonWindowSingleton;
763 
NonWindowSingleton()764 already_AddRefed<Geolocation> Geolocation::NonWindowSingleton() {
765   if (sNonWindowSingleton) {
766     return do_AddRef(sNonWindowSingleton);
767   }
768 
769   RefPtr<Geolocation> result = new Geolocation();
770   DebugOnly<nsresult> rv = result->Init();
771   MOZ_ASSERT(NS_SUCCEEDED(rv), "How can this fail?");
772 
773   ClearOnShutdown(&sNonWindowSingleton);
774   sNonWindowSingleton = result;
775   return result.forget();
776 }
777 
Init(nsPIDOMWindowInner * aContentDom)778 nsresult Geolocation::Init(nsPIDOMWindowInner* aContentDom) {
779   // Remember the window
780   if (aContentDom) {
781     mOwner = do_GetWeakReference(aContentDom);
782     if (!mOwner) {
783       return NS_ERROR_FAILURE;
784     }
785 
786     // Grab the principal of the document
787     nsCOMPtr<Document> doc = aContentDom->GetDoc();
788     if (!doc) {
789       return NS_ERROR_FAILURE;
790     }
791 
792     mPrincipal = doc->NodePrincipal();
793     // Store the protocol to send via telemetry later.
794     if (mPrincipal->SchemeIs("http")) {
795       mProtocolType = ProtocolType::HTTP;
796     } else if (mPrincipal->SchemeIs("https")) {
797       mProtocolType = ProtocolType::HTTPS;
798     }
799   }
800 
801   // If no aContentDom was passed into us, we are being used
802   // by chrome/c++ and have no mOwner, no mPrincipal, and no need
803   // to prompt.
804   mService = nsGeolocationService::GetGeolocationService();
805   if (mService) {
806     mService->AddLocator(this);
807   }
808 
809   return NS_OK;
810 }
811 
Shutdown()812 void Geolocation::Shutdown() {
813   // Release all callbacks
814   mPendingCallbacks.Clear();
815   mWatchingCallbacks.Clear();
816 
817   if (mService) {
818     mService->RemoveLocator(this);
819     mService->UpdateAccuracy();
820   }
821 
822   mService = nullptr;
823   mPrincipal = nullptr;
824 }
825 
GetParentObject() const826 nsPIDOMWindowInner* Geolocation::GetParentObject() const {
827   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mOwner);
828   return window.get();
829 }
830 
HasActiveCallbacks()831 bool Geolocation::HasActiveCallbacks() {
832   return mPendingCallbacks.Length() || mWatchingCallbacks.Length();
833 }
834 
HighAccuracyRequested()835 bool Geolocation::HighAccuracyRequested() {
836   for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) {
837     if (mWatchingCallbacks[i]->WantsHighAccuracy()) {
838       return true;
839     }
840   }
841 
842   for (uint32_t i = 0; i < mPendingCallbacks.Length(); i++) {
843     if (mPendingCallbacks[i]->WantsHighAccuracy()) {
844       return true;
845     }
846   }
847 
848   return false;
849 }
850 
RemoveRequest(nsGeolocationRequest * aRequest)851 void Geolocation::RemoveRequest(nsGeolocationRequest* aRequest) {
852   bool requestWasKnown = (mPendingCallbacks.RemoveElement(aRequest) !=
853                           mWatchingCallbacks.RemoveElement(aRequest));
854 
855   Unused << requestWasKnown;
856 }
857 
858 NS_IMETHODIMP
Update(nsIDOMGeoPosition * aSomewhere)859 Geolocation::Update(nsIDOMGeoPosition* aSomewhere) {
860   if (!WindowOwnerStillExists()) {
861     Shutdown();
862     return NS_OK;
863   }
864 
865   // Don't update position if window is not fully active or the document is
866   // hidden. We keep the pending callaback and watchers waiting for the next
867   // update.
868   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(this->GetOwner());
869   if (window) {
870     nsCOMPtr<Document> document = window->GetDoc();
871     bool isHidden = document && document->Hidden();
872     if (isHidden || !window->IsFullyActive()) {
873       return NS_OK;
874     }
875   }
876 
877   if (aSomewhere) {
878     nsCOMPtr<nsIDOMGeoPositionCoords> coords;
879     aSomewhere->GetCoords(getter_AddRefs(coords));
880     if (coords) {
881       double accuracy = -1;
882       coords->GetAccuracy(&accuracy);
883       mozilla::Telemetry::Accumulate(
884           mozilla::Telemetry::GEOLOCATION_ACCURACY_EXPONENTIAL, accuracy);
885     }
886   }
887 
888   for (uint32_t i = mPendingCallbacks.Length(); i > 0; i--) {
889     mPendingCallbacks[i - 1]->Update(aSomewhere);
890     RemoveRequest(mPendingCallbacks[i - 1]);
891   }
892 
893   // notify everyone that is watching
894   for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) {
895     mWatchingCallbacks[i]->Update(aSomewhere);
896   }
897 
898   return NS_OK;
899 }
900 
901 NS_IMETHODIMP
NotifyError(uint16_t aErrorCode)902 Geolocation::NotifyError(uint16_t aErrorCode) {
903   if (!WindowOwnerStillExists()) {
904     Shutdown();
905     return NS_OK;
906   }
907 
908   mozilla::Telemetry::Accumulate(mozilla::Telemetry::GEOLOCATION_ERROR, true);
909 
910   for (uint32_t i = mPendingCallbacks.Length(); i > 0; i--) {
911     RefPtr<nsGeolocationRequest> request = mPendingCallbacks[i - 1];
912     request->NotifyErrorAndShutdown(aErrorCode);
913     // NotifyErrorAndShutdown() removes the request from the array
914   }
915 
916   // notify everyone that is watching
917   for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) {
918     RefPtr<nsGeolocationRequest> request = mWatchingCallbacks[i];
919     request->NotifyErrorAndShutdown(aErrorCode);
920   }
921 
922   return NS_OK;
923 }
924 
IsFullyActiveOrChrome()925 bool Geolocation::IsFullyActiveOrChrome() {
926   // For regular content window, only allow this proceed if the window is "fully
927   // active".
928   if (nsPIDOMWindowInner* window = this->GetParentObject()) {
929     return window->IsFullyActive();
930   }
931   // Calls coming from chrome code don't have window, so we can proceed.
932   return true;
933 }
934 
IsAlreadyCleared(nsGeolocationRequest * aRequest)935 bool Geolocation::IsAlreadyCleared(nsGeolocationRequest* aRequest) {
936   for (uint32_t i = 0, length = mClearedWatchIDs.Length(); i < length; ++i) {
937     if (mClearedWatchIDs[i] == aRequest->WatchId()) {
938       return true;
939     }
940   }
941 
942   return false;
943 }
944 
ShouldBlockInsecureRequests() const945 bool Geolocation::ShouldBlockInsecureRequests() const {
946   if (Preferences::GetBool(PREF_GEO_SECURITY_ALLOWINSECURE, false)) {
947     return false;
948   }
949 
950   nsCOMPtr<nsPIDOMWindowInner> win = do_QueryReferent(mOwner);
951   if (!win) {
952     return false;
953   }
954 
955   nsCOMPtr<Document> doc = win->GetDoc();
956   if (!doc) {
957     return false;
958   }
959 
960   if (!nsGlobalWindowInner::Cast(win)->IsSecureContext()) {
961     nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, "DOM"_ns, doc,
962                                     nsContentUtils::eDOM_PROPERTIES,
963                                     "GeolocationInsecureRequestIsForbidden");
964     return true;
965   }
966 
967   return false;
968 }
969 
ClearPendingRequest(nsGeolocationRequest * aRequest)970 bool Geolocation::ClearPendingRequest(nsGeolocationRequest* aRequest) {
971   if (aRequest->IsWatch() && this->IsAlreadyCleared(aRequest)) {
972     this->NotifyAllowedRequest(aRequest);
973     this->ClearWatch(aRequest->WatchId());
974     return true;
975   }
976 
977   return false;
978 }
979 
GetCurrentPosition(PositionCallback & aCallback,PositionErrorCallback * aErrorCallback,const PositionOptions & aOptions,CallerType aCallerType,ErrorResult & aRv)980 void Geolocation::GetCurrentPosition(PositionCallback& aCallback,
981                                      PositionErrorCallback* aErrorCallback,
982                                      const PositionOptions& aOptions,
983                                      CallerType aCallerType, ErrorResult& aRv) {
984   nsresult rv = GetCurrentPosition(
985       GeoPositionCallback(&aCallback), GeoPositionErrorCallback(aErrorCallback),
986       CreatePositionOptionsCopy(aOptions), aCallerType);
987 
988   if (NS_FAILED(rv)) {
989     aRv.Throw(rv);
990   }
991 }
992 
MainThreadTarget(Geolocation * geo)993 static nsIEventTarget* MainThreadTarget(Geolocation* geo) {
994   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(geo->GetOwner());
995   if (!window) {
996     return GetMainThreadEventTarget();
997   }
998   return nsGlobalWindowInner::Cast(window)->EventTargetFor(
999       mozilla::TaskCategory::Other);
1000 }
1001 
GetCurrentPosition(GeoPositionCallback callback,GeoPositionErrorCallback errorCallback,UniquePtr<PositionOptions> && options,CallerType aCallerType)1002 nsresult Geolocation::GetCurrentPosition(GeoPositionCallback callback,
1003                                          GeoPositionErrorCallback errorCallback,
1004                                          UniquePtr<PositionOptions>&& options,
1005                                          CallerType aCallerType) {
1006   if (!IsFullyActiveOrChrome()) {
1007     RefPtr<GeolocationPositionError> positionError =
1008         new GeolocationPositionError(
1009             this, GeolocationPositionError_Binding::POSITION_UNAVAILABLE);
1010     positionError->NotifyCallback(errorCallback);
1011     return NS_OK;
1012   }
1013 
1014   if (mPendingCallbacks.Length() > MAX_GEO_REQUESTS_PER_WINDOW) {
1015     return NS_ERROR_NOT_AVAILABLE;
1016   }
1017 
1018   // After this we hand over ownership of options to our nsGeolocationRequest.
1019 
1020   nsIEventTarget* target = MainThreadTarget(this);
1021   RefPtr<nsGeolocationRequest> request = new nsGeolocationRequest(
1022       this, std::move(callback), std::move(errorCallback), std::move(options),
1023       target);
1024 
1025   if (!StaticPrefs::geo_enabled() || ShouldBlockInsecureRequests() ||
1026       !request->CheckPermissionDelegate()) {
1027     request->RequestDelayedTask(target,
1028                                 nsGeolocationRequest::DelayedTaskType::Deny);
1029     return NS_OK;
1030   }
1031 
1032   if (!mOwner && aCallerType != CallerType::System) {
1033     return NS_ERROR_FAILURE;
1034   }
1035 
1036   if (mOwner) {
1037     if (!RegisterRequestWithPrompt(request)) {
1038       return NS_ERROR_NOT_AVAILABLE;
1039     }
1040 
1041     return NS_OK;
1042   }
1043 
1044   if (aCallerType != CallerType::System) {
1045     return NS_ERROR_FAILURE;
1046   }
1047 
1048   request->RequestDelayedTask(target,
1049                               nsGeolocationRequest::DelayedTaskType::Allow);
1050 
1051   return NS_OK;
1052 }
1053 
WatchPosition(PositionCallback & aCallback,PositionErrorCallback * aErrorCallback,const PositionOptions & aOptions,CallerType aCallerType,ErrorResult & aRv)1054 int32_t Geolocation::WatchPosition(PositionCallback& aCallback,
1055                                    PositionErrorCallback* aErrorCallback,
1056                                    const PositionOptions& aOptions,
1057                                    CallerType aCallerType, ErrorResult& aRv) {
1058   return WatchPosition(GeoPositionCallback(&aCallback),
1059                        GeoPositionErrorCallback(aErrorCallback),
1060                        CreatePositionOptionsCopy(aOptions), aCallerType, aRv);
1061 }
1062 
WatchPosition(nsIDOMGeoPositionCallback * aCallback,nsIDOMGeoPositionErrorCallback * aErrorCallback,UniquePtr<PositionOptions> && aOptions)1063 int32_t Geolocation::WatchPosition(
1064     nsIDOMGeoPositionCallback* aCallback,
1065     nsIDOMGeoPositionErrorCallback* aErrorCallback,
1066     UniquePtr<PositionOptions>&& aOptions) {
1067   MOZ_ASSERT(aCallback);
1068 
1069   return WatchPosition(GeoPositionCallback(aCallback),
1070                        GeoPositionErrorCallback(aErrorCallback),
1071                        std::move(aOptions), CallerType::System, IgnoreErrors());
1072 }
1073 
1074 // On errors we return 0 because that's not a valid watch id and will
1075 // get ignored in clearWatch.
WatchPosition(GeoPositionCallback aCallback,GeoPositionErrorCallback aErrorCallback,UniquePtr<PositionOptions> && aOptions,CallerType aCallerType,ErrorResult & aRv)1076 int32_t Geolocation::WatchPosition(GeoPositionCallback aCallback,
1077                                    GeoPositionErrorCallback aErrorCallback,
1078                                    UniquePtr<PositionOptions>&& aOptions,
1079                                    CallerType aCallerType, ErrorResult& aRv) {
1080   if (!IsFullyActiveOrChrome()) {
1081     RefPtr<GeolocationPositionError> positionError =
1082         new GeolocationPositionError(
1083             this, GeolocationPositionError_Binding::POSITION_UNAVAILABLE);
1084     positionError->NotifyCallback(aErrorCallback);
1085     return 0;
1086   }
1087 
1088   if (mWatchingCallbacks.Length() > MAX_GEO_REQUESTS_PER_WINDOW) {
1089     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
1090     return 0;
1091   }
1092 
1093   // The watch ID:
1094   int32_t watchId = mLastWatchId++;
1095 
1096   nsIEventTarget* target = MainThreadTarget(this);
1097   RefPtr<nsGeolocationRequest> request = new nsGeolocationRequest(
1098       this, std::move(aCallback), std::move(aErrorCallback),
1099       std::move(aOptions), target, true, watchId);
1100 
1101   if (!StaticPrefs::geo_enabled() || ShouldBlockInsecureRequests() ||
1102       !request->CheckPermissionDelegate()) {
1103     request->RequestDelayedTask(target,
1104                                 nsGeolocationRequest::DelayedTaskType::Deny);
1105     return watchId;
1106   }
1107 
1108   if (!mOwner && aCallerType != CallerType::System) {
1109     aRv.Throw(NS_ERROR_FAILURE);
1110     return 0;
1111   }
1112 
1113   if (mOwner) {
1114     if (!RegisterRequestWithPrompt(request)) {
1115       aRv.Throw(NS_ERROR_NOT_AVAILABLE);
1116       return 0;
1117     }
1118 
1119     return watchId;
1120   }
1121 
1122   if (aCallerType != CallerType::System) {
1123     aRv.Throw(NS_ERROR_FAILURE);
1124     return 0;
1125   }
1126 
1127   request->Allow(JS::UndefinedHandleValue);
1128   return watchId;
1129 }
1130 
ClearWatch(int32_t aWatchId)1131 void Geolocation::ClearWatch(int32_t aWatchId) {
1132   if (aWatchId < 1) {
1133     return;
1134   }
1135 
1136   if (!mClearedWatchIDs.Contains(aWatchId)) {
1137     mClearedWatchIDs.AppendElement(aWatchId);
1138   }
1139 
1140   for (uint32_t i = 0, length = mWatchingCallbacks.Length(); i < length; ++i) {
1141     if (mWatchingCallbacks[i]->WatchId() == aWatchId) {
1142       mWatchingCallbacks[i]->Shutdown();
1143       RemoveRequest(mWatchingCallbacks[i]);
1144       mClearedWatchIDs.RemoveElement(aWatchId);
1145       break;
1146     }
1147   }
1148 
1149   // make sure we also search through the pending requests lists for
1150   // watches to clear...
1151   for (uint32_t i = 0, length = mPendingRequests.Length(); i < length; ++i) {
1152     if (mPendingRequests[i]->IsWatch() &&
1153         (mPendingRequests[i]->WatchId() == aWatchId)) {
1154       mPendingRequests[i]->Shutdown();
1155       mPendingRequests.RemoveElementAt(i);
1156       break;
1157     }
1158   }
1159 }
1160 
WindowOwnerStillExists()1161 bool Geolocation::WindowOwnerStillExists() {
1162   // an owner was never set when Geolocation
1163   // was created, which means that this object
1164   // is being used without a window.
1165   if (mOwner == nullptr) {
1166     return true;
1167   }
1168 
1169   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mOwner);
1170 
1171   if (window) {
1172     nsPIDOMWindowOuter* outer = window->GetOuterWindow();
1173     if (!outer || outer->GetCurrentInnerWindow() != window || outer->Closed()) {
1174       return false;
1175     }
1176   }
1177 
1178   return true;
1179 }
1180 
NotifyAllowedRequest(nsGeolocationRequest * aRequest)1181 void Geolocation::NotifyAllowedRequest(nsGeolocationRequest* aRequest) {
1182   if (aRequest->IsWatch()) {
1183     mWatchingCallbacks.AppendElement(aRequest);
1184   } else {
1185     mPendingCallbacks.AppendElement(aRequest);
1186   }
1187 }
1188 
RegisterRequestWithPrompt(nsGeolocationRequest * request)1189 bool Geolocation::RegisterRequestWithPrompt(nsGeolocationRequest* request) {
1190   nsIEventTarget* target = MainThreadTarget(this);
1191   ContentPermissionRequestBase::PromptResult pr = request->CheckPromptPrefs();
1192   if (pr == ContentPermissionRequestBase::PromptResult::Granted) {
1193     request->RequestDelayedTask(target,
1194                                 nsGeolocationRequest::DelayedTaskType::Allow);
1195     return true;
1196   }
1197   if (pr == ContentPermissionRequestBase::PromptResult::Denied) {
1198     request->RequestDelayedTask(target,
1199                                 nsGeolocationRequest::DelayedTaskType::Deny);
1200     return true;
1201   }
1202 
1203   request->RequestDelayedTask(target,
1204                               nsGeolocationRequest::DelayedTaskType::Request);
1205   return true;
1206 }
1207 
WrapObject(JSContext * aCtx,JS::Handle<JSObject * > aGivenProto)1208 JSObject* Geolocation::WrapObject(JSContext* aCtx,
1209                                   JS::Handle<JSObject*> aGivenProto) {
1210   return Geolocation_Binding::Wrap(aCtx, this, aGivenProto);
1211 }
1212