Bug 455327 - geolocation - support timeout position option. r=Olli.Pettay sr=bzbarsky

This commit is contained in:
Doug Turner 2008-10-20 12:37:10 -07:00
Родитель a1e05f66e5
Коммит 57817fe1c1
7 изменённых файлов: 228 добавлений и 101 удалений

Просмотреть файл

@ -41,4 +41,5 @@
interface nsIDOMGeoPositionOptions : nsISupports
{
attribute boolean enableHighAccuracy;
attribute unsigned long timeout;
};

Просмотреть файл

@ -47,7 +47,6 @@
#include "nsIObserverService.h"
#include "nsIPrefService.h"
#include "nsIPrefBranch2.h"
#include "nsIProxyObjectManager.h"
#include "nsIJSContextStack.h"
#include <math.h>
@ -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<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)
{
}
@ -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<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)
{
@ -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<nsDOMGeoPositionError> positionError = new nsDOMGeoPositionError(1, NS_LITERAL_STRING(""));
nsCOMPtr<nsIDOMGeoPositionErrorCallback> callbackProxy;
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;
@ -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<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));
@ -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<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;
}

Просмотреть файл

@ -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<nsITimer> mTimeoutTimer;
nsCOMPtr<nsIDOMGeoPositionCallback> mCallback;
nsCOMPtr<nsIDOMGeoPositionErrorCallback> mErrorCallback;
nsCOMPtr<nsIDOMGeoPositionOptions> 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<nsGeolocationRequest> mPendingCallbacks;
nsCOMArray<nsGeolocationRequest> mWatchingCallbacks;
nsTArray<nsRefPtr<nsGeolocationRequest> > mPendingCallbacks;
nsTArray<nsRefPtr<nsGeolocationRequest> > mWatchingCallbacks;
PRBool mUpdateInProgress;

Просмотреть файл

@ -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 \

Просмотреть файл

@ -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;

Просмотреть файл

@ -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)

Просмотреть файл

@ -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>