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