Bug 874996 - Part 1 - Simplify handling of geolocation requests. r=jdm

Requests are now only put in the pending/watching requests
arrays after they have been allowed, and are removed after
being passed a single position (for getCurrentPosition) or
cleared (for watchPosition). getCurrentPosition requests that
are fulfilled by the cache aren't even added to the list.
This commit is contained in:
Guilherme Gonçalves 2013-06-19 12:08:10 -07:00 committed by John Schoenick
parent b7a0d6e56a
commit 9a9b715411
2 changed files with 66 additions and 110 deletions

View File

@ -184,23 +184,15 @@ private:
class RequestSendLocationEvent : public nsRunnable
{
public:
// a bit funky. if locator is passed, that means this
// event should remove the request from it. If we ever
// have to do more, then we can change this around.
RequestSendLocationEvent(nsIDOMGeoPosition* aPosition,
nsGeolocationRequest* aRequest,
Geolocation* aLocator)
nsGeolocationRequest* aRequest)
: mPosition(aPosition),
mRequest(aRequest),
mLocator(aLocator)
mRequest(aRequest)
{
}
NS_IMETHOD Run() {
mRequest->SendLocation(mPosition);
if (mLocator) {
mLocator->RemoveRequest(mRequest);
}
return NS_OK;
}
@ -303,14 +295,13 @@ nsGeolocationRequest::nsGeolocationRequest(Geolocation* aLocator,
mozilla::idl::GeoPositionOptions* aOptions,
bool aWatchPositionRequest,
int32_t aWatchId)
: mAllowed(false),
mCleared(false),
mIsWatchPositionRequest(aWatchPositionRequest),
: mIsWatchPositionRequest(aWatchPositionRequest),
mCallback(aCallback),
mErrorCallback(aErrorCallback),
mOptions(aOptions),
mLocator(aLocator),
mWatchId(aWatchId)
mWatchId(aWatchId),
mShutdown(false)
{
}
@ -347,19 +338,13 @@ nsGeolocationRequest::NotifyError(int16_t errorCode)
NS_IMETHODIMP
nsGeolocationRequest::Notify(nsITimer* aTimer)
{
if (mCleared) {
return NS_OK;
}
// If we haven't gotten an answer from the geolocation
// provider yet, fire a TIMEOUT error and reset the timer.
if (!mIsWatchPositionRequest) {
mLocator->RemoveRequest(this);
}
MOZ_ASSERT(!mShutdown, "timeout after shutdown");
NotifyError(nsIDOMGeoPositionError::TIMEOUT);
if (mIsWatchPositionRequest) {
if (!mIsWatchPositionRequest) {
Shutdown();
mLocator->RemoveRequest(this);
} else if (!mShutdown) {
SetTimeoutTimer();
}
@ -413,9 +398,6 @@ nsGeolocationRequest::GetElement(nsIDOMElement * *aRequestingElement)
NS_IMETHODIMP
nsGeolocationRequest::Cancel()
{
// remove ourselves from the locators callback lists.
mLocator->RemoveRequest(this);
NotifyError(nsIDOMGeoPositionError::PERMISSION_DENIED);
return NS_OK;
}
@ -460,22 +442,25 @@ nsGeolocationRequest::Allow()
}
gs->SetHigherAccuracy(mOptions && mOptions->enableHighAccuracy);
if (lastPosition && maximumAge > 0 &&
( PRTime(PR_Now() / PR_USEC_PER_MSEC) - maximumAge <=
PRTime(cachedPositionTime) )) {
bool canUseCache = lastPosition && maximumAge > 0 &&
(PRTime(PR_Now() / PR_USEC_PER_MSEC) - maximumAge <=
PRTime(cachedPositionTime));
if (canUseCache) {
// okay, we can return a cached position
mAllowed = true;
// getCurrentPosition requests serviced by the cache
// will now be owned by the RequestSendLocationEvent
Update(lastPosition);
}
nsCOMPtr<nsIRunnable> ev =
new RequestSendLocationEvent(
lastPosition, this, mIsWatchPositionRequest ? nullptr : mLocator);
NS_DispatchToMainThread(ev);
if (mIsWatchPositionRequest || !canUseCache) {
// let the locator know we're pending
// we will now be owned by the locator
mLocator->NotifyAllowedRequest(this);
}
SetTimeoutTimer();
mAllowed = true;
return NS_OK;
}
@ -501,28 +486,14 @@ nsGeolocationRequest::SetTimeoutTimer()
}
}
void
nsGeolocationRequest::MarkCleared()
{
if (mTimeoutTimer) {
mTimeoutTimer->Cancel();
mTimeoutTimer = nullptr;
}
mCleared = true;
}
void
nsGeolocationRequest::SendLocation(nsIDOMGeoPosition* aPosition)
{
if (mCleared || !mAllowed) {
if (mShutdown) {
// Ignore SendLocationEvents issued before we were cleared.
return;
}
if (mTimeoutTimer) {
mTimeoutTimer->Cancel();
mTimeoutTimer = nullptr;
}
nsRefPtr<Position> wrapped, cachedWrapper = mLocator->GetCachedPosition();
if (cachedWrapper && aPosition == cachedWrapper->GetWrappedGeoPosition()) {
wrapped = cachedWrapper;
@ -558,7 +529,9 @@ nsGeolocationRequest::SendLocation(nsIDOMGeoPosition* aPosition)
callback->HandleEvent(aPosition);
}
if (mIsWatchPositionRequest) {
if (!mIsWatchPositionRequest) {
Shutdown();
} else if (!mShutdown) { // The handler may have called clearWatch
SetTimeoutTimer();
}
}
@ -572,28 +545,23 @@ nsGeolocationRequest::GetPrincipal()
return mLocator->GetPrincipal();
}
bool
void
nsGeolocationRequest::Update(nsIDOMGeoPosition* aPosition)
{
if (!mAllowed) {
return false;
}
nsCOMPtr<nsIRunnable> ev = new RequestSendLocationEvent(aPosition,
this,
mIsWatchPositionRequest ? nullptr : mLocator);
nsCOMPtr<nsIRunnable> ev = new RequestSendLocationEvent(aPosition, this);
NS_DispatchToMainThread(ev);
return true;
}
void
nsGeolocationRequest::Shutdown()
{
MOZ_ASSERT(!mShutdown, "request shutdown twice");
mShutdown = true;
if (mTimeoutTimer) {
mTimeoutTimer->Cancel();
mTimeoutTimer = nullptr;
}
mCleared = true;
// This should happen last, to ensure that this request isn't taken into consideration
// when deciding whether existing requests still require high accuracy.
@ -1054,13 +1022,8 @@ Geolocation::Init(nsIDOMWindow* aContentDom)
void
Geolocation::Shutdown()
{
// Shutdown and release all callbacks
for (uint32_t i = 0; i< mPendingCallbacks.Length(); i++)
mPendingCallbacks[i]->Shutdown();
// Release all callbacks
mPendingCallbacks.Clear();
for (uint32_t i = 0; i< mWatchingCallbacks.Length(); i++)
mWatchingCallbacks[i]->Shutdown();
mWatchingCallbacks.Clear();
if (mService) {
@ -1080,28 +1043,20 @@ Geolocation::GetParentObject() const {
bool
Geolocation::HasActiveCallbacks()
{
for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) {
if (mWatchingCallbacks[i]->IsActive()) {
return true;
}
}
return mPendingCallbacks.Length() != 0;
return mPendingCallbacks.Length() || mWatchingCallbacks.Length();
}
bool
Geolocation::HighAccuracyRequested()
{
for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) {
if (mWatchingCallbacks[i]->IsActive() &&
mWatchingCallbacks[i]->WantsHighAccuracy()) {
if (mWatchingCallbacks[i]->WantsHighAccuracy()) {
return true;
}
}
for (uint32_t i = 0; i < mPendingCallbacks.Length(); i++) {
if (mPendingCallbacks[i]->IsActive() &&
mPendingCallbacks[i]->WantsHighAccuracy()) {
if (mPendingCallbacks[i]->WantsHighAccuracy()) {
return true;
}
}
@ -1112,15 +1067,13 @@ Geolocation::HighAccuracyRequested()
void
Geolocation::RemoveRequest(nsGeolocationRequest* aRequest)
{
mPendingCallbacks.RemoveElement(aRequest);
bool requestWasKnown =
(mPendingCallbacks.RemoveElement(aRequest) !=
mWatchingCallbacks.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".
aRequest->MarkCleared();
// request must have been in one of the lists
MOZ_ASSERT(requestWasKnown);
unused << requestWasKnown;
}
void
@ -1130,10 +1083,9 @@ Geolocation::Update(nsIDOMGeoPosition *aSomewhere)
return Shutdown();
}
for (uint32_t i = mPendingCallbacks.Length(); i> 0; i--) {
if (mPendingCallbacks[i-1]->Update(aSomewhere)) {
mPendingCallbacks.RemoveElementAt(i-1);
}
for (uint32_t i = mPendingCallbacks.Length(); i > 0; i--) {
mPendingCallbacks[i-1]->Update(aSomewhere);
RemoveRequest(mPendingCallbacks[i-1]);
}
// notify everyone that is watching
@ -1212,8 +1164,6 @@ Geolocation::GetCurrentPosition(GeoPositionCallback& callback,
return NS_ERROR_FAILURE;
}
mPendingCallbacks.AppendElement(request);
if (sGeoInitPending) {
PendingRequest req = { request, PendingRequest::GetCurrentPosition };
mPendingRequests.AppendElement(req);
@ -1301,10 +1251,6 @@ Geolocation::WatchPosition(GeoPositionCallback& aCallback,
if (!sGeoEnabled) {
nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(false, request);
// need to hand back an index/reference.
mWatchingCallbacks.AppendElement(request);
NS_DispatchToMainThread(ev);
return NS_OK;
}
@ -1313,8 +1259,6 @@ Geolocation::WatchPosition(GeoPositionCallback& aCallback,
return NS_ERROR_FAILURE;
}
mWatchingCallbacks.AppendElement(request);
if (sGeoInitPending) {
PendingRequest req = { request, PendingRequest::WatchPosition };
mPendingRequests.AppendElement(req);
@ -1352,7 +1296,8 @@ Geolocation::ClearWatch(int32_t aWatchId)
for (uint32_t i = 0, length = mWatchingCallbacks.Length(); i < length; ++i) {
if (mWatchingCallbacks[i]->WatchId() == aWatchId) {
mWatchingCallbacks[i]->MarkCleared();
mWatchingCallbacks[i]->Shutdown();
RemoveRequest(mWatchingCallbacks[i]);
break;
}
}
@ -1406,6 +1351,16 @@ Geolocation::WindowOwnerStillExists()
return true;
}
void
Geolocation::NotifyAllowedRequest(nsGeolocationRequest* aRequest)
{
if (aRequest->IsWatch()) {
mWatchingCallbacks.AppendElement(aRequest);
} else {
mPendingCallbacks.AppendElement(aRequest);
}
}
bool
Geolocation::RegisterRequestWithPrompt(nsGeolocationRequest* request)
{

View File

@ -72,13 +72,10 @@ class nsGeolocationRequest
void Shutdown();
// Called by the geolocation device to notify that a location has changed.
bool Update(nsIDOMGeoPosition* aPosition);
void Update(nsIDOMGeoPosition* aPosition);
void SendLocation(nsIDOMGeoPosition* location);
void MarkCleared();
bool WantsHighAccuracy() {return mOptions && mOptions->enableHighAccuracy;}
bool IsActive() {return !mCleared;}
bool Allowed() {return mAllowed;}
void SetTimeoutTimer();
nsIPrincipal* GetPrincipal();
@ -87,12 +84,11 @@ class nsGeolocationRequest
virtual bool Recv__delete__(const bool& allow) MOZ_OVERRIDE;
virtual void IPDLRelease() MOZ_OVERRIDE { Release(); }
bool IsWatch() { return mIsWatchPositionRequest; }
int32_t WatchId() { return mWatchId; }
private:
void NotifyError(int16_t errorCode);
bool mAllowed;
bool mCleared;
bool mIsWatchPositionRequest;
nsCOMPtr<nsITimer> mTimeoutTimer;
@ -103,6 +99,7 @@ class nsGeolocationRequest
nsRefPtr<mozilla::dom::Geolocation> mLocator;
int32_t mWatchId;
bool mShutdown;
};
/**
@ -208,6 +205,9 @@ public:
// Returns true if any of the callbacks are repeating
bool HasActiveCallbacks();
// Register an allowed request
void NotifyAllowedRequest(nsGeolocationRequest* aRequest);
// Remove request from all callbacks arrays
void RemoveRequest(nsGeolocationRequest* request);
@ -245,7 +245,8 @@ private:
// 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.
// there is a page change. All requests held by either array are active, that
// is, they have been allowed and expect to be fulfilled.
nsTArray<nsRefPtr<nsGeolocationRequest> > mPendingCallbacks;
nsTArray<nsRefPtr<nsGeolocationRequest> > mWatchingCallbacks;