dom/src/geolocation/nsGeolocation.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:0aa310f4401a
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 }

mercurial