Bug 914888 - Reset geolocation provider accuracy when the last high accuracy request is shut down. r=jdm
authorMichael Harrison <mjh563@yahoo.co.uk>
Tue, 15 Oct 2013 14:03:34 -0400
changeset 165670 403bbc5e127fe42d7dbd1bfdf8a897df66862480
parent 165669 b66f5da7a5e1f83f411acd8983a6642cbfa29c4f
child 165671 cf6610d2b9e7406b929471ef0dd0cc0bb89f4c84
push id428
push userbbajaj@mozilla.com
push dateTue, 28 Jan 2014 00:16:25 +0000
treeherdermozilla-release@cd72a7ff3a75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdm
bugs914888
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 914888 - Reset geolocation provider accuracy when the last high accuracy request is shut down. r=jdm
dom/src/geolocation/nsGeolocation.cpp
dom/src/geolocation/nsGeolocation.h
dom/system/NetworkGeolocationProvider.js
dom/tests/unit/test_geolocation_reset_accuracy.js
dom/tests/unit/test_geolocation_reset_accuracy_wrap.js
dom/tests/unit/xpcshell.ini
--- a/dom/src/geolocation/nsGeolocation.cpp
+++ b/dom/src/geolocation/nsGeolocation.cpp
@@ -72,17 +72,17 @@ class nsGeolocationRequest
                        const GeoPositionCallback& aCallback,
                        const GeoPositionErrorCallback& aErrorCallback,
                        PositionOptions* aOptions,
                        bool aWatchPositionRequest = false,
                        int32_t aWatchId = 0);
   void Shutdown();
 
   void SendLocation(nsIDOMGeoPosition* location);
-  bool WantsHighAccuracy() {return mOptions && mOptions->mEnableHighAccuracy;}
+  bool WantsHighAccuracy() {return !mShutdown && mOptions && mOptions->mEnableHighAccuracy;}
   void SetTimeoutTimer();
   nsIPrincipal* GetPrincipal();
 
   ~nsGeolocationRequest();
 
   virtual bool Recv__delete__(const bool& allow) MOZ_OVERRIDE;
   virtual void IPDLRelease() MOZ_OVERRIDE { Release(); }
 
@@ -457,17 +457,17 @@ nsGeolocationRequest::Allow()
   // if the user has specified a maximumAge, return a cached value.
 
   uint32_t maximumAge = 0;
   if (mOptions) {
     if (mOptions->mMaximumAge > 0) {
       maximumAge = mOptions->mMaximumAge;
     }
   }
-  gs->SetHigherAccuracy(mOptions && mOptions->mEnableHighAccuracy);
+  gs->UpdateAccuracy(WantsHighAccuracy());
 
   bool canUseCache = lastPosition && maximumAge > 0 &&
     (PRTime(PR_Now() / PR_USEC_PER_MSEC) - maximumAge <=
     PRTime(cachedPositionTime));
 
   if (canUseCache) {
     // okay, we can return a cached position
     // getCurrentPosition requests serviced by the cache
@@ -597,22 +597,22 @@ nsGeolocationRequest::Shutdown()
   MOZ_ASSERT(!mShutdown, "request shutdown twice");
   mShutdown = true;
 
   if (mTimeoutTimer) {
     mTimeoutTimer->Cancel();
     mTimeoutTimer = 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 there are no other high accuracy requests, the geolocation service will
+  // notify the provider to switch to the default accuracy.
   if (mOptions && mOptions->mEnableHighAccuracy) {
     nsRefPtr<nsGeolocationService> gs = nsGeolocationService::GetGeolocationService();
     if (gs) {
-      gs->SetHigherAccuracy(false);
+      gs->UpdateAccuracy();
     }
   }
 }
 
 bool nsGeolocationRequest::Recv__delete__(const bool& allow)
 {
   if (allow) {
     (void) Allow();
@@ -915,19 +915,19 @@ nsGeolocationService::HighAccuracyReques
     if (mGeolocators[i]->HighAccuracyRequested()) {
       return true;
     }
   }
   return false;
 }
 
 void
-nsGeolocationService::SetHigherAccuracy(bool aEnable)
+nsGeolocationService::UpdateAccuracy(bool aForceHigh)
 {
-  bool highRequired = aEnable || HighAccuracyRequested();
+  bool highRequired = aForceHigh || HighAccuracyRequested();
 
   if (XRE_GetProcessType() == GeckoProcessType_Content) {
     ContentChild* cpc = ContentChild::GetSingleton();
     cpc->SendSetGeolocationHigherAccuracy(highRequired);
     return;
   }
 
   if (!mHigherAccuracy && highRequired) {
@@ -1075,16 +1075,17 @@ void
 Geolocation::Shutdown()
 {
   // Release all callbacks
   mPendingCallbacks.Clear();
   mWatchingCallbacks.Clear();
 
   if (mService) {
     mService->RemoveLocator(this);
+    mService->UpdateAccuracy();
   }
 
   mService = nullptr;
   mPrincipal = nullptr;
 }
 
 nsIDOMWindow*
 Geolocation::GetParentObject() const {
--- a/dom/src/geolocation/nsGeolocation.h
+++ b/dom/src/geolocation/nsGeolocation.h
@@ -80,18 +80,18 @@ public:
   nsresult StartDevice(nsIPrincipal* aPrincipal);
 
   // Stop the started geolocation device (gps, nmea, etc.)
   void     StopDevice();
 
   // create, or reinitalize the callback timer
   void     SetDisconnectTimer();
 
-  // request higher accuracy, if possible
-  void     SetHigherAccuracy(bool aEnable);
+  // Update the accuracy and notify the provider if changed
+  void     UpdateAccuracy(bool aForceHigh = false);
   bool     HighAccuracyRequested();
 
 private:
 
   ~nsGeolocationService();
 
   // Disconnect timer.  When this timer expires, it clears all pending callbacks
   // and closes down the provider, unless we are watching a point, and in that
--- a/dom/system/NetworkGeolocationProvider.js
+++ b/dom/system/NetworkGeolocationProvider.js
@@ -68,16 +68,19 @@ function WifiGeoPositionProvider() {
   try {
     gUseScanning = Services.prefs.getBoolPref("geo.wifi.scan");
   } catch (e) {}
 
   this.wifiService = null;
   this.timer = null;
   this.hasSeenWiFi = false;
   this.started = false;
+  // this is only used when logging is enabled, to debug interactions with the
+  // geolocation service
+  this.highAccuracy = false;
 }
 
 WifiGeoPositionProvider.prototype = {
   classID:          Components.ID("{77DA64D3-7458-4920-9491-86CC9914F904}"),
   QueryInterface:   XPCOMUtils.generateQI([Ci.nsIGeolocationProvider,
                                            Ci.nsIWifiListener,
                                            Ci.nsITimerCallback]),
   startup:  function() {
@@ -127,20 +130,22 @@ WifiGeoPositionProvider.prototype = {
       this.timer.cancel();
       this.timer = null;
     }
 
     this.started = false;
   },
 
   setHighAccuracy: function(enable) {
+    this.highAccuracy = enable;
+    LOG("setting highAccuracy to " + (this.highAccuracy?"TRUE":"FALSE"));
   },
 
   onChange: function(accessPoints) {
-    LOG("onChange called");
+    LOG("onChange called, highAccuracy = " + (this.highAccuracy?"TRUE":"FALSE"));
     this.hasSeenWiFi = true;
 
     let url = Services.urlFormatter.formatURLPref("geo.wifi.uri");
 
     function isPublic(ap) {
         let mask = "_nomap"
         let result = ap.ssid.indexOf(mask, ap.ssid.length - mask.length) == -1;
         if (result != -1) {
new file mode 100644
--- /dev/null
+++ b/dom/tests/unit/test_geolocation_reset_accuracy.js
@@ -0,0 +1,104 @@
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+const providerCID = Components.ID("{14aa4b81-e266-45cb-88f8-89595dece114}");
+const providerContract = "@mozilla.org/geolocation/provider;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() {
+  },
+  shutdown: function() {
+  },
+  setHighAccuracy: function(enable) {
+    this._isHigh = enable;
+    if (enable) {
+      this._seenHigh = true;
+    }
+  },
+  _isHigh: false,
+  _seenHigh: 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) { }
+
+function successCallback()
+{
+  do_check_true(false);
+  do_test_finished();
+}
+
+function errorCallback()
+{
+  do_check_true(false);
+  do_test_finished();
+}
+
+function run_test()
+{
+  if (runningInParent) {
+    // XPCShell does not get a profile by default. The geolocation service
+    // depends on the settings service which uses IndexedDB and IndexedDB
+    // needs a place where it can store databases.
+    do_get_profile();
+
+    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);
+  }
+
+  let geolocation = Cc["@mozilla.org/geolocation;1"].createInstance(Ci.nsISupports);
+
+  do_test_pending();
+
+  let watchID1 = geolocation.watchPosition(successCallback, errorCallback);
+  let watchID2 = geolocation.watchPosition(successCallback, errorCallback,
+                                           {enableHighAccuracy: true});
+
+  do_timeout(1000, function() {
+    geolocation.clearWatch(watchID2);
+    do_timeout(1000, check_results);
+  });
+}
+
+function check_results()
+{
+  if (runningInParent) {
+    // check the provider was set to high accuracy during the test
+    do_check_true(provider._seenHigh);
+    // check the provider is not currently set to high accuracy
+    do_check_false(provider._isHigh);
+  }
+  do_test_finished();
+}
new file mode 100644
--- /dev/null
+++ b/dom/tests/unit/test_geolocation_reset_accuracy_wrap.js
@@ -0,0 +1,70 @@
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+const providerCID = Components.ID("{14aa4b81-e266-45cb-88f8-89595dece114}");
+const providerContract = "@mozilla.org/geolocation/provider;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() {
+  },
+  shutdown: function() {
+  },
+  setHighAccuracy: function(enable) {
+    this._isHigh = enable;
+    if (enable) {
+      this._seenHigh = true;
+    }
+  },
+  _isHigh: false,
+  _seenHigh: false
+};
+
+function run_test()
+{
+  // XPCShell does not get a profile by default. The geolocation service
+  // depends on the settings service which uses IndexedDB and IndexedDB
+  // needs a place where it can store databases.
+  do_get_profile();
+
+  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_geolocation_reset_accuracy.js", check_results);
+}
+
+function check_results()
+{
+  // check the provider was set to high accuracy during the test
+  do_check_true(provider._seenHigh);
+  // check the provider is not currently set to high accuracy
+  do_check_false(provider._isHigh);
+
+  do_test_finished();
+}
--- a/dom/tests/unit/xpcshell.ini
+++ b/dom/tests/unit/xpcshell.ini
@@ -7,8 +7,13 @@ tail =
 [test_geolocation_provider.js]
 # Bug 684962: test hangs consistently on Android
 skip-if = os == "android"
 [test_geolocation_timeout.js]
 # Bug 919946: test hangs consistently on Android
 skip-if = os == "android"
 [test_geolocation_timeout_wrap.js]
 skip-if = os == "mac" || os == "android"
+[test_geolocation_reset_accuracy.js]
+# Bug 919946: test hangs consistently on Android
+skip-if = os == "android"
+[test_geolocation_reset_accuracy_wrap.js]
+skip-if = os == "mac" || os == "android"