Bug 832411 - Track geolocation requests' use of high accuracy and ensure the providers are update when necessary. r=dougt
authorJosh Matthews <josh@joshmatthews.net>
Tue, 26 Feb 2013 12:27:31 -0500
changeset 123041 6e195e29b4d6b35993e977a37658386b1fc8157d
parent 123040 f613612cb05c3cc8a09524797c689f428aac028b
child 123042 c862b0b9a898fca9549bbd778961ea639dd2f13e
push id24372
push useremorley@mozilla.com
push dateWed, 27 Feb 2013 13:22:59 +0000
treeherdermozilla-central@0a91da5f5eab [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdougt
bugs832411
milestone22.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 832411 - Track geolocation requests' use of high accuracy and ensure the providers are update when necessary. r=dougt
dom/interfaces/geolocation/Makefile.in
dom/interfaces/geolocation/nsIGeolocation.idl
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
dom/src/geolocation/Makefile.in
dom/src/geolocation/nsGeolocation.cpp
dom/src/geolocation/nsGeolocation.h
dom/tests/unit/test_geo_provider_accuracy.js
dom/tests/unit/test_geo_provider_accuracy_wrap.js
dom/tests/unit/test_geolocation_provider.js
dom/tests/unit/xpcshell.ini
--- a/dom/interfaces/geolocation/Makefile.in
+++ b/dom/interfaces/geolocation/Makefile.in
@@ -17,15 +17,16 @@ GRE_MODULE     = 1
 XPIDLSRCS =                                    \
             nsIDOMGeoGeolocation.idl           \
             nsIDOMGeoPosition.idl              \
             nsIDOMGeoPositionCoords.idl        \
             nsIDOMGeoPositionCallback.idl      \
             nsIDOMGeoPositionError.idl         \
             nsIDOMGeoPositionErrorCallback.idl \
             nsIDOMNavigatorGeolocation.idl     \
+            nsIGeolocation.idl \
             $(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
 XPIDL_FLAGS += \
   -I$(topsrcdir)/dom/interfaces/base \
   $(NULL)
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/geolocation/nsIGeolocation.idl
@@ -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);
+};
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -120,16 +120,17 @@ static const char* sClipboardTextFlavors
 using base::ChildPrivileges;
 using base::KillProcess;
 using namespace mozilla::dom::bluetooth;
 using namespace mozilla::dom::devicestorage;
 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;
 
 namespace mozilla {
 namespace dom {
 
 #define NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC "ipc:network:set-offline"
@@ -2278,18 +2279,34 @@ ContentParent::RecvFilePathUpdateNotify(
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (!obs) {
         return false;
     }
     obs->NotifyObservers(dsf, "file-watcher-update", NS_ConvertASCIItoUTF16(aReason).get());
     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) {
     nsIPrincipal* principal = aPrincipal;
     if (principal == nullptr) {
       KillHard();
       return true;
     }
@@ -2331,27 +2348,17 @@ ContentParent::RecvAddGeolocationListene
       return true;
     }
   }
 #endif
 
   // 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;
 }
 
 bool
 ContentParent::RecvRemoveGeolocationListener()
 {
   if (mGeolocationWatchID != -1) {
     nsCOMPtr<nsIDOMGeoGeolocation> geo = do_GetService("@mozilla.org/geolocation;1");
@@ -2362,22 +2369,23 @@ ContentParent::RecvRemoveGeolocationList
     mGeolocationWatchID = -1;
   }
   return true;
 }
 
 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
 ContentParent::HandleEvent(nsIDOMGeoPosition* postion)
 {
   unused << SendGeolocationUpdate(GeoPosition(postion));
   return NS_OK;
 }
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -334,17 +334,18 @@ private:
                                  InfallibleTArray<nsString>* aRetvals);
     virtual bool RecvAsyncMessage(const nsString& aMsg,
                                   const ClonedMessageData& aData);
 
     virtual bool RecvFilePathUpdateNotify(const nsString& aType,
                                           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);
 
     virtual bool RecvConsoleMessage(const nsString& aMessage);
     virtual bool RecvScriptError(const nsString& aMessage,
                                  const nsString& aSourceName,
                                  const nsString& aSourceLine,
                                  const uint32_t& aLineNumber,
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -425,17 +425,17 @@ parent:
                           bool textClickable,
                           nsString cookie,
                           nsString name);
 
     PExternalHelperApp(OptionalURIParams uri, nsCString aMimeContentType,
                        nsCString aContentDisposition, bool aForceSave,
                        int64_t aContentLength, OptionalURIParams aReferrer);
 
-    AddGeolocationListener(Principal principal);
+    AddGeolocationListener(Principal principal, bool highAccuracy);
     RemoveGeolocationListener();
     SetGeolocationHigherAccuracy(bool enable);
 
     ConsoleMessage(nsString message);
     ScriptError(nsString message, nsString sourceName, nsString sourceLine,
                 uint32_t lineNumber, uint32_t colNumber, uint32_t flags,
                 nsCString category); 
 
--- a/dom/src/geolocation/Makefile.in
+++ b/dom/src/geolocation/Makefile.in
@@ -12,17 +12,16 @@ include $(DEPTH)/config/autoconf.mk
 MODULE         = dom
 LIBRARY_NAME   = jsdomgeolocation_s
 LIBXUL_LIBRARY = 1
 
 # we don't want the shared lib, but we want to force the creation of a static lib.
 FORCE_STATIC_LIB = 1
 FAIL_ON_WARNINGS := 1
 
-
 CPPSRCS		= \
 		nsGeolocation.cpp \
 		nsGeoPosition.cpp \
 		$(NULL)
 
 LOCAL_INCLUDES = \
 		-I$(topsrcdir)/dom/base \
 		-I$(topsrcdir)/dom/ipc \
--- a/dom/src/geolocation/nsGeolocation.cpp
+++ b/dom/src/geolocation/nsGeolocation.cpp
@@ -454,20 +454,18 @@ nsGeolocationRequest::Allow()
   // -or-
   // b) the cached position time is some reasonable value to return to the user (<30s)
 
   uint32_t maximumAge = 30 * PR_MSEC_PER_SEC;
   if (mOptions) {
     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 <=
         PRTime(cachedPositionTime) )) {
     // okay, we can return a cached position
     mAllowed = true;
 
     nsCOMPtr<nsIRunnable> ev = new RequestSendLocationEvent(lastPosition,
@@ -583,30 +581,32 @@ nsGeolocationRequest::Update(nsIDOMGeoPo
   }
   NS_DispatchToMainThread(ev);
   return true;
 }
 
 void
 nsGeolocationRequest::Shutdown()
 {
-  if (mOptions && mOptions->enableHighAccuracy) {
-    nsRefPtr<nsGeolocationService> gs = nsGeolocationService::GetGeolocationService();
-    if (gs) {
-      gs->SetHigherAccuracy(false);
-    }
-  }
-
   if (mTimeoutTimer) {
     mTimeoutTimer->Cancel();
     mTimeoutTimer = nullptr;
   }
   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)
 {
   if (allow) {
     (void) Allow();
   } else {
     (void) Cancel();
@@ -974,17 +974,18 @@ nsGeolocationService::StartDevice(nsIPri
 
   // we do not want to keep the geolocation devices online
   // indefinitely.  Close them down after a reasonable period of
   // inactivivity
   SetDisconnectTimer();
 
   if (XRE_GetProcessType() == GeckoProcessType_Content) {
     ContentChild* cpc = ContentChild::GetSingleton();
-    cpc->SendAddGeolocationListener(IPC::Principal(aPrincipal));
+    cpc->SendAddGeolocationListener(IPC::Principal(aPrincipal),
+                                    HighAccuracyRequested());
     return NS_OK;
   }
 
   // Start them up!
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (!obs) {
     return NS_ERROR_FAILURE;
   }
@@ -1009,38 +1010,51 @@ nsGeolocationService::SetDisconnectTimer
     mDisconnectTimer->Cancel();
   }
 
   mDisconnectTimer->Init(this,
                          sProviderTimeout,
                          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
 nsGeolocationService::StopDevice()
 {
   if(mDisconnectTimer) {
     mDisconnectTimer->Cancel();
     mDisconnectTimer = nullptr;
@@ -1101,16 +1115,17 @@ nsGeolocationService::RemoveLocator(nsGe
 // nsGeolocation
 ////////////////////////////////////////////////////
 
 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
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGeolocation)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGeolocation)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGeolocation)
   tmp->mPendingRequests.Clear();
@@ -1205,16 +1220,36 @@ nsGeolocation::HasActiveCallbacks()
     if (mWatchingCallbacks[i]->IsActive()) {
       return true;
     }
   }
 
   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)
 {
   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
   // ever called WatchPosition() and we do not want to mess
@@ -1251,17 +1286,17 @@ nsGeolocation::GetCurrentPosition(nsIDOM
 {
   nsresult rv;
   nsAutoPtr<mozilla::idl::GeoPositionOptions> options(
       OptionsFromJSOptions(cx, jsoptions, &rv));
   NS_ENSURE_SUCCESS(rv, rv);
   return GetCurrentPosition(callback, errorCallback, options.forget());
 }
 
-nsresult
+NS_IMETHODIMP
 nsGeolocation::GetCurrentPosition(nsIDOMGeoPositionCallback *callback,
                                   nsIDOMGeoPositionErrorCallback *errorCallback,
                                   mozilla::idl::GeoPositionOptions *options)
 {
   NS_ENSURE_ARG_POINTER(callback);
 
   if (mPendingCallbacks.Length() > MAX_GEO_REQUESTS_PER_WINDOW) {
     return NS_ERROR_NOT_AVAILABLE;
@@ -1324,17 +1359,17 @@ nsGeolocation::WatchPosition(nsIDOMGeoPo
 {
   nsresult rv;
   nsAutoPtr<mozilla::idl::GeoPositionOptions> options(
       OptionsFromJSOptions(cx, jsoptions, &rv));
   NS_ENSURE_SUCCESS(rv, rv);
   return WatchPosition(callback, errorCallback, options.forget(), _retval);
 }
 
-nsresult
+NS_IMETHODIMP
 nsGeolocation::WatchPosition(nsIDOMGeoPositionCallback *callback,
                              nsIDOMGeoPositionErrorCallback *errorCallback,
                              mozilla::idl::GeoPositionOptions *options,
                              int32_t *_retval)
 {
   NS_ENSURE_ARG_POINTER(callback);
 
   if (mWatchingCallbacks.Length() > MAX_GEO_REQUESTS_PER_WINDOW) {
--- a/dom/src/geolocation/nsGeolocation.h
+++ b/dom/src/geolocation/nsGeolocation.h
@@ -21,16 +21,17 @@
 #include "nsCycleCollectionParticipant.h"
 
 #include "nsIDOMGeoGeolocation.h"
 #include "nsIDOMGeoPosition.h"
 #include "nsIDOMGeoPositionError.h"
 #include "nsIDOMGeoPositionCallback.h"
 #include "nsIDOMGeoPositionErrorCallback.h"
 #include "nsIDOMNavigatorGeolocation.h"
+#include "nsIGeolocation.h"
 
 #include "nsPIDOMWindow.h"
 
 #include "nsIGeolocationProvider.h"
 #include "nsIContentPermissionPrompt.h"
 #include "DictionaryHelpers.h"
 #include "PCOMContentPermissionRequestChild.h"
 #include "mozilla/Attributes.h"
@@ -59,16 +60,17 @@ class nsGeolocationRequest
   void Shutdown();
 
   // Called by the geolocation device to notify that a location has changed.
   // isBetter: the accuracy is as good or better than the previous position. 
   bool Update(nsIDOMGeoPosition* aPosition, bool aIsBetter);
 
   void SendLocation(nsIDOMGeoPosition* location);
   void MarkCleared();
+  bool WantsHighAccuracy() {return mOptions && mOptions->enableHighAccuracy;}
   bool IsActive() {return !mCleared;}
   bool Allowed() {return mAllowed;}
   void SetTimeoutTimer();
   nsIPrincipal* GetPrincipal();
 
   ~nsGeolocationRequest();
 
   bool Recv__delete__(const bool& allow);
@@ -130,16 +132,17 @@ public:
   // 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);
+  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
   // case, we disable the disconnect timer.
@@ -159,24 +162,26 @@ private:
   // Current state of requests for higher accuracy
   bool mHigherAccuracy;
 };
 
 
 /**
  * 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();
 
   nsresult Init(nsIDOMWindow* contentDom=nullptr);
 
   // Called by the geolocation device to notify that a location has changed.
   void Update(nsIDOMGeoPosition* aPosition, bool aIsBetter);
 
@@ -193,27 +198,22 @@ public:
   nsIPrincipal* GetPrincipal() { return mPrincipal; }
 
   // Getter for the window that this nsGeolocation is owned by
   nsIWeakReference* GetOwner() { return mOwner; }
 
   // 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();
 
   bool RegisterRequestWithPrompt(nsGeolocationRequest* request);
 
   // Methods for the service when it's ready to process requests:
   nsresult GetCurrentPositionReady(nsGeolocationRequest* aRequest);
new file mode 100644
--- /dev/null
+++ b/dom/tests/unit/test_geo_provider_accuracy.js
@@ -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});
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/tests/unit/test_geo_provider_accuracy_wrap.js
@@ -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();
+  });
+}
\ No newline at end of file
--- a/dom/tests/unit/test_geolocation_provider.js
+++ b/dom/tests/unit/test_geolocation_provider.js
@@ -70,16 +70,17 @@ function run_test()
 
     httpserver = new HttpServer();
     httpserver.registerPathHandler("/geo", geoHandler);
     httpserver.start(4444);
 
     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);
     obs.addObserver(observer, "geolocation-device-events", false);
 
     geolocation = Cc["@mozilla.org/geolocation;1"].getService(Ci.nsIDOMGeoGeolocation);
     watchID = geolocation.watchPosition(successCallback, errorCallback);
 }
--- a/dom/tests/unit/xpcshell.ini
+++ b/dom/tests/unit/xpcshell.ini
@@ -5,11 +5,14 @@ tail =
 [test_bug319968.js]
 [test_bug465752.js]
 [test_geolocation_provider.js]
 # Bug 684962: test hangs consistently on Android
 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"