diff --git a/dom/public/idl/geolocation/nsIDOMGeoPositionOptions.idl b/dom/public/idl/geolocation/nsIDOMGeoPositionOptions.idl index 3e5c5b412ebe..d723a8b94af2 100644 --- a/dom/public/idl/geolocation/nsIDOMGeoPositionOptions.idl +++ b/dom/public/idl/geolocation/nsIDOMGeoPositionOptions.idl @@ -41,4 +41,5 @@ interface nsIDOMGeoPositionOptions : nsISupports { attribute boolean enableHighAccuracy; + attribute unsigned long timeout; }; diff --git a/dom/src/geolocation/nsGeolocation.cpp b/dom/src/geolocation/nsGeolocation.cpp index 76831e3fe1ac..eaa8ff8c3cc8 100644 --- a/dom/src/geolocation/nsGeolocation.cpp +++ b/dom/src/geolocation/nsGeolocation.cpp @@ -47,7 +47,6 @@ #include "nsIObserverService.h" #include "nsIPrefService.h" #include "nsIPrefBranch2.h" -#include "nsIProxyObjectManager.h" #include "nsIJSContextStack.h" #include @@ -69,13 +68,12 @@ 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) @@ -87,8 +85,8 @@ 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) { } @@ -106,16 +104,43 @@ nsDOMGeoPositionError::GetCode(PRInt16 *aCode) 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 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) { } @@ -126,11 +151,41 @@ 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 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) { @@ -152,6 +207,8 @@ nsGeolocationRequest::GetRequestingWindow(nsIDOMWindow * *aRequestingWindow) NS_IMETHODIMP nsGeolocationRequest::Cancel() { + NotifyError(NS_GEO_ERROR_CODE_PERMISSION_ERROR); + // remove ourselves from the locators callback lists. mLocator->RemoveRequest(this); return NS_OK; @@ -165,35 +222,15 @@ nsGeolocationRequest::Allow() nsresult rv = geoService->StartDevice(); if (NS_FAILED(rv)) { + // Location provider error + NotifyError(NS_GEO_ERROR_CODE_LOCATION_PROVIDER_ERROR); + return NS_OK; + } - if (!mErrorCallback) - return NS_OK; // If no one is listening for errors, fail silently. - - // TODO what are the real error values here!! - nsRefPtr positionError = new nsDOMGeoPositionError(1, NS_LITERAL_STRING("")); - - nsCOMPtr callbackProxy; - - nsCOMPtr 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 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; @@ -214,7 +251,7 @@ nsGeolocationRequest::MarkCleared() } void -nsGeolocationRequest::SendLocation(nsIDOMGeoPosition* position) +nsGeolocationRequest::SendLocation(nsIDOMGeoPosition* aPosition) { if (mCleared || !mAllowed) return; @@ -232,14 +269,14 @@ nsGeolocationRequest::SendLocation(nsIDOMGeoPosition* position) 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; @@ -266,12 +303,14 @@ nsGeolocationRequest::SendLocation(nsIDOMGeoPosition* position) } else { - mCallback->HandleEvent(position); + mCallback->HandleEvent(aPosition); } // remove the stack JSContext* cx; stack->Pop(&cx); + + mHasSentData = PR_TRUE; } void @@ -378,8 +417,9 @@ 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)) { @@ -434,10 +474,10 @@ nsGeolocationService::SetPrompt(nsIGeolocationPrompt * aPrompt) } 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; } @@ -532,6 +572,7 @@ nsGeolocationService::GetInstance() { if (!nsGeolocationService::gService) { nsGeolocationService::gService = new nsGeolocationService(); + NS_ASSERTION(nsGeolocationService::gService, "null nsGeolocationService."); } return nsGeolocationService::gService; } @@ -545,15 +586,15 @@ nsGeolocationService::GetGeolocationService() } 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); } //////////////////////////////////////////////////// @@ -569,17 +610,17 @@ 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 window = do_QueryInterface(contentDom); + nsCOMPtr window = do_QueryInterface(aContentDom); if (window) mOwner = window->GetCurrentInnerWindow(); // Grab the uri of the document nsCOMPtr domdoc; - contentDom->GetDocument(getter_AddRefs(domdoc)); + aContentDom->GetDocument(getter_AddRefs(domdoc)); nsCOMPtr doc = do_QueryInterface(domdoc); if (doc) doc->NodePrincipal()->GetURI(getter_AddRefs(mURI)); @@ -597,11 +638,11 @@ 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(); @@ -616,13 +657,13 @@ nsGeolocation::Shutdown() 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 @@ -630,11 +671,11 @@ nsGeolocation::RemoveRequest(nsGeolocationRequest* request) // 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 @@ -653,13 +694,13 @@ nsGeolocation::Update(nsIDOMGeoPosition *somewhere) } // 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; } @@ -682,37 +723,43 @@ nsGeolocation::GetCurrentPosition(nsIDOMGeoPositionCallback *callback, if (prompt == nsnull) return NS_ERROR_NOT_AVAILABLE; - nsRefPtr request = new nsGeolocationRequest(this, callback, errorCallback); + nsRefPtr 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 request = new nsGeolocationRequest(this, callback, errorCallback); + nsRefPtr 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; } diff --git a/dom/src/geolocation/nsGeolocation.h b/dom/src/geolocation/nsGeolocation.h index f12334a215ab..2267688bf3de 100644 --- a/dom/src/geolocation/nsGeolocation.h +++ b/dom/src/geolocation/nsGeolocation.h @@ -55,18 +55,25 @@ #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); @@ -76,14 +83,20 @@ class nsGeolocationRequest : public nsIGeolocationRequest ~nsGeolocationRequest(); private: - PRBool mAllowed; - PRBool mCleared; - PRBool mFuzzLocation; + void NotifyError(PRInt16 errorCode); + PRPackedBool mAllowed; + PRPackedBool mCleared; + PRPackedBool mFuzzLocation; + PRPackedBool mHasSentData; + + nsCOMPtr mTimeoutTimer; nsCOMPtr mCallback; nsCOMPtr mErrorCallback; + nsCOMPtr mOptions; + + nsGeolocation* mLocator; // The locator exists longer than this object. - nsGeolocation* mLocator; // The locator exists alonger than this object. }; /** @@ -186,13 +199,13 @@ public: 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; } @@ -212,8 +225,8 @@ private: // |mWatchingCallbacks| holds objects until the object is explictly removed or // there is a page change. - nsCOMArray mPendingCallbacks; - nsCOMArray mWatchingCallbacks; + nsTArray > mPendingCallbacks; + nsTArray > mWatchingCallbacks; PRBool mUpdateInProgress; diff --git a/dom/tests/mochitest/geolocation/Makefile.in b/dom/tests/mochitest/geolocation/Makefile.in index 66ed9cf4d877..90623bd3ecd7 100644 --- a/dom/tests/mochitest/geolocation/Makefile.in +++ b/dom/tests/mochitest/geolocation/Makefile.in @@ -53,6 +53,7 @@ _TEST_FILES = \ test_cancelWatch.html \ test_clearWatch.html \ test_geoPrompt.html \ + test_timeoutWatch.html \ prompt_common.js \ geolocation_common.js \ geolocation.html \ diff --git a/dom/tests/mochitest/geolocation/geolocation_common.js b/dom/tests/mochitest/geolocation/geolocation_common.js index 9c00f1ef4df3..f1b58b8bee97 100644 --- a/dom/tests/mochitest/geolocation/geolocation_common.js +++ b/dom/tests/mochitest/geolocation/geolocation_common.js @@ -113,7 +113,7 @@ function attachPrompt() { .getService(Components.interfaces.nsIGeolocationService); old_prompt = geolocationService.prompt; - if(DELAYED_PROMPT) + if(DELAYED_PROMPT) geolocationService.prompt = delayed_prompt; else geolocationService.prompt = geolocation_prompt; diff --git a/dom/tests/mochitest/geolocation/testLocationProvider.js b/dom/tests/mochitest/geolocation/testLocationProvider.js index 69ed05e7b764..733dc0023541 100644 --- a/dom/tests/mochitest/geolocation/testLocationProvider.js +++ b/dom/tests/mochitest/geolocation/testLocationProvider.js @@ -3,15 +3,15 @@ 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; }, @@ -25,11 +25,11 @@ GeolocationObject.prototype = { 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) { @@ -64,7 +64,7 @@ MyLocation.prototype = { Ci.nsITimer.TYPE_REPEATING_SLACK); }, - currentLocation: new GeolocationObject(), + currentLocation: new GeopositionObject(), shutdown: function() { dump("shutdown"); if(this.timer) diff --git a/dom/tests/mochitest/geolocation/test_timeoutWatch.html b/dom/tests/mochitest/geolocation/test_timeoutWatch.html new file mode 100644 index 000000000000..066e6cd2dd3e --- /dev/null +++ b/dom/tests/mochitest/geolocation/test_timeoutWatch.html @@ -0,0 +1,65 @@ + + + + + Test for timeout option + + + + + + + + +Mozilla Bug +

+ +
+
+
+ +