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