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