Bug 594261 - IPC part - Factor out geolocation prompt into something that can be...
[mozilla-central.git] / dom / src / geolocation / nsGeolocation.cpp
blob68dc2dc64cc1e031b9e04d74eabef81d25a4ffa6
1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
14 * The Original Code is Geolocation.
16 * The Initial Developer of the Original Code is Mozilla Foundation
17 * Portions created by the Initial Developer are Copyright (C) 2008
18 * the Initial Developer. All Rights Reserved.
20 * Contributor(s):
21 * Doug Turner <dougt@meer.net> (Original Author)
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
37 #ifdef MOZ_IPC
38 #include "nsGeolocationOOP.h"
39 #include "nsXULAppAPI.h"
41 #include "mozilla/dom/PBrowserChild.h"
42 #include "mozilla/dom/PBrowserParent.h"
43 #include "mozilla/dom/ContentChild.h"
44 #include "nsNetUtil.h"
46 #include "nsFrameManager.h"
47 #include "nsFrameLoader.h"
48 #include "nsIFrameLoader.h"
50 #include "nsIDocShellTreeOwner.h"
51 #include "nsIDocShellTreeItem.h"
52 #include "nsIWebProgressListener2.h"
54 #include "nsDOMEventTargetHelper.h"
55 #include "TabChild.h"
56 #endif
58 #include "nsGeolocation.h"
59 #include "nsAutoPtr.h"
60 #include "nsCOMPtr.h"
61 #include "nsIDOMWindow.h"
62 #include "nsDOMClassInfo.h"
63 #include "nsComponentManagerUtils.h"
64 #include "nsICategoryManager.h"
65 #include "nsISupportsPrimitives.h"
66 #include "nsServiceManagerUtils.h"
67 #include "nsContentUtils.h"
68 #include "nsIURI.h"
69 #include "nsIPermissionManager.h"
70 #include "nsIObserverService.h"
71 #include "nsIPrefService.h"
72 #include "nsIPrefBranch2.h"
73 #include "nsIJSContextStack.h"
74 #include "nsThreadUtils.h"
75 #include "mozilla/Services.h"
76 #include "mozilla/unused.h"
78 #include <math.h>
80 #ifdef WINCE_WINDOWS_MOBILE
81 #include "WinMobileLocationProvider.h"
82 #endif
84 #ifdef MOZ_MAEMO_LIBLOCATION
85 #include "MaemoLocationProvider.h"
86 #endif
88 #ifdef ANDROID
89 #include "AndroidLocationProvider.h"
90 #endif
92 #include "nsIDOMDocument.h"
93 #include "nsIDocument.h"
95 // Some limit to the number of get or watch geolocation requests
96 // that a window can make.
97 #define MAX_GEO_REQUESTS_PER_WINDOW 1500
99 using mozilla::unused; // <snicker>
100 using namespace mozilla::dom;
102 class RequestPromptEvent : public nsRunnable
104 public:
105 RequestPromptEvent(nsGeolocationRequest* request)
106 : mRequest(request)
110 NS_IMETHOD Run() {
111 nsCOMPtr<nsIContentPermissionPrompt> prompt = do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
112 if (prompt) {
113 prompt->Prompt(mRequest);
115 return NS_OK;
118 private:
119 nsRefPtr<nsGeolocationRequest> mRequest;
122 class RequestAllowEvent : public nsRunnable
124 public:
125 RequestAllowEvent(int allow, nsGeolocationRequest* request)
126 : mAllow(allow),
127 mRequest(request)
131 NS_IMETHOD Run() {
132 if (mAllow)
133 mRequest->Allow();
134 else
135 mRequest->Cancel();
136 return NS_OK;
139 private:
140 PRBool mAllow;
141 nsRefPtr<nsGeolocationRequest> mRequest;
144 class RequestSendLocationEvent : public nsRunnable
146 public:
147 // a bit funky. if locator is passed, that means this
148 // event should remove the request from it. If we ever
149 // have to do more, then we can change this around.
150 RequestSendLocationEvent(nsIDOMGeoPosition* aPosition,
151 nsGeolocationRequest* aRequest,
152 nsGeolocation* aLocator = nsnull)
153 : mPosition(aPosition),
154 mRequest(aRequest),
155 mLocator(aLocator)
159 NS_IMETHOD Run() {
160 mRequest->SendLocation(mPosition);
161 if (mLocator)
162 mLocator->RemoveRequest(mRequest);
163 return NS_OK;
166 private:
167 nsCOMPtr<nsIDOMGeoPosition> mPosition;
168 nsRefPtr<nsGeolocationRequest> mRequest;
170 nsRefPtr<nsGeolocation> mLocator;
173 ////////////////////////////////////////////////////
174 // nsDOMGeoPositionError
175 ////////////////////////////////////////////////////
177 class nsDOMGeoPositionError : public nsIDOMGeoPositionError
179 public:
180 NS_DECL_ISUPPORTS
181 NS_DECL_NSIDOMGEOPOSITIONERROR
183 nsDOMGeoPositionError(PRInt16 aCode);
184 void NotifyCallback(nsIDOMGeoPositionErrorCallback* callback);
186 private:
187 ~nsDOMGeoPositionError();
188 PRInt16 mCode;
191 DOMCI_DATA(GeoPositionError, nsDOMGeoPositionError)
193 NS_INTERFACE_MAP_BEGIN(nsDOMGeoPositionError)
194 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMGeoPositionError)
195 NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPositionError)
196 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(GeoPositionError)
197 NS_INTERFACE_MAP_END
199 NS_IMPL_THREADSAFE_ADDREF(nsDOMGeoPositionError)
200 NS_IMPL_THREADSAFE_RELEASE(nsDOMGeoPositionError)
202 nsDOMGeoPositionError::nsDOMGeoPositionError(PRInt16 aCode)
203 : mCode(aCode)
207 nsDOMGeoPositionError::~nsDOMGeoPositionError(){}
210 NS_IMETHODIMP
211 nsDOMGeoPositionError::GetCode(PRInt16 *aCode)
213 NS_ENSURE_ARG_POINTER(aCode);
214 *aCode = mCode;
215 return NS_OK;
218 void
219 nsDOMGeoPositionError::NotifyCallback(nsIDOMGeoPositionErrorCallback* aCallback)
221 if (!aCallback)
222 return;
224 // Ensure that the proper context is on the stack (bug 452762)
225 nsCOMPtr<nsIJSContextStack> stack(do_GetService("@mozilla.org/js/xpc/ContextStack;1"));
226 if (!stack || NS_FAILED(stack->Push(nsnull)))
227 return;
229 aCallback->HandleEvent(this);
231 // remove the stack
232 JSContext* cx;
233 stack->Pop(&cx);
235 ////////////////////////////////////////////////////
236 // nsGeolocationRequest
237 ////////////////////////////////////////////////////
239 nsGeolocationRequest::nsGeolocationRequest(nsGeolocation* aLocator,
240 nsIDOMGeoPositionCallback* aCallback,
241 nsIDOMGeoPositionErrorCallback* aErrorCallback,
242 nsIDOMGeoPositionOptions* aOptions)
243 : mAllowed(PR_FALSE),
244 mCleared(PR_FALSE),
245 mCallback(aCallback),
246 mErrorCallback(aErrorCallback),
247 mOptions(aOptions),
248 mLocator(aLocator)
252 nsGeolocationRequest::~nsGeolocationRequest()
256 nsresult
257 nsGeolocationRequest::Init()
259 // This method is called before the user has given permission for this request.
261 // check to see if we have a geolocation provider, if not, notify an error and bail.
262 nsRefPtr<nsGeolocationService> geoService = nsGeolocationService::GetInstance();
263 if (!geoService->HasGeolocationProvider()) {
264 NotifyError(nsIDOMGeoPositionError::POSITION_UNAVAILABLE);
265 return NS_ERROR_FAILURE;
268 return NS_OK;
271 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGeolocationRequest)
272 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
273 NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
274 NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
275 NS_INTERFACE_MAP_END
277 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGeolocationRequest)
278 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGeolocationRequest)
280 NS_IMPL_CYCLE_COLLECTION_4(nsGeolocationRequest, mCallback, mErrorCallback, mOptions, mLocator)
283 void
284 nsGeolocationRequest::NotifyError(PRInt16 errorCode)
286 nsRefPtr<nsDOMGeoPositionError> positionError = new nsDOMGeoPositionError(errorCode);
287 if (!positionError)
288 return;
290 positionError->NotifyCallback(mErrorCallback);
294 NS_IMETHODIMP
295 nsGeolocationRequest::Notify(nsITimer* aTimer)
297 // If we haven't gotten an answer from the geolocation
298 // provider yet, cancel the request. Same logic as
299 // ::Cancel, just a different error
301 NotifyError(nsIDOMGeoPositionError::TIMEOUT);
302 // remove ourselves from the locator's callback lists.
303 mLocator->RemoveRequest(this);
305 mTimeoutTimer = nsnull;
306 return NS_OK;
309 NS_IMETHODIMP
310 nsGeolocationRequest::GetUri(nsIURI * *aRequestingURI)
312 NS_ENSURE_ARG_POINTER(aRequestingURI);
314 nsCOMPtr<nsIURI> uri = mLocator->GetURI();
315 uri.forget(aRequestingURI);
317 return NS_OK;
320 NS_IMETHODIMP
321 nsGeolocationRequest::GetType(nsACString & aType)
323 aType = "geolocation";
324 return NS_OK;
327 NS_IMETHODIMP
328 nsGeolocationRequest::GetWindow(nsIDOMWindow * *aRequestingWindow)
330 NS_ENSURE_ARG_POINTER(aRequestingWindow);
332 nsCOMPtr<nsIDOMWindow> window = do_QueryReferent(mLocator->GetOwner());
333 window.forget(aRequestingWindow);
335 return NS_OK;
338 NS_IMETHODIMP
339 nsGeolocationRequest::GetElement(nsIDOMElement * *aRequestingElement)
341 NS_ENSURE_ARG_POINTER(aRequestingElement);
342 *aRequestingElement = nsnull;
343 return NS_OK;
346 NS_IMETHODIMP
347 nsGeolocationRequest::Cancel()
349 NotifyError(nsIDOMGeoPositionError::PERMISSION_DENIED);
351 // remove ourselves from the locators callback lists.
352 mLocator->RemoveRequest(this);
353 return NS_OK;
356 NS_IMETHODIMP
357 nsGeolocationRequest::Allow()
359 nsRefPtr<nsGeolocationService> geoService = nsGeolocationService::GetInstance();
361 // Kick off the geo device, if it isn't already running
362 nsresult rv = geoService->StartDevice();
364 if (NS_FAILED(rv)) {
365 // Location provider error
366 NotifyError(nsIDOMGeoPositionError::POSITION_UNAVAILABLE);
367 return NS_OK;
370 nsCOMPtr<nsIDOMGeoPosition> lastPosition = geoService->GetCachedPosition();
371 DOMTimeStamp cachedPositionTime;
372 if (lastPosition)
373 lastPosition->GetTimestamp(&cachedPositionTime);
375 // check to see if we can use a cached value
377 // either:
378 // a) the user has specified a maximumAge which allows us to return a cached value,
379 // -or-
380 // b) the cached position time is some reasonable value to return to the user (<30s)
382 PRUint32 maximumAge = 30 * PR_MSEC_PER_SEC;
383 if (mOptions) {
384 PRInt32 tempAge;
385 nsresult rv = mOptions->GetMaximumAge(&tempAge);
386 if (NS_SUCCEEDED(rv)) {
387 if (tempAge >= 0)
388 maximumAge = tempAge;
392 if (lastPosition && maximumAge > 0 &&
393 ( PRTime(PR_Now() / PR_USEC_PER_MSEC) - maximumAge <=
394 PRTime(cachedPositionTime) )) {
395 // okay, we can return a cached position
396 mAllowed = PR_TRUE;
398 nsCOMPtr<nsIRunnable> ev = new RequestSendLocationEvent(lastPosition, this, mLocator);
399 NS_DispatchToMainThread(ev);
402 SetTimeoutTimer();
404 mAllowed = PR_TRUE;
405 return NS_OK;
408 void
409 nsGeolocationRequest::SetTimeoutTimer()
411 if (mTimeoutTimer) {
412 mTimeoutTimer->Cancel();
413 mTimeoutTimer = nsnull;
415 PRInt32 timeout;
416 if (mOptions && NS_SUCCEEDED(mOptions->GetTimeout(&timeout)) && timeout > 0) {
418 if (timeout < 10)
419 timeout = 10;
421 mTimeoutTimer = do_CreateInstance("@mozilla.org/timer;1");
422 mTimeoutTimer->InitWithCallback(this, timeout, nsITimer::TYPE_ONE_SHOT);
426 void
427 nsGeolocationRequest::MarkCleared()
429 mCleared = PR_TRUE;
432 void
433 nsGeolocationRequest::SendLocation(nsIDOMGeoPosition* aPosition)
435 if (mCleared || !mAllowed)
436 return;
438 if (mTimeoutTimer) {
439 mTimeoutTimer->Cancel();
440 mTimeoutTimer = nsnull;
443 // we should not pass null back to the DOM.
444 if (!aPosition) {
445 NotifyError(nsIDOMGeoPositionError::POSITION_UNAVAILABLE);
446 return;
449 // Ensure that the proper context is on the stack (bug 452762)
450 nsCOMPtr<nsIJSContextStack> stack(do_GetService("@mozilla.org/js/xpc/ContextStack;1"));
451 if (!stack || NS_FAILED(stack->Push(nsnull)))
452 return; // silently fail
454 mCallback->HandleEvent(aPosition);
456 // remove the stack
457 JSContext* cx;
458 stack->Pop(&cx);
460 SetTimeoutTimer();
463 void
464 nsGeolocationRequest::Shutdown()
466 mCleared = PR_TRUE;
467 mCallback = nsnull;
468 mErrorCallback = nsnull;
471 #ifdef MOZ_IPC
472 bool nsGeolocationRequest::Recv__delete__(const bool& allow)
474 if (allow)
475 (void) Allow();
476 else
477 (void) Cancel();
478 return true;
480 #endif
481 ////////////////////////////////////////////////////
482 // nsGeolocationService
483 ////////////////////////////////////////////////////
484 NS_INTERFACE_MAP_BEGIN(nsGeolocationService)
485 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIGeolocationUpdate)
486 NS_INTERFACE_MAP_ENTRY(nsIGeolocationUpdate)
487 NS_INTERFACE_MAP_ENTRY(nsIObserver)
488 NS_INTERFACE_MAP_END
490 NS_IMPL_THREADSAFE_ADDREF(nsGeolocationService)
491 NS_IMPL_THREADSAFE_RELEASE(nsGeolocationService)
494 static PRBool sGeoEnabled = PR_TRUE;
495 static PRBool sGeoIgnoreLocationFilter = PR_FALSE;
497 static int
498 GeoEnabledChangedCallback(const char *aPrefName, void *aClosure)
500 sGeoEnabled = nsContentUtils::GetBoolPref("geo.enabled", PR_TRUE);
501 return 0;
504 static int
505 GeoIgnoreLocationFilterChangedCallback(const char *aPrefName, void *aClosure)
507 sGeoIgnoreLocationFilter = nsContentUtils::GetBoolPref("geo.ignore.location_filter",
508 PR_TRUE);
509 return 0;
513 nsresult nsGeolocationService::Init()
515 mTimeout = nsContentUtils::GetIntPref("geo.timeout", 6000);
517 nsContentUtils::RegisterPrefCallback("geo.ignore.location_filter",
518 GeoIgnoreLocationFilterChangedCallback,
519 nsnull);
521 GeoIgnoreLocationFilterChangedCallback("geo.ignore.location_filter", nsnull);
524 nsContentUtils::RegisterPrefCallback("geo.enabled",
525 GeoEnabledChangedCallback,
526 nsnull);
528 GeoEnabledChangedCallback("geo.enabled", nsnull);
530 if (!sGeoEnabled)
531 return NS_ERROR_FAILURE;
533 nsCOMPtr<nsIGeolocationProvider> provider = do_GetService(NS_GEOLOCATION_PROVIDER_CONTRACTID);
534 if (provider)
535 mProviders.AppendObject(provider);
537 // look up any providers that were registered via the category manager
538 nsCOMPtr<nsICategoryManager> catMan(do_GetService("@mozilla.org/categorymanager;1"));
539 if (!catMan)
540 return NS_ERROR_FAILURE;
542 // geolocation service can be enabled -> now register observer
543 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
544 if (!obs)
545 return NS_ERROR_FAILURE;
547 obs->AddObserver(this, "quit-application", false);
549 nsCOMPtr<nsISimpleEnumerator> geoproviders;
550 catMan->EnumerateCategory("geolocation-provider", getter_AddRefs(geoproviders));
551 if (geoproviders) {
553 PRBool hasMore;
554 while (NS_SUCCEEDED(geoproviders->HasMoreElements(&hasMore)) && hasMore) {
555 nsCOMPtr<nsISupports> elem;
556 geoproviders->GetNext(getter_AddRefs(elem));
558 nsCOMPtr<nsISupportsCString> elemString = do_QueryInterface(elem);
560 nsCAutoString name;
561 elemString->GetData(name);
563 nsXPIDLCString spec;
564 catMan->GetCategoryEntry("geolocation-provider", name.get(), getter_Copies(spec));
566 provider = do_GetService(spec);
567 if (provider)
568 mProviders.AppendObject(provider);
572 // we should move these providers outside of this file! dft
574 #ifdef WINCE_WINDOWS_MOBILE
575 provider = new WinMobileLocationProvider();
576 if (provider)
577 mProviders.AppendObject(provider);
578 #endif
580 #ifdef MOZ_MAEMO_LIBLOCATION
581 provider = new MaemoLocationProvider();
582 if (provider)
583 mProviders.AppendObject(provider);
584 #endif
586 #ifdef ANDROID
587 provider = new AndroidLocationProvider();
588 if (provider)
589 mProviders.AppendObject(provider);
590 #endif
591 return NS_OK;
594 nsGeolocationService::~nsGeolocationService()
598 NS_IMETHODIMP
599 nsGeolocationService::Observe(nsISupports* aSubject,
600 const char* aTopic,
601 const PRUnichar* aData)
603 if (!strcmp("quit-application", aTopic))
605 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
606 if (obs) {
607 obs->RemoveObserver(this, "quit-application");
610 for (PRUint32 i = 0; i< mGeolocators.Length(); i++)
611 mGeolocators[i]->Shutdown();
613 StopDevice();
615 return NS_OK;
618 if (!strcmp("timer-callback", aTopic))
620 // decide if we can close down the service.
621 for (PRUint32 i = 0; i< mGeolocators.Length(); i++)
622 if (mGeolocators[i]->HasActiveCallbacks())
624 SetDisconnectTimer();
625 return NS_OK;
628 // okay to close up.
629 StopDevice();
630 Update(nsnull);
631 return NS_OK;
634 return NS_ERROR_FAILURE;
637 NS_IMETHODIMP
638 nsGeolocationService::Update(nsIDOMGeoPosition *aSomewhere)
640 // here we have to determine this aSomewhere is a "better"
641 // position than any previously recv'ed.
643 if (!IsBetterPosition(aSomewhere))
644 return NS_OK;
646 SetCachedPosition(aSomewhere);
648 for (PRUint32 i = 0; i< mGeolocators.Length(); i++)
649 mGeolocators[i]->Update(aSomewhere);
650 return NS_OK;
653 PRBool
654 nsGeolocationService::IsBetterPosition(nsIDOMGeoPosition *aSomewhere)
656 if (!aSomewhere)
657 return PR_FALSE;
659 nsRefPtr<nsGeolocationService> geoService = nsGeolocationService::GetInstance();
660 if (!geoService)
661 return PR_FALSE;
663 nsCOMPtr<nsIDOMGeoPosition> lastPosition = geoService->GetCachedPosition();
664 if (!lastPosition)
665 return PR_TRUE;
667 nsresult rv;
668 DOMTimeStamp oldTime;
669 rv = lastPosition->GetTimestamp(&oldTime);
670 NS_ENSURE_SUCCESS(rv, PR_FALSE);
672 nsCOMPtr<nsIDOMGeoPositionCoords> coords;
673 lastPosition->GetCoords(getter_AddRefs(coords));
674 if (!coords)
675 return PR_FALSE;
677 double oldAccuracy;
678 rv = coords->GetAccuracy(&oldAccuracy);
679 NS_ENSURE_SUCCESS(rv, PR_FALSE);
681 double oldLat, oldLon;
682 rv = coords->GetLongitude(&oldLon);
683 NS_ENSURE_SUCCESS(rv, PR_FALSE);
685 rv = coords->GetLatitude(&oldLat);
686 NS_ENSURE_SUCCESS(rv, PR_FALSE);
689 DOMTimeStamp newTime;
690 rv = aSomewhere->GetTimestamp(&newTime);
691 NS_ENSURE_SUCCESS(rv, PR_FALSE);
693 aSomewhere->GetCoords(getter_AddRefs(coords));
694 if (!coords)
695 return PR_FALSE;
697 double newAccuracy;
698 rv = coords->GetAccuracy(&newAccuracy);
699 NS_ENSURE_SUCCESS(rv, PR_FALSE);
701 double newLat, newLon;
702 rv = coords->GetLongitude(&newLon);
703 NS_ENSURE_SUCCESS(rv, PR_FALSE);
705 rv = coords->GetLatitude(&newLat);
706 NS_ENSURE_SUCCESS(rv, PR_FALSE);
708 // check to see if there has been a large movement
709 // Use spherical law of cosines to calculate difference
710 // Not quite as correct as the Haversine but simpler and cheaper
711 double radsInDeg = 3.14159265 / 180.0;
713 double rNewLat = newLat * radsInDeg;
714 double rNewLon = newLon * radsInDeg;
715 double rOldLat = oldLat * radsInDeg;
716 double rOldLon = oldLon * radsInDeg;
718 // WGS84 equatorial radius of earth = 6378137m
719 double delta = acos( (sin(rNewLat) * sin(rOldLat)) + (cos(rNewLat) * cos(rOldLat) * cos(rOldLon - rNewLon)) ) * 6378137;
721 // The threshold is when the distance between the two positions exceeds the
722 // worse (larger value) of the two accuracies.
723 double max_accuracy = NS_MAX(oldAccuracy, newAccuracy);
724 if (delta > max_accuracy)
725 return PR_TRUE;
727 // check to see if the aSomewhere position is more accurate
728 if (oldAccuracy >= newAccuracy)
729 return PR_TRUE;
731 return PR_FALSE;
734 void
735 nsGeolocationService::SetCachedPosition(nsIDOMGeoPosition* aPosition)
737 mLastPosition = aPosition;
740 nsIDOMGeoPosition*
741 nsGeolocationService::GetCachedPosition()
743 return mLastPosition;
746 PRBool
747 nsGeolocationService::HasGeolocationProvider()
749 return mProviders.Count() > 0;
752 nsresult
753 nsGeolocationService::StartDevice()
755 if (!sGeoEnabled)
756 return NS_ERROR_NOT_AVAILABLE;
758 if (!HasGeolocationProvider())
759 return NS_ERROR_NOT_AVAILABLE;
761 // if we have one, start it up.
763 // Start them up!
764 nsresult rv = NS_ERROR_NOT_AVAILABLE;
765 for (PRUint32 i = mProviders.Count() - 1; i != PRUint32(-1); --i) {
766 // If any provder gets started without error, go ahead
767 // and proceed without error
768 nsresult temp = mProviders[i]->Startup();
769 if (NS_SUCCEEDED(temp)) {
770 rv = NS_OK;
772 mProviders[i]->Watch(this);
776 if (NS_FAILED(rv))
777 return NS_ERROR_NOT_AVAILABLE;
779 // we do not want to keep the geolocation devices online
780 // indefinitely. Close them down after a reasonable period of
781 // inactivivity
782 SetDisconnectTimer();
784 return NS_OK;
787 void
788 nsGeolocationService::SetDisconnectTimer()
790 if (!mDisconnectTimer)
791 mDisconnectTimer = do_CreateInstance("@mozilla.org/timer;1");
792 else
793 mDisconnectTimer->Cancel();
795 mDisconnectTimer->Init(this,
796 mTimeout,
797 nsITimer::TYPE_ONE_SHOT);
800 void
801 nsGeolocationService::StopDevice()
803 for (PRUint32 i = mProviders.Count() - 1; i != PRUint32(-1); --i) {
804 mProviders[i]->Shutdown();
807 if(mDisconnectTimer) {
808 mDisconnectTimer->Cancel();
809 mDisconnectTimer = nsnull;
813 nsGeolocationService* nsGeolocationService::gService = nsnull;
815 nsGeolocationService*
816 nsGeolocationService::GetInstance()
818 if (!nsGeolocationService::gService) {
819 nsGeolocationService::gService = new nsGeolocationService();
820 NS_ASSERTION(nsGeolocationService::gService, "null nsGeolocationService.");
822 if (nsGeolocationService::gService) {
823 if (NS_FAILED(nsGeolocationService::gService->Init())) {
824 delete nsGeolocationService::gService;
825 nsGeolocationService::gService = nsnull;
829 return nsGeolocationService::gService;
832 nsGeolocationService*
833 nsGeolocationService::GetGeolocationService()
835 nsGeolocationService* inst = nsGeolocationService::GetInstance();
836 NS_IF_ADDREF(inst);
837 return inst;
840 void
841 nsGeolocationService::AddLocator(nsGeolocation* aLocator)
843 mGeolocators.AppendElement(aLocator);
846 void
847 nsGeolocationService::RemoveLocator(nsGeolocation* aLocator)
849 mGeolocators.RemoveElement(aLocator);
852 ////////////////////////////////////////////////////
853 // nsGeolocation
854 ////////////////////////////////////////////////////
856 DOMCI_DATA(GeoGeolocation, nsGeolocation)
858 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGeolocation)
859 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMGeoGeolocation)
860 NS_INTERFACE_MAP_ENTRY(nsIDOMGeoGeolocation)
861 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(GeoGeolocation)
862 NS_INTERFACE_MAP_END
864 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGeolocation)
865 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGeolocation)
866 NS_IMPL_CYCLE_COLLECTION_CLASS(nsGeolocation)
868 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGeolocation)
869 tmp->mPendingCallbacks.Clear();
870 tmp->mWatchingCallbacks.Clear();
871 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
873 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsGeolocation)
874 PRUint32 i;
875 for (i = 0; i < tmp->mPendingCallbacks.Length(); ++i)
876 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mPendingCallbacks[i], nsIContentPermissionRequest)
878 for (i = 0; i < tmp->mWatchingCallbacks.Length(); ++i)
879 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mWatchingCallbacks[i], nsIContentPermissionRequest)
880 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
882 nsGeolocation::nsGeolocation()
886 nsGeolocation::~nsGeolocation()
888 if (mService)
889 Shutdown();
892 nsresult
893 nsGeolocation::Init(nsIDOMWindow* aContentDom)
895 // Remember the window
896 if (aContentDom) {
897 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aContentDom);
898 if (!window)
899 return NS_ERROR_FAILURE;
901 mOwner = do_GetWeakReference(window->GetCurrentInnerWindow());
902 if (!mOwner)
903 return NS_ERROR_FAILURE;
905 // Grab the uri of the document
906 nsCOMPtr<nsIDOMDocument> domdoc;
907 aContentDom->GetDocument(getter_AddRefs(domdoc));
908 nsCOMPtr<nsIDocument> doc = do_QueryInterface(domdoc);
909 if (!doc)
910 return NS_ERROR_FAILURE;
912 doc->NodePrincipal()->GetURI(getter_AddRefs(mURI));
914 if (!mURI)
915 return NS_ERROR_FAILURE;
918 // If no aContentDom was passed into us, we are being used
919 // by chrome/c++ and have no mOwner, no mURI, and no need
920 // to prompt.
921 mService = nsGeolocationService::GetInstance();
922 if (mService)
923 mService->AddLocator(this);
925 return NS_OK;
928 void
929 nsGeolocation::Shutdown()
931 // Shutdown and release all callbacks
932 for (PRUint32 i = 0; i< mPendingCallbacks.Length(); i++)
933 mPendingCallbacks[i]->Shutdown();
934 mPendingCallbacks.Clear();
936 for (PRUint32 i = 0; i< mWatchingCallbacks.Length(); i++)
937 mWatchingCallbacks[i]->Shutdown();
938 mWatchingCallbacks.Clear();
940 if (mService)
941 mService->RemoveLocator(this);
943 mService = nsnull;
944 mURI = nsnull;
947 PRBool
948 nsGeolocation::HasActiveCallbacks()
950 return mWatchingCallbacks.Length() != 0;
953 void
954 nsGeolocation::RemoveRequest(nsGeolocationRequest* aRequest)
956 mPendingCallbacks.RemoveElement(aRequest);
958 // if it is in the mWatchingCallbacks, we can't do much
959 // since we passed back the position in the array to who
960 // ever called WatchPosition() and we do not want to mess
961 // around with the ordering of the array. Instead, just
962 // mark the request as "cleared".
964 aRequest->MarkCleared();
967 void
968 nsGeolocation::Update(nsIDOMGeoPosition *aSomewhere)
970 if (!WindowOwnerStillExists())
971 return Shutdown();
973 for (PRUint32 i = 0; i< mPendingCallbacks.Length(); i++) {
974 nsCOMPtr<nsIRunnable> ev = new RequestSendLocationEvent(aSomewhere,
975 mPendingCallbacks[i]);
976 NS_DispatchToMainThread(ev);
978 mPendingCallbacks.Clear();
980 // notify everyone that is watching
981 for (PRUint32 i = 0; i< mWatchingCallbacks.Length(); i++) {
982 nsCOMPtr<nsIRunnable> ev = new RequestSendLocationEvent(aSomewhere, mWatchingCallbacks[i]);
983 NS_DispatchToMainThread(ev);
987 NS_IMETHODIMP
988 nsGeolocation::GetCurrentPosition(nsIDOMGeoPositionCallback *callback,
989 nsIDOMGeoPositionErrorCallback *errorCallback,
990 nsIDOMGeoPositionOptions *options)
992 NS_ENSURE_ARG_POINTER(callback);
994 if (!sGeoEnabled)
995 return NS_ERROR_NOT_AVAILABLE;
997 if (mPendingCallbacks.Length() > MAX_GEO_REQUESTS_PER_WINDOW)
998 return NS_ERROR_NOT_AVAILABLE;
1000 nsRefPtr<nsGeolocationRequest> request = new nsGeolocationRequest(this, callback, errorCallback, options);
1001 if (!request)
1002 return NS_ERROR_OUT_OF_MEMORY;
1004 if (NS_FAILED(request->Init()))
1005 return NS_ERROR_FAILURE; // this as OKAY. not sure why we wouldn't throw. xxx dft
1007 if (mOwner) {
1008 RegisterRequestWithPrompt(request);
1009 mPendingCallbacks.AppendElement(request);
1010 return NS_OK;
1013 if (!nsContentUtils::IsCallerChrome())
1014 return NS_ERROR_FAILURE;
1016 mPendingCallbacks.AppendElement(request);
1018 nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(true, request);
1019 NS_DispatchToMainThread(ev);
1021 return NS_OK;
1024 NS_IMETHODIMP
1025 nsGeolocation::WatchPosition(nsIDOMGeoPositionCallback *callback,
1026 nsIDOMGeoPositionErrorCallback *errorCallback,
1027 nsIDOMGeoPositionOptions *options,
1028 PRInt32 *_retval NS_OUTPARAM)
1031 NS_ENSURE_ARG_POINTER(callback);
1033 if (!sGeoEnabled)
1034 return NS_ERROR_NOT_AVAILABLE;
1036 if (mPendingCallbacks.Length() > MAX_GEO_REQUESTS_PER_WINDOW)
1037 return NS_ERROR_NOT_AVAILABLE;
1039 nsRefPtr<nsGeolocationRequest> request = new nsGeolocationRequest(this, callback, errorCallback, options);
1040 if (!request)
1041 return NS_ERROR_OUT_OF_MEMORY;
1043 if (NS_FAILED(request->Init()))
1044 return NS_ERROR_FAILURE; // this as OKAY. not sure why we wouldn't throw. xxx dft
1046 if (mOwner) {
1047 RegisterRequestWithPrompt(request);
1049 // need to hand back an index/reference.
1050 mWatchingCallbacks.AppendElement(request);
1051 *_retval = mWatchingCallbacks.Length() - 1;
1053 return NS_OK;
1056 if (!nsContentUtils::IsCallerChrome())
1057 return NS_ERROR_FAILURE;
1059 request->Allow();
1061 // need to hand back an index/reference.
1062 mWatchingCallbacks.AppendElement(request);
1063 *_retval = mWatchingCallbacks.Length() - 1;
1065 return NS_OK;
1068 NS_IMETHODIMP
1069 nsGeolocation::ClearWatch(PRInt32 aWatchId)
1071 PRUint32 count = mWatchingCallbacks.Length();
1072 if (aWatchId < 0 || count == 0 || PRUint32(aWatchId) > count)
1073 return NS_OK;
1075 mWatchingCallbacks[aWatchId]->MarkCleared();
1076 return NS_OK;
1079 PRBool
1080 nsGeolocation::WindowOwnerStillExists()
1082 // an owner was never set when nsGeolocation
1083 // was created, which means that this object
1084 // is being used without a window.
1085 if (mOwner == nsnull)
1086 return PR_TRUE;
1088 nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mOwner);
1090 if (window)
1092 PRBool closed = PR_FALSE;
1093 window->GetClosed(&closed);
1094 if (closed)
1095 return PR_FALSE;
1097 nsPIDOMWindow* outer = window->GetOuterWindow();
1098 if (!outer || outer->GetCurrentInnerWindow() != window)
1099 return PR_FALSE;
1102 return PR_TRUE;
1105 void
1106 nsGeolocation::RegisterRequestWithPrompt(nsGeolocationRequest* request)
1108 #ifdef MOZ_IPC
1109 if (XRE_GetProcessType() == GeckoProcessType_Content) {
1110 nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mOwner);
1111 if (!window)
1112 return;
1114 // because owner implements nsITabChild, we can assume that it is
1115 // the one and only TabChild.
1116 TabChild* child = GetTabChildFrom(window->GetDocShell());
1118 // Retain a reference so the object isn't deleted without IPDL's knowledge.
1119 // Corresponding release occurs in DeallocPContentPermissionRequest.
1120 request->AddRef();
1122 nsCString type = NS_LITERAL_CSTRING("geolocation");
1123 child->SendPContentPermissionRequestConstructor(request, type, IPC::URI(mURI));
1125 request->Sendprompt();
1126 return;
1128 #endif
1130 if (nsContentUtils::GetBoolPref("geo.prompt.testing", PR_FALSE))
1132 nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(nsContentUtils::GetBoolPref("geo.prompt.testing.allow", PR_FALSE), request);
1133 NS_DispatchToMainThread(ev);
1134 return;
1137 nsCOMPtr<nsIRunnable> ev = new RequestPromptEvent(request);
1138 NS_DispatchToMainThread(ev);
1141 #if !defined(WINCE_WINDOWS_MOBILE) && !defined(MOZ_MAEMO_LIBLOCATION) && !defined(ANDROID)
1142 DOMCI_DATA(GeoPositionCoords, void)
1143 DOMCI_DATA(GeoPosition, void)
1144 #endif
1146 #ifdef MOZ_IPC
1147 nsGeolocationRequestProxy::nsGeolocationRequestProxy()
1149 MOZ_COUNT_CTOR(nsGeolocationRequestProxy);
1152 nsGeolocationRequestProxy::~nsGeolocationRequestProxy()
1154 MOZ_COUNT_DTOR(nsGeolocationRequestProxy);
1157 nsresult
1158 nsGeolocationRequestProxy::Init(mozilla::dom::GeolocationRequestParent* parent)
1160 NS_ASSERTION(parent, "null parent");
1161 mParent = parent;
1163 nsCOMPtr<nsIContentPermissionPrompt> prompt = do_GetService(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
1164 if (!prompt) {
1165 return NS_ERROR_FAILURE;
1168 (void) prompt->Prompt(this);
1169 return NS_OK;
1172 NS_IMPL_ISUPPORTS1(nsGeolocationRequestProxy, nsIContentPermissionRequest);
1174 NS_IMETHODIMP
1175 nsGeolocationRequestProxy::GetType(nsACString & aType)
1177 aType = "geolocation";
1178 return NS_OK;
1181 NS_IMETHODIMP
1182 nsGeolocationRequestProxy::GetWindow(nsIDOMWindow * *aRequestingWindow)
1184 NS_ENSURE_ARG_POINTER(aRequestingWindow);
1185 *aRequestingWindow = nsnull;
1186 return NS_OK;
1189 NS_IMETHODIMP
1190 nsGeolocationRequestProxy::GetUri(nsIURI * *aRequestingURI)
1192 NS_ENSURE_ARG_POINTER(aRequestingURI);
1193 NS_ASSERTION(mParent, "No parent for request");
1195 NS_ADDREF(*aRequestingURI = mParent->mURI);
1196 return NS_OK;
1199 NS_IMETHODIMP
1200 nsGeolocationRequestProxy::GetElement(nsIDOMElement * *aRequestingElement)
1202 NS_ENSURE_ARG_POINTER(aRequestingElement);
1203 NS_ASSERTION(mParent && mParent->mElement.get(), "No parent for request");
1204 NS_ADDREF(*aRequestingElement = mParent->mElement);
1205 return NS_OK;
1208 NS_IMETHODIMP
1209 nsGeolocationRequestProxy::Cancel()
1211 NS_ASSERTION(mParent, "No parent for request");
1212 unused << mozilla::dom::GeolocationRequestParent::Send__delete__(mParent, false);
1213 mParent = nsnull;
1214 return NS_OK;
1217 NS_IMETHODIMP
1218 nsGeolocationRequestProxy::Allow()
1220 NS_ASSERTION(mParent, "No parent for request");
1221 unused << mozilla::dom::GeolocationRequestParent::Send__delete__(mParent, true);
1222 mParent = nsnull;
1223 return NS_OK;
1226 namespace mozilla {
1227 namespace dom {
1229 GeolocationRequestParent::GeolocationRequestParent(nsIDOMElement *element, const IPC::URI& uri)
1231 MOZ_COUNT_CTOR(GeolocationRequestParent);
1233 mURI = uri;
1234 mElement = element;
1237 GeolocationRequestParent::~GeolocationRequestParent()
1239 MOZ_COUNT_DTOR(GeolocationRequestParent);
1242 bool
1243 GeolocationRequestParent::Recvprompt()
1245 mProxy = new nsGeolocationRequestProxy();
1246 NS_ASSERTION(mProxy, "Alloc of request proxy failed");
1247 if (NS_FAILED(mProxy->Init(this)))
1248 mProxy->Cancel();
1249 return true;
1252 } // namespace dom
1253 } // namespace mozilla
1254 #endif