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