Bug 455327 - geolocation - support timeout position option. r=Olli.Pettay sr=bzbarsky
authorDoug Turner <dougt@meer.net>
Mon, 20 Oct 2008 12:37:10 -0700
changeset 20687 a087bc8963b354856393307920e9cc4cf5f12b97
parent 20686 8af6350f8dd0e61bfe481ecc2ecc26d53e65bfa7
child 20688 8eb68774bb653128bc565604652f42e556c88101
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersOlli.Pettay, bzbarsky
bugs455327
milestone1.9.1b2pre
Bug 455327 - geolocation - support timeout position option. r=Olli.Pettay sr=bzbarsky
dom/public/idl/geolocation/nsIDOMGeoPositionOptions.idl
dom/src/geolocation/nsGeolocation.cpp
dom/src/geolocation/nsGeolocation.h
dom/tests/mochitest/geolocation/Makefile.in
dom/tests/mochitest/geolocation/geolocation_common.js
dom/tests/mochitest/geolocation/testLocationProvider.js
dom/tests/mochitest/geolocation/test_timeoutWatch.html
--- a/dom/public/idl/geolocation/nsIDOMGeoPositionOptions.idl
+++ b/dom/public/idl/geolocation/nsIDOMGeoPositionOptions.idl
@@ -36,9 +36,10 @@
 
 
 #include "domstubs.idl"
 
 [scriptable, uuid(74FA3BE6-4CBF-47C7-9BE1-FB9F17DD0D5D)]
 interface nsIDOMGeoPositionOptions : nsISupports
 {
   attribute boolean enableHighAccuracy;
+  attribute unsigned long timeout;
 };
--- a/dom/src/geolocation/nsGeolocation.cpp
+++ b/dom/src/geolocation/nsGeolocation.cpp
@@ -42,17 +42,16 @@
 #include "nsComponentManagerUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsContentUtils.h"
 #include "nsIURI.h"
 #include "nsIPermissionManager.h"
 #include "nsIObserverService.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch2.h"
-#include "nsIProxyObjectManager.h"
 #include "nsIJSContextStack.h"
 
 #include <math.h>
 
 #ifdef NS_MAEMO_LOCATION
 #include "MaemoLocationProvider.h"
 #endif
 
@@ -64,36 +63,35 @@
 ////////////////////////////////////////////////////
 
 class nsDOMGeoPositionError : public nsIDOMGeoPositionError
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMGEOPOSITIONERROR
 
-  nsDOMGeoPositionError(PRInt16 aCode, const nsAString& aMessage);
+  nsDOMGeoPositionError(PRInt16 aCode);
+  void NotifyCallback(nsIDOMGeoPositionErrorCallback* callback);
 
 private:
   ~nsDOMGeoPositionError();
   PRInt16 mCode;
-  nsString mMessage;
-
 };
 
 NS_INTERFACE_MAP_BEGIN(nsDOMGeoPositionError)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMGeoPositionError)
   NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPositionError)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(GeoPositionError)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_THREADSAFE_ADDREF(nsDOMGeoPositionError)
 NS_IMPL_THREADSAFE_RELEASE(nsDOMGeoPositionError)
 
-nsDOMGeoPositionError::nsDOMGeoPositionError(PRInt16 aCode, const nsAString& aMessage)
-  : mCode(aCode), mMessage(aMessage)
+nsDOMGeoPositionError::nsDOMGeoPositionError(PRInt16 aCode)
+  : mCode(aCode)
 {
 }
 
 nsDOMGeoPositionError::~nsDOMGeoPositionError(){}
 
 
 NS_IMETHODIMP
 nsDOMGeoPositionError::GetCode(PRInt16 *aCode)
@@ -101,41 +99,98 @@ nsDOMGeoPositionError::GetCode(PRInt16 *
   NS_ENSURE_ARG_POINTER(aCode);
   *aCode = mCode;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMGeoPositionError::GetMessage(nsAString & aMessage)
 {
-  aMessage = mMessage;
+  aMessage.Truncate();
   return NS_OK;
 }
 
+void
+nsDOMGeoPositionError::NotifyCallback(nsIDOMGeoPositionErrorCallback* aCallback)
+{
+  if (!aCallback)
+    return;
+  
+  // Ensure that the proper context is on the stack (bug 452762)
+  nsCOMPtr<nsIJSContextStack> stack(do_GetService("@mozilla.org/js/xpc/ContextStack;1"));
+  if (!stack || NS_FAILED(stack->Push(nsnull)))
+    return;
+  
+  aCallback->HandleEvent(this);
+  
+  // remove the stack
+  JSContext* cx;
+  stack->Pop(&cx);
+}
 ////////////////////////////////////////////////////
 // nsGeolocationRequest
 ////////////////////////////////////////////////////
 
-nsGeolocationRequest::nsGeolocationRequest(nsGeolocation* locator, nsIDOMGeoPositionCallback* callback, nsIDOMGeoPositionErrorCallback* errorCallback)
-  : mAllowed(PR_FALSE), mCleared(PR_FALSE), mFuzzLocation(PR_FALSE), mCallback(callback), mErrorCallback(errorCallback), mLocator(locator)
+nsGeolocationRequest::nsGeolocationRequest(nsGeolocation* aLocator,
+                                           nsIDOMGeoPositionCallback* aCallback,
+                                           nsIDOMGeoPositionErrorCallback* aErrorCallback,
+                                           nsIDOMGeoPositionOptions* aOptions)
+  : mAllowed(PR_FALSE),
+    mCleared(PR_FALSE),
+    mFuzzLocation(PR_FALSE),
+    mHasSentData(PR_FALSE),
+    mCallback(aCallback),
+    mErrorCallback(aErrorCallback),
+    mOptions(aOptions),
+    mLocator(aLocator)
 {
 }
 
 nsGeolocationRequest::~nsGeolocationRequest()
 {
 }
 
 NS_INTERFACE_MAP_BEGIN(nsGeolocationRequest)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIGeolocationRequest)
   NS_INTERFACE_MAP_ENTRY(nsIGeolocationRequest)
+  NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(nsGeolocationRequest)
 NS_IMPL_RELEASE(nsGeolocationRequest)
 
+
+void
+nsGeolocationRequest::NotifyError(PRInt16 errorCode)
+{
+  nsRefPtr<nsDOMGeoPositionError> positionError = new nsDOMGeoPositionError(errorCode);
+  if (!positionError)
+    return;
+  
+  positionError->NotifyCallback(mErrorCallback);
+}
+
+
+NS_IMETHODIMP
+nsGeolocationRequest::Notify(nsITimer* aTimer)
+{
+  // If we haven't gotten an answer from the geolocation
+  // provider yet, cancel the request.  Same logic as
+  // ::Cancel, just a different error
+  
+  if (!mHasSentData) {
+    NotifyError(NS_GEO_ERROR_CODE_TIMEOUT);
+    // remove ourselves from the locator's callback lists.
+    mLocator->RemoveRequest(this);
+  }
+
+  mTimeoutTimer = nsnull;
+  return NS_OK;
+}
+ 
 NS_IMETHODIMP
 nsGeolocationRequest::GetRequestingURI(nsIURI * *aRequestingURI)
 {
   NS_ENSURE_ARG_POINTER(aRequestingURI);
   *aRequestingURI = mLocator->GetURI();
   NS_IF_ADDREF(*aRequestingURI);
   return NS_OK;
 }
@@ -147,58 +202,40 @@ nsGeolocationRequest::GetRequestingWindo
   *aRequestingWindow = mLocator->GetOwner();
   NS_IF_ADDREF(*aRequestingWindow);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsGeolocationRequest::Cancel()
 {
+  NotifyError(NS_GEO_ERROR_CODE_PERMISSION_ERROR);
+
   // remove ourselves from the locators callback lists.
   mLocator->RemoveRequest(this);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsGeolocationRequest::Allow()
 {
   // Kick off the geo device, if it isn't already running
   nsRefPtr<nsGeolocationService> geoService = nsGeolocationService::GetInstance();
   nsresult rv = geoService->StartDevice();
   
   if (NS_FAILED(rv)) {
-
-    if (!mErrorCallback)
-      return NS_OK;  // If no one is listening for errors, fail silently.
-
-    // TODO what are the real error values here!!
-    nsRefPtr<nsDOMGeoPositionError> positionError = new nsDOMGeoPositionError(1, NS_LITERAL_STRING(""));
-
-    nsCOMPtr<nsIDOMGeoPositionErrorCallback> callbackProxy;
+    // Location provider error
+    NotifyError(NS_GEO_ERROR_CODE_LOCATION_PROVIDER_ERROR);
+    return NS_OK;
+  }
 
-    nsCOMPtr<nsIProxyObjectManager> proxyObjMgr = do_GetService("@mozilla.org/xpcomproxy;1");
-    proxyObjMgr->GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
-                                   NS_GET_IID(nsIDOMGeoPositionErrorCallback),
-                                   mErrorCallback,
-                                   NS_PROXY_ASYNC | NS_PROXY_ALWAYS,
-                                   getter_AddRefs(callbackProxy));
-
-
-    // Ensure that the proper context is on the stack (bug 452762)
-    nsCOMPtr<nsIJSContextStack> stack(do_GetService("@mozilla.org/js/xpc/ContextStack;1"));
-    if (!stack || NS_FAILED(stack->Push(nsnull)))
-      return NS_OK; // silently fail
-
-    callbackProxy->HandleEvent(positionError);
-
-    // remove the stack
-    JSContext* cx;
-    stack->Pop(&cx);
-
-    return NS_OK;  // silently fail
+  PRUint32 timeout;
+  if (mOptions && NS_SUCCEEDED(mOptions->GetTimeout(&timeout)) && timeout > 0) {
+      mTimeoutTimer = do_CreateInstance("@mozilla.org/timer;1");
+      mTimeoutTimer->InitWithCallback(this, timeout, nsITimer::TYPE_ONE_SHOT);
   }
 
   mAllowed = PR_TRUE;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsGeolocationRequest::AllowButFuzz()
@@ -209,17 +246,17 @@ nsGeolocationRequest::AllowButFuzz()
 
 void
 nsGeolocationRequest::MarkCleared()
 {
   mCleared = PR_TRUE;
 }
 
 void
-nsGeolocationRequest::SendLocation(nsIDOMGeoPosition* position)
+nsGeolocationRequest::SendLocation(nsIDOMGeoPosition* aPosition)
 {
   if (mCleared || !mAllowed)
     return;
 
   // Ensure that the proper context is on the stack (bug 452762)
   nsCOMPtr<nsIJSContextStack> stack(do_GetService("@mozilla.org/js/xpc/ContextStack;1"));
   if (!stack || NS_FAILED(stack->Push(nsnull)))
     return; // silently fail
@@ -227,24 +264,24 @@ nsGeolocationRequest::SendLocation(nsIDO
   //TODO mFuzzLocation.  Needs to be defined what we do here.
   if (mFuzzLocation)
   {
     // need to make a copy because nsIDOMGeoPosition is
     // readonly, and we are not sure of its implementation.
 
     double lat, lon, alt, herror, verror, heading, velocity;
     DOMTimeStamp time;
-    position->GetLatitude(&lat);
-    position->GetLongitude(&lon);
-    position->GetAltitude(&alt);
-    position->GetAccuracy(&herror);
-    position->GetAltitudeAccuracy(&verror);
-    position->GetHeading(&heading);
-    position->GetVelocity(&velocity);
-    position->GetTimestamp(&time); 
+    aPosition->GetLatitude(&lat);
+    aPosition->GetLongitude(&lon);
+    aPosition->GetAltitude(&alt);
+    aPosition->GetAccuracy(&herror);
+    aPosition->GetAltitudeAccuracy(&verror);
+    aPosition->GetHeading(&heading);
+    aPosition->GetVelocity(&velocity);
+    aPosition->GetTimestamp(&time); 
 
     // Truncate ?
     // lat = floor(lat*10+.5)/10;
     // lon = floor(lon*10+.5)/10;
     // herror = 1600; /* about 1 mile */
 
     lat = 0;
     lon = 0;
@@ -261,22 +298,24 @@ nsGeolocationRequest::SendLocation(nsIDO
                                                           verror,
                                                           heading,
                                                           velocity,
                                                           time);
     mCallback->HandleEvent(somewhere);
   }
   else
   {
-    mCallback->HandleEvent(position);
+    mCallback->HandleEvent(aPosition);
   }
 
   // remove the stack
   JSContext* cx;
   stack->Pop(&cx);
+
+  mHasSentData = PR_TRUE;
 }
 
 void
 nsGeolocationRequest::Shutdown()
 {
   mCleared = PR_TRUE;
   mCallback = nsnull;
   mErrorCallback = nsnull;
@@ -373,18 +412,19 @@ nsGeolocationService::nsGeolocationServi
   mTimeout = nsContentUtils::GetIntPref("geo.timeout", 6000);
 }
 
 nsGeolocationService::~nsGeolocationService()
 {
 }
 
 NS_IMETHODIMP
-nsGeolocationService::Observe(nsISupports* aSubject, const char* aTopic,
-                             const PRUnichar* aData)
+nsGeolocationService::Observe(nsISupports* aSubject,
+                              const char* aTopic,
+                              const PRUnichar* aData)
 {
   if (!strcmp("quit-application", aTopic))
   {
     nsCOMPtr<nsIObserverService> obs = do_GetService("@mozilla.org/observer-service;1");
     if (obs) {
       obs->RemoveObserver(this, "quit-application");
     }
 
@@ -429,20 +469,20 @@ nsGeolocationService::GetPrompt(nsIGeolo
 NS_IMETHODIMP
 nsGeolocationService::SetPrompt(nsIGeolocationPrompt * aPrompt)
 {
   mPrompt = aPrompt;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsGeolocationService::Update(nsIDOMGeoPosition *somewhere)
+nsGeolocationService::Update(nsIDOMGeoPosition *aSomewhere)
 {
   for (PRUint32 i = 0; i< mGeolocators.Length(); i++)
-    mGeolocators[i]->Update(somewhere);
+    mGeolocators[i]->Update(aSomewhere);
   return NS_OK;
 }
 
 already_AddRefed<nsIDOMGeoPosition>
 nsGeolocationService::GetLastKnownPosition()
 {
   nsIDOMGeoPosition* p = nsnull;
   if (mProvider)
@@ -527,64 +567,65 @@ nsGeolocationService::StopDevice()
 
 nsGeolocationService* nsGeolocationService::gService = nsnull;
 
 nsGeolocationService*
 nsGeolocationService::GetInstance()
 {
   if (!nsGeolocationService::gService) {
     nsGeolocationService::gService = new nsGeolocationService();
+    NS_ASSERTION(nsGeolocationService::gService, "null nsGeolocationService.");
   }
   return nsGeolocationService::gService;
 }
 
 nsGeolocationService*
 nsGeolocationService::GetGeolocationService()
 {
   nsGeolocationService* inst = nsGeolocationService::GetInstance();
   NS_ADDREF(inst);
   return inst;
 }
 
 void
-nsGeolocationService::AddLocator(nsGeolocation* locator)
+nsGeolocationService::AddLocator(nsGeolocation* aLocator)
 {
-  mGeolocators.AppendElement(locator);
+  mGeolocators.AppendElement(aLocator);
 }
 
 void
-nsGeolocationService::RemoveLocator(nsGeolocation* locator)
+nsGeolocationService::RemoveLocator(nsGeolocation* aLocator)
 {
-  mGeolocators.RemoveElement(locator);
+  mGeolocators.RemoveElement(aLocator);
 }
 
 ////////////////////////////////////////////////////
 // nsGeolocation
 ////////////////////////////////////////////////////
 
 NS_INTERFACE_MAP_BEGIN(nsGeolocation)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMGeoGeolocation)
   NS_INTERFACE_MAP_ENTRY(nsIDOMGeoGeolocation)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(GeoGeolocation)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(nsGeolocation)
 NS_IMPL_RELEASE(nsGeolocation)
 
-nsGeolocation::nsGeolocation(nsIDOMWindow* contentDom) 
+nsGeolocation::nsGeolocation(nsIDOMWindow* aContentDom) 
 : mUpdateInProgress(PR_FALSE)
 {
   // Remember the window
-  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(contentDom);
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aContentDom);
   if (window)
     mOwner = window->GetCurrentInnerWindow();
 
   // Grab the uri of the document
   nsCOMPtr<nsIDOMDocument> domdoc;
-  contentDom->GetDocument(getter_AddRefs(domdoc));
+  aContentDom->GetDocument(getter_AddRefs(domdoc));
   nsCOMPtr<nsIDocument> doc = do_QueryInterface(domdoc);
   if (doc)
     doc->NodePrincipal()->GetURI(getter_AddRefs(mURI));
 
   mService = nsGeolocationService::GetInstance();
   if (mService)
     mService->AddLocator(this);
 }
@@ -592,54 +633,54 @@ nsGeolocation::nsGeolocation(nsIDOMWindo
 nsGeolocation::~nsGeolocation()
 {
 }
 
 void
 nsGeolocation::Shutdown()
 {
   // Shutdown and release all callbacks
-  for (PRInt32 i = 0; i< mPendingCallbacks.Count(); i++)
+  for (PRUint32 i = 0; i< mPendingCallbacks.Length(); i++)
     mPendingCallbacks[i]->Shutdown();
   mPendingCallbacks.Clear();
 
-  for (PRInt32 i = 0; i< mWatchingCallbacks.Count(); i++)
+  for (PRUint32 i = 0; i< mWatchingCallbacks.Length(); i++)
     mWatchingCallbacks[i]->Shutdown();
   mWatchingCallbacks.Clear();
 
   if (mService)
     mService->RemoveLocator(this);
 
   mService = nsnull;
   mOwner = nsnull;
   mURI = nsnull;
 }
 
 PRBool
 nsGeolocation::HasActiveCallbacks()
 {
-  return (PRBool) mWatchingCallbacks.Count();
+  return mWatchingCallbacks.Length() != 0;
 }
 
 void
-nsGeolocation::RemoveRequest(nsGeolocationRequest* request)
+nsGeolocation::RemoveRequest(nsGeolocationRequest* aRequest)
 {
-  mPendingCallbacks.RemoveObject(request);
+  mPendingCallbacks.RemoveElement(aRequest);
 
   // if it is in the mWatchingCallbacks, we can't do much
   // since we passed back the position in the array to who
   // ever called WatchPosition() and we do not want to mess
   // around with the ordering of the array.  Instead, just
   // mark the request as "cleared".
 
-  request->MarkCleared();
+  aRequest->MarkCleared();
 }
 
 void
-nsGeolocation::Update(nsIDOMGeoPosition *somewhere)
+nsGeolocation::Update(nsIDOMGeoPosition *aSomewhere)
 {
   // This method calls out to objects which may spin and
   // event loop which may add new location objects into
   // mPendingCallbacks, and mWatchingCallbacks.  Since this
   // function can only be called on the primary thread, we
   // can lock this method with a member var.
 
   if (mUpdateInProgress)
@@ -648,23 +689,23 @@ nsGeolocation::Update(nsIDOMGeoPosition 
   mUpdateInProgress = PR_TRUE;
   if (!OwnerStillExists())
   {
     Shutdown();
     return;
   }
 
   // notify anyone that has been waiting
-  for (PRInt32 i = 0; i< mPendingCallbacks.Count(); i++)
-    mPendingCallbacks[i]->SendLocation(somewhere);
+  for (PRUint32 i = 0; i< mPendingCallbacks.Length(); i++)
+    mPendingCallbacks[i]->SendLocation(aSomewhere);
   mPendingCallbacks.Clear();
 
   // notify everyone that is watching
-  for (PRInt32 i = 0; i< mWatchingCallbacks.Count(); i++)
-      mWatchingCallbacks[i]->SendLocation(somewhere);
+  for (PRUint32 i = 0; i< mWatchingCallbacks.Length(); i++)
+    mWatchingCallbacks[i]->SendLocation(aSomewhere);
 
   mUpdateInProgress = PR_FALSE;
 }
 
 NS_IMETHODIMP
 nsGeolocation::GetLastPosition(nsIDOMGeoPosition * *aLastPosition)
 {
   // we are advocating that this method be removed.
@@ -677,47 +718,53 @@ NS_IMETHODIMP
 nsGeolocation::GetCurrentPosition(nsIDOMGeoPositionCallback *callback,
                                   nsIDOMGeoPositionErrorCallback *errorCallback,
                                   nsIDOMGeoPositionOptions *options)
 {
   nsIGeolocationPrompt* prompt = mService->GetPrompt();
   if (prompt == nsnull)
     return NS_ERROR_NOT_AVAILABLE;
 
-  nsRefPtr<nsGeolocationRequest> request = new nsGeolocationRequest(this, callback, errorCallback);
+  nsRefPtr<nsGeolocationRequest> request = new nsGeolocationRequest(this, callback, errorCallback, options);
+  if (!request)
+    return NS_ERROR_OUT_OF_MEMORY;
+
   prompt->Prompt(request);
 
   // What if you have a location provider that only sends a location once, then stops.?  fix.
-  mPendingCallbacks.AppendObject(request);
+  mPendingCallbacks.AppendElement(request);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsGeolocation::WatchPosition(nsIDOMGeoPositionCallback *callback,
-                             nsIDOMGeoPositionErrorCallback *errorCallback,
-                             nsIDOMGeoPositionOptions *options, 
+nsGeolocation::WatchPosition(nsIDOMGeoPositionCallback *aCallback,
+                             nsIDOMGeoPositionErrorCallback *aErrorCallback,
+                             nsIDOMGeoPositionOptions *aOptions, 
                              PRUint16 *_retval NS_OUTPARAM)
 {
   nsIGeolocationPrompt* prompt = mService->GetPrompt();
   if (prompt == nsnull)
     return NS_ERROR_NOT_AVAILABLE;
     
-  nsRefPtr<nsGeolocationRequest> request = new nsGeolocationRequest(this, callback, errorCallback);
+  nsRefPtr<nsGeolocationRequest> request = new nsGeolocationRequest(this, aCallback, aErrorCallback, aOptions);
+  if (!request)
+    return NS_ERROR_OUT_OF_MEMORY;
+
   prompt->Prompt(request);
 
   // need to hand back an index/reference.
-  mWatchingCallbacks.AppendObject(request);
-  *_retval = mWatchingCallbacks.Count() - 1;
+  mWatchingCallbacks.AppendElement(request);
+  *_retval = mWatchingCallbacks.Length() - 1;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsGeolocation::ClearWatch(PRUint16 watchId)
+nsGeolocation::ClearWatch(PRUint16 aWatchId)
 {
-  mWatchingCallbacks[watchId]->MarkCleared();
+  mWatchingCallbacks[aWatchId]->MarkCleared();
   return NS_OK;
 }
 
 PRBool
 nsGeolocation::OwnerStillExists()
 {
   if (!mOwner)
     return PR_FALSE;
--- a/dom/src/geolocation/nsGeolocation.h
+++ b/dom/src/geolocation/nsGeolocation.h
@@ -50,45 +50,58 @@
 #include "nsIDOMGeoPositionErrorCallback.h"
 #include "nsIDOMGeoPositionOptions.h"
 #include "nsIDOMNavigatorGeolocation.h"
 
 #include "nsPIDOMWindow.h"
 
 #include "nsIGeolocationProvider.h"
 
+#define NS_GEO_ERROR_CODE_PERMISSION_ERROR        1
+#define NS_GEO_ERROR_CODE_LOCATION_PROVIDER_ERROR 2
+#define NS_GEO_ERROR_CODE_POSITION_NOT_FOUND      3
+#define NS_GEO_ERROR_CODE_TIMEOUT                 4
+
 class nsGeolocationService;
 class nsGeolocation;
 
-class nsGeolocationRequest : public nsIGeolocationRequest
+class nsGeolocationRequest : public nsIGeolocationRequest, public nsITimerCallback
 {
  public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIGEOLOCATIONREQUEST
-
+  NS_DECL_NSITIMERCALLBACK
+ 
   nsGeolocationRequest(nsGeolocation* locator,
                        nsIDOMGeoPositionCallback* callback,
-                       nsIDOMGeoPositionErrorCallback* errorCallback);
+                       nsIDOMGeoPositionErrorCallback* errorCallback,
+                       nsIDOMGeoPositionOptions* options);
   void Shutdown();
 
   void SendLocation(nsIDOMGeoPosition* location);
   void MarkCleared();
   PRBool Allowed() {return mAllowed;}
 
   ~nsGeolocationRequest();
 
  private:
-  PRBool mAllowed;
-  PRBool mCleared;
-  PRBool mFuzzLocation;
 
+  void NotifyError(PRInt16 errorCode);
+  PRPackedBool mAllowed;
+  PRPackedBool mCleared;
+  PRPackedBool mFuzzLocation;
+  PRPackedBool mHasSentData;
+
+  nsCOMPtr<nsITimer> mTimeoutTimer;
   nsCOMPtr<nsIDOMGeoPositionCallback> mCallback;
   nsCOMPtr<nsIDOMGeoPositionErrorCallback> mErrorCallback;
+  nsCOMPtr<nsIDOMGeoPositionOptions> mOptions;
 
-  nsGeolocation* mLocator; // The locator exists alonger than this object.
+  nsGeolocation* mLocator; // The locator exists longer than this object.
+
 };
 
 /**
  * Simple object that holds a single point in space.
  */ 
 class nsGeoPosition : public nsIDOMGeoPosition
 {
 public:
@@ -181,23 +194,23 @@ public:
   NS_DECL_NSIDOMGEOGEOLOCATION
 
   nsGeolocation(nsIDOMWindow* contentDom);
 
   // Called by the geolocation device to notify that a location has changed.
   void Update(nsIDOMGeoPosition* aPosition);
 
   // Returns true if any of the callbacks are repeating
-  PRBool   HasActiveCallbacks();
+  PRBool HasActiveCallbacks();
 
   // Remove request from all callbacks arrays
-  void     RemoveRequest(nsGeolocationRequest* request);
+  void RemoveRequest(nsGeolocationRequest* request);
 
   // Shutting down.
-  void     Shutdown();
+  void Shutdown();
 
   // Setter and Getter of the URI that this nsGeolocation was loaded from
   nsIURI* GetURI() { return mURI; }
 
   // Setter and Getter of the window that this nsGeolocation is owned by
   nsIDOMWindow* GetOwner() { return mOwner; }
 
   // Check to see if the widnow still exists
@@ -207,18 +220,18 @@ private:
 
   ~nsGeolocation();
 
   // Two callback arrays.  The first |mPendingCallbacks| holds objects for only
   // one callback and then they are released/removed from the array.  The second
   // |mWatchingCallbacks| holds objects until the object is explictly removed or
   // there is a page change.
 
-  nsCOMArray<nsGeolocationRequest> mPendingCallbacks;
-  nsCOMArray<nsGeolocationRequest> mWatchingCallbacks;
+  nsTArray<nsRefPtr<nsGeolocationRequest> > mPendingCallbacks;
+  nsTArray<nsRefPtr<nsGeolocationRequest> > mWatchingCallbacks;
 
   PRBool mUpdateInProgress;
 
   // window that this was created for.  Weak reference.
   nsPIDOMWindow* mOwner;
 
   // where the content was loaded from
   nsCOMPtr<nsIURI> mURI;
--- a/dom/tests/mochitest/geolocation/Makefile.in
+++ b/dom/tests/mochitest/geolocation/Makefile.in
@@ -48,16 +48,17 @@ include $(topsrcdir)/config/rules.mk
 		test_lastPosition.html \
 		test_manyWindows.html \
 		test_allowCurrent.html \
 		test_allowWatch.html \
 		test_cancelCurrent.html \
 		test_cancelWatch.html \
 		test_clearWatch.html \
 		test_geoPrompt.html \
+		test_timeoutWatch.html \
 		prompt_common.js       \
 		geolocation_common.js  \
 		geolocation.html \
 		test_optional_api_params.html \
 		$(NULL)
 
 
 libs:: 	$(_TEST_FILES)
--- a/dom/tests/mochitest/geolocation/geolocation_common.js
+++ b/dom/tests/mochitest/geolocation/geolocation_common.js
@@ -108,17 +108,17 @@ function delayed_prompt(request) {
 }
 
 function attachPrompt() {
   netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
   var geolocationService = Components.classes["@mozilla.org/geolocation/service;1"]
                            .getService(Components.interfaces.nsIGeolocationService);
   old_prompt = geolocationService.prompt;
 
-	if(DELAYED_PROMPT)
+  if(DELAYED_PROMPT)
     geolocationService.prompt = delayed_prompt;
   else
     geolocationService.prompt = geolocation_prompt;
 }
 
 function removePrompt() {
   netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
   var geolocationService = Components.classes["@mozilla.org/geolocation/service;1"]
--- a/dom/tests/mochitest/geolocation/testLocationProvider.js
+++ b/dom/tests/mochitest/geolocation/testLocationProvider.js
@@ -1,40 +1,40 @@
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 
-function GeolocationObject() {};
-GeolocationObject.prototype = {
+function GeopositionObject() {};
+GeopositionObject.prototype = {
 
-    QueryInterface:   XPCOMUtils.generateQI([Ci.nsIDOMGeolocation, Ci.nsIClassInfo, Ci.nsIGeolocationPrompt]),
+    QueryInterface:   XPCOMUtils.generateQI([Ci.nsIDOMGeoPosition, Ci.nsIClassInfo]),
 
     // Class Info is required to be able to pass objects back into the DOM.
     
     getInterfaces: function(countRef) {
-        var interfaces = [Ci.nsIDOMGeolocation, Ci.nsIClassInfo, Ci.nsISupports, Ci.nsIGeolocationPrompt];
+        var interfaces = [Ci.nsIDOMGeoPosition, Ci.nsIClassInfo, Ci.nsISupports];
         countRef.value = interfaces.length;
         return interfaces;
     },
     getHelperForLanguage: function(language) null,
     contractID: "",
     classDescription: "geolocation object",
     classID: null,
     implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT,
     flags: Ci.nsIClassInfo.DOM_OBJECT,
 
     latitude: 1,
     longitude: 1,
     altitude: 1,
-    horizontalAccuracy: 1,
-    verticalAccuracy: 1,
+    accuracy: 1,
+    altitudeAccuracy: 1,
+    heading: 1,
+    velocity: 1,
     timestamp: 1,
-
-    prompt: function(uri) {return 1;},
 };
 
 function dump(msg) {
     var consoleService = Components.classes["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService);
     consoleService.logStringMessage("mylocation: " + msg);
 }
 
 function MyLocation() {};
@@ -59,17 +59,17 @@ MyLocation.prototype = {
                         watchCallback.parent = this;
 
                         if(!this.timer)
                            this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
                         this.timer.initWithCallback(watchCallback, this.timerInterval, 
                            Ci.nsITimer.TYPE_REPEATING_SLACK);
                      },
 
-    currentLocation: new GeolocationObject(),
+    currentLocation: new GeopositionObject(),
     shutdown:        function() { 
                        dump("shutdown"); 
                        if(this.timer)
                          this.timer.cancel();
                      },
 };
 
 var components = [MyLocation];
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/geolocation/test_timeoutWatch.html
@@ -0,0 +1,65 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=455327
+-->
+<head>
+  <title>Test for timeout option </title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="prompt_common.js"></script>
+  <script type="text/javascript" src="geolocation_common.js"></script>
+
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=455327">Mozilla Bug </a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+/** Test for Bug  **/
+
+SimpleTest.waitForExplicitFinish();
+
+promptOption = ACCEPT;
+attachPrompt();
+
+ok(navigator.geolocation, "Ensure that the geolocation object is present");
+
+function successCallback(pos){
+  ok(0, "success should have never been called.");
+  removePrompt();
+  SimpleTest.finish();
+}
+
+function errorCallback(err) {
+    removePrompt();
+
+    if (err.code == 2) {
+        // nothing is hooked up to test against.
+        SimpleTest.finish();
+        return;
+    }
+
+  ok(err.code == 4, "ensure error is a timeout.");
+  removePrompt();
+  SimpleTest.finish();
+}
+
+
+var options = {
+    enableHighAccuracy: true,
+    timeout: 100
+};
+
+navigator.geolocation.watchPosition(successCallback,
+                                    errorCallback,
+                                    options);
+
+</script>
+</pre>
+</body>
+</html>