Bug 832411 - Track geolocation requests' use of high accuracy and ensure the providers are update when necessary. r=dougt

This commit is contained in:
Josh Matthews 2013-02-26 12:27:31 -05:00
parent 9aceee2108
commit 16a9bb20ba
12 changed files with 268 additions and 48 deletions

View File

@ -22,6 +22,7 @@ XPIDLSRCS = \
nsIDOMGeoPositionError.idl \
nsIDOMGeoPositionErrorCallback.idl \
nsIDOMNavigatorGeolocation.idl \
nsIGeolocation.idl \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -0,0 +1,29 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "domstubs.idl"
interface nsIDOMGeoPositionCallback;
interface nsIDOMGeoPositionErrorCallback;
[ptr] native GeoPositionOptions(mozilla::dom::GeoPositionOptions);
%{C++
namespace mozilla {
namespace dom {
class GeoPositionOptions;
}
}
%}
[scriptable, builtinclass, uuid(d8e6449f-92c8-4c6a-aa9f-fef70157ec29)]
interface nsIGeolocation : nsISupports
{
// Versions of the DOM APIs that don't require JS option values
int32_t watchPosition(in nsIDOMGeoPositionCallback callback,
in nsIDOMGeoPositionErrorCallback errorCallback,
in GeoPositionOptions options);
void getCurrentPosition(in nsIDOMGeoPositionCallback callback,
in nsIDOMGeoPositionErrorCallback errorCallback,
in GeoPositionOptions options);
};

View File

@ -125,6 +125,7 @@ using namespace mozilla::dom::indexedDB;
using namespace mozilla::dom::power;
using namespace mozilla::dom::sms;
using namespace mozilla::hal;
using namespace mozilla::idl;
using namespace mozilla::ipc;
using namespace mozilla::layers;
using namespace mozilla::net;
@ -2283,8 +2284,24 @@ ContentParent::RecvFilePathUpdateNotify(const nsString& aType, const nsString& a
return true;
}
static int32_t
AddGeolocationListener(nsIDOMGeoPositionCallback* watcher, bool highAccuracy)
{
nsCOMPtr<nsIGeolocation> geo = do_GetService("@mozilla.org/geolocation;1");
if (!geo) {
return -1;
}
GeoPositionOptions* options = new GeoPositionOptions();
options->enableHighAccuracy = highAccuracy;
int32_t retval = 1;
geo->WatchPosition(watcher, nullptr, options, &retval);
return retval;
}
bool
ContentParent::RecvAddGeolocationListener(const IPC::Principal& aPrincipal)
ContentParent::RecvAddGeolocationListener(const IPC::Principal& aPrincipal,
const bool& aHighAccuracy)
{
#ifdef MOZ_PERMISSIONS
if (Preferences::GetBool("geo.testing.ignore_ipc_principal", false) == false) {
@ -2336,17 +2353,7 @@ ContentParent::RecvAddGeolocationListener(const IPC::Principal& aPrincipal)
// To ensure no geolocation updates are skipped, we always force the
// creation of a new listener.
RecvRemoveGeolocationListener();
nsCOMPtr<nsIDOMGeoGeolocation> geo = do_GetService("@mozilla.org/geolocation;1");
if (!geo) {
return true;
}
nsRefPtr<nsGeolocation> geosvc = static_cast<nsGeolocation*>(geo.get());
nsAutoPtr<mozilla::idl::GeoPositionOptions> options(new mozilla::idl::GeoPositionOptions());
jsval null = JS::NullValue();
options->Init(nullptr, &null);
geosvc->WatchPosition(this, nullptr, options.forget(), &mGeolocationWatchID);
mGeolocationWatchID = AddGeolocationListener(this, aHighAccuracy);
return true;
}
@ -2367,12 +2374,13 @@ ContentParent::RecvRemoveGeolocationListener()
bool
ContentParent::RecvSetGeolocationHigherAccuracy(const bool& aEnable)
{
nsRefPtr<nsGeolocationService> geoSvc =
nsGeolocationService::GetGeolocationService();
if (geoSvc) {
geoSvc->SetHigherAccuracy(aEnable);
}
return true;
// This should never be called without a listener already present,
// so this check allows us to forgo securing privileges.
if (mGeolocationWatchID != -1) {
RecvRemoveGeolocationListener();
mGeolocationWatchID = AddGeolocationListener(this, aEnable);
}
return true;
}
NS_IMETHODIMP

View File

@ -339,7 +339,8 @@ private:
const nsString& aFilePath,
const nsCString& aReason);
virtual bool RecvAddGeolocationListener(const IPC::Principal& aPrincipal);
virtual bool RecvAddGeolocationListener(const IPC::Principal& aPrincipal,
const bool& aHighAccuracy);
virtual bool RecvRemoveGeolocationListener();
virtual bool RecvSetGeolocationHigherAccuracy(const bool& aEnable);

View File

@ -430,7 +430,7 @@ parent:
nsCString aContentDisposition, bool aForceSave,
int64_t aContentLength, OptionalURIParams aReferrer);
AddGeolocationListener(Principal principal);
AddGeolocationListener(Principal principal, bool highAccuracy);
RemoveGeolocationListener();
SetGeolocationHigherAccuracy(bool enable);

View File

@ -17,7 +17,6 @@ LIBXUL_LIBRARY = 1
FORCE_STATIC_LIB = 1
FAIL_ON_WARNINGS := 1
CPPSRCS = \
nsGeolocation.cpp \
nsGeoPosition.cpp \

View File

@ -459,10 +459,8 @@ nsGeolocationRequest::Allow()
if (mOptions->maximumAge >= 0) {
maximumAge = mOptions->maximumAge;
}
if (mOptions->enableHighAccuracy) {
gs->SetHigherAccuracy(true);
}
}
gs->SetHigherAccuracy(mOptions && mOptions->enableHighAccuracy);
if (lastPosition && maximumAge > 0 &&
( PRTime(PR_Now() / PR_USEC_PER_MSEC) - maximumAge <=
@ -588,13 +586,6 @@ nsGeolocationRequest::Update(nsIDOMGeoPosition* aPosition, bool aIsBetter)
void
nsGeolocationRequest::Shutdown()
{
if (mOptions && mOptions->enableHighAccuracy) {
nsRefPtr<nsGeolocationService> gs = nsGeolocationService::GetGeolocationService();
if (gs) {
gs->SetHigherAccuracy(false);
}
}
if (mTimeoutTimer) {
mTimeoutTimer->Cancel();
mTimeoutTimer = nullptr;
@ -602,6 +593,15 @@ nsGeolocationRequest::Shutdown()
mCleared = true;
mCallback = nullptr;
mErrorCallback = nullptr;
// This should happen last, to ensure that this request isn't taken into consideration
// when deciding whether existing requests still require high accuracy.
if (mOptions && mOptions->enableHighAccuracy) {
nsRefPtr<nsGeolocationService> gs = nsGeolocationService::GetGeolocationService();
if (gs) {
gs->SetHigherAccuracy(false);
}
}
}
bool nsGeolocationRequest::Recv__delete__(const bool& allow)
@ -979,7 +979,8 @@ nsGeolocationService::StartDevice(nsIPrincipal *aPrincipal, bool aRequestPrivate
if (XRE_GetProcessType() == GeckoProcessType_Content) {
ContentChild* cpc = ContentChild::GetSingleton();
cpc->SendAddGeolocationListener(IPC::Principal(aPrincipal));
cpc->SendAddGeolocationListener(IPC::Principal(aPrincipal),
HighAccuracyRequested());
return NS_OK;
}
@ -1014,28 +1015,41 @@ nsGeolocationService::SetDisconnectTimer()
nsITimer::TYPE_ONE_SHOT);
}
bool
nsGeolocationService::HighAccuracyRequested()
{
for (uint32_t i = 0; i < mGeolocators.Length(); i++) {
if (mGeolocators[i]->HighAccuracyRequested()) {
return true;
}
}
return false;
}
void
nsGeolocationService::SetHigherAccuracy(bool aEnable)
{
bool highRequired = aEnable || HighAccuracyRequested();
if (XRE_GetProcessType() == GeckoProcessType_Content) {
ContentChild* cpc = ContentChild::GetSingleton();
cpc->SendSetGeolocationHigherAccuracy(aEnable);
cpc->SendSetGeolocationHigherAccuracy(highRequired);
return;
}
if (!mHigherAccuracy && aEnable) {
if (!mHigherAccuracy && highRequired) {
for (int32_t i = 0; i < mProviders.Count(); i++) {
mProviders[i]->SetHighAccuracy(true);
}
}
if (mHigherAccuracy && !aEnable) {
if (mHigherAccuracy && !highRequired) {
for (int32_t i = 0; i < mProviders.Count(); i++) {
mProviders[i]->SetHighAccuracy(false);
}
}
mHigherAccuracy = aEnable;
mHigherAccuracy = highRequired;
}
void
@ -1106,6 +1120,7 @@ DOMCI_DATA(GeoGeolocation, nsGeolocation)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGeolocation)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMGeoGeolocation)
NS_INTERFACE_MAP_ENTRY(nsIDOMGeoGeolocation)
NS_INTERFACE_MAP_ENTRY(nsIGeolocation)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(GeoGeolocation)
NS_INTERFACE_MAP_END
@ -1210,6 +1225,26 @@ nsGeolocation::HasActiveCallbacks()
return mPendingCallbacks.Length() != 0;
}
bool
nsGeolocation::HighAccuracyRequested()
{
for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) {
if (mWatchingCallbacks[i]->IsActive() &&
mWatchingCallbacks[i]->WantsHighAccuracy()) {
return true;
}
}
for (uint32_t i = 0; i < mPendingCallbacks.Length(); i++) {
if (mPendingCallbacks[i]->IsActive() &&
mPendingCallbacks[i]->WantsHighAccuracy()) {
return true;
}
}
return false;
}
void
nsGeolocation::RemoveRequest(nsGeolocationRequest* aRequest)
{
@ -1256,7 +1291,7 @@ nsGeolocation::GetCurrentPosition(nsIDOMGeoPositionCallback *callback,
return GetCurrentPosition(callback, errorCallback, options.forget());
}
nsresult
NS_IMETHODIMP
nsGeolocation::GetCurrentPosition(nsIDOMGeoPositionCallback *callback,
nsIDOMGeoPositionErrorCallback *errorCallback,
mozilla::idl::GeoPositionOptions *options)
@ -1329,7 +1364,7 @@ nsGeolocation::WatchPosition(nsIDOMGeoPositionCallback *callback,
return WatchPosition(callback, errorCallback, options.forget(), _retval);
}
nsresult
NS_IMETHODIMP
nsGeolocation::WatchPosition(nsIDOMGeoPositionCallback *callback,
nsIDOMGeoPositionErrorCallback *errorCallback,
mozilla::idl::GeoPositionOptions *options,

View File

@ -26,6 +26,7 @@
#include "nsIDOMGeoPositionCallback.h"
#include "nsIDOMGeoPositionErrorCallback.h"
#include "nsIDOMNavigatorGeolocation.h"
#include "nsIGeolocation.h"
#include "nsPIDOMWindow.h"
@ -64,6 +65,7 @@ class nsGeolocationRequest
void SendLocation(nsIDOMGeoPosition* location);
void MarkCleared();
bool WantsHighAccuracy() {return mOptions && mOptions->enableHighAccuracy;}
bool IsActive() {return !mCleared;}
bool Allowed() {return mAllowed;}
void SetTimeoutTimer();
@ -135,6 +137,7 @@ public:
// request higher accuracy, if possible
void SetHigherAccuracy(bool aEnable);
bool HighAccuracyRequested();
private:
@ -164,14 +167,16 @@ private:
/**
* Can return a geolocation info
*/
class nsGeolocation MOZ_FINAL : public nsIDOMGeoGeolocation
class nsGeolocation MOZ_FINAL : public nsIDOMGeoGeolocation,
public nsIGeolocation
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_NSIDOMGEOGEOLOCATION
NS_DECL_NSIGEOLOCATION
NS_DECL_CYCLE_COLLECTION_CLASS(nsGeolocation)
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsGeolocation, nsIDOMGeoGeolocation)
nsGeolocation();
@ -198,17 +203,12 @@ public:
// Check to see if the widnow still exists
bool WindowOwnerStillExists();
// Check to see if any active request requires high accuracy
bool HighAccuracyRequested();
// Notification from the service:
void ServiceReady();
// Versions of the DOM APIs that don't require JS option values
nsresult WatchPosition(nsIDOMGeoPositionCallback *callback,
nsIDOMGeoPositionErrorCallback *errorCallback,
mozilla::idl::GeoPositionOptions *options,
int32_t *_retval);
nsresult GetCurrentPosition(nsIDOMGeoPositionCallback *callback,
nsIDOMGeoPositionErrorCallback *errorCallback,
mozilla::idl::GeoPositionOptions *options);
private:
~nsGeolocation();

View File

@ -0,0 +1,81 @@
const Cc = Components.classes;
const Ci = Components.interfaces;
const providerCID = Components.ID("{14aa4b81-e266-45cb-88f8-89595dece114}");
const providerContract = "@mozilla.org/geolocation/unittest/geoprovider;1";
const categoryName = "geolocation-provider";
var provider = {
QueryInterface: function eventsink_qi(iid) {
if (iid.equals(Components.interfaces.nsISupports) ||
iid.equals(Components.interfaces.nsIFactory) ||
iid.equals(Components.interfaces.nsIGeolocationProvider))
return this;
throw Components.results.NS_ERROR_NO_INTERFACE;
},
createInstance: function eventsink_ci(outer, iid) {
if (outer)
throw Components.results.NS_ERROR_NO_AGGREGATION;
return this.QueryInterface(iid);
},
lockFactory: function eventsink_lockf(lock) {
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
},
startup: function() {
},
watch: function(callback, isPrivate) {
do_execute_soon(function() {
callback.update({coords: {latitude: 42, longitude: 42}, timestamp: 0});
});
},
shutdown: function() {
},
setHighAccuracy: function(enable) {
if (enable) {
this._seenHigh = true;
} else {
this._seenNotHigh = true;
}
},
_seenHigh: false,
_seenNotHigh: false
};
let runningInParent = true;
try {
runningInParent = Components.classes["@mozilla.org/xre/runtime;1"].
getService(Components.interfaces.nsIXULRuntime).processType
== Components.interfaces.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
}
catch (e) { }
var geolocation;
function run_test()
{
do_test_pending();
if (runningInParent) {
Components.manager.nsIComponentRegistrar.registerFactory(providerCID,
"Unit test geo provider", providerContract, provider);
var catMan = Components.classes["@mozilla.org/categorymanager;1"]
.getService(Components.interfaces.nsICategoryManager);
catMan.nsICategoryManager.addCategoryEntry(categoryName, "unit test",
providerContract, false, true);
var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
prefs.setBoolPref("geo.testing.ignore_ipc_principal", true);
prefs.setBoolPref("geo.wifi.scan", false);
}
geolocation = Cc["@mozilla.org/geolocation;1"].createInstance(Ci.nsIDOMGeoGeolocation);
geolocation.getCurrentPosition(function() {
geolocation.getCurrentPosition(function() {
if (runningInParent) {
do_check_true(provider._seenNotHigh);
do_check_true(provider._seenHigh);
}
do_test_finished();
}, null, {enableHighAccuracy: false, maxAge: 0});
}, null, {enableHighAccuracy: true, maxAge: 0});
}

View File

@ -0,0 +1,62 @@
const Cc = Components.classes;
const Ci = Components.interfaces;
const providerCID = Components.ID("{14aa4b81-e266-45cb-88f8-89595dece114}");
const providerContract = "@mozilla.org/geolocation/unittest/geoprovider;1";
const categoryName = "geolocation-provider";
var provider = {
QueryInterface: function eventsink_qi(iid) {
if (iid.equals(Components.interfaces.nsISupports) ||
iid.equals(Components.interfaces.nsIFactory) ||
iid.equals(Components.interfaces.nsIGeolocationProvider))
return this;
throw Components.results.NS_ERROR_NO_INTERFACE;
},
createInstance: function eventsink_ci(outer, iid) {
if (outer)
throw Components.results.NS_ERROR_NO_AGGREGATION;
return this.QueryInterface(iid);
},
lockFactory: function eventsink_lockf(lock) {
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
},
startup: function() {
},
watch: function(callback, isPrivate) {
do_execute_soon(function() {
callback.update({coords: {latitude: 42, longitude: 42}, timestamp: 0});
});
},
shutdown: function() {
},
setHighAccuracy: function(enable) {
if (enable) {
this._seenHigh = true;
} else {
this._seenNotHigh = true;
}
},
_seenHigh: false,
_seenNotHigh: false
};
function run_test()
{
Components.manager.nsIComponentRegistrar.registerFactory(providerCID,
"Unit test geo provider", providerContract, provider);
var catMan = Components.classes["@mozilla.org/categorymanager;1"]
.getService(Components.interfaces.nsICategoryManager);
catMan.nsICategoryManager.addCategoryEntry(categoryName, "unit test",
providerContract, false, true);
var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
prefs.setBoolPref("geo.testing.ignore_ipc_principal", true);
prefs.setBoolPref("geo.wifi.scan", false);
run_test_in_child("test_geo_provider_accuracy.js", function() {
do_check_true(provider._seenNotHigh);
do_check_true(provider._seenHigh);
do_test_finished();
});
}

View File

@ -75,6 +75,7 @@ function run_test()
var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
prefs.setCharPref("geo.wifi.uri", "http://localhost:4444/geo");
prefs.setBoolPref("geo.testing.ignore_ipc_principal", true);
prefs.setBoolPref("geo.wifi.scan", false);
var obs = Cc["@mozilla.org/observer-service;1"].getService();
obs = obs.QueryInterface(Ci.nsIObserverService);

View File

@ -10,6 +10,9 @@ skip-if = os == "android"
[test_geolocation_timeout.js]
[test_geolocation_timeout_wrap.js]
skip-if = os == "mac"
[test_geo_provider_accuracy.js]
[test_geo_provider_accuracy_wrap.js]
skip-if = os == "mac"
[test_multiple_geo_listeners.js]
[test_multiple_geo_listeners_wrap.js]
skip-if = os == "mac"