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