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