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