summaryrefslogtreecommitdiff
path: root/dom/geolocation
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /dom/geolocation
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloaduxp-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/geolocation')
-rw-r--r--dom/geolocation/MLSFallback.cpp82
-rw-r--r--dom/geolocation/MLSFallback.h48
-rw-r--r--dom/geolocation/moz.build50
-rw-r--r--dom/geolocation/nsGeoPosition.cpp263
-rw-r--r--dom/geolocation/nsGeoPosition.h145
-rw-r--r--dom/geolocation/nsGeoPositionIPCSerialiser.h152
-rw-r--r--dom/geolocation/nsGeolocation.cpp1432
-rw-r--r--dom/geolocation/nsGeolocation.h260
8 files changed, 2432 insertions, 0 deletions
diff --git a/dom/geolocation/MLSFallback.cpp b/dom/geolocation/MLSFallback.cpp
new file mode 100644
index 0000000000..6cd355ed74
--- /dev/null
+++ b/dom/geolocation/MLSFallback.cpp
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "MLSFallback.h"
+#include "nsGeoPosition.h"
+#include "nsIGeolocationProvider.h"
+#include "nsServiceManagerUtils.h"
+
+NS_IMPL_ISUPPORTS(MLSFallback, nsITimerCallback)
+
+MLSFallback::MLSFallback(uint32_t delay)
+: mDelayMs(delay)
+{
+}
+
+MLSFallback::~MLSFallback()
+{
+}
+
+nsresult
+MLSFallback::Startup(nsIGeolocationUpdate* aWatcher)
+{
+ if (mHandoffTimer || mMLSFallbackProvider) {
+ return NS_OK;
+ }
+
+ mUpdateWatcher = aWatcher;
+ nsresult rv;
+ mHandoffTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = mHandoffTimer->InitWithCallback(this, mDelayMs, nsITimer::TYPE_ONE_SHOT);
+ return rv;
+}
+
+nsresult
+MLSFallback::Shutdown()
+{
+ mUpdateWatcher = nullptr;
+
+ if (mHandoffTimer) {
+ mHandoffTimer->Cancel();
+ mHandoffTimer = nullptr;
+ }
+
+ nsresult rv = NS_OK;
+ if (mMLSFallbackProvider) {
+ rv = mMLSFallbackProvider->Shutdown();
+ mMLSFallbackProvider = nullptr;
+ }
+ return rv;
+}
+
+NS_IMETHODIMP
+MLSFallback::Notify(nsITimer* aTimer)
+{
+ return CreateMLSFallbackProvider();
+}
+
+nsresult
+MLSFallback::CreateMLSFallbackProvider()
+{
+ if (mMLSFallbackProvider || !mUpdateWatcher) {
+ return NS_OK;
+ }
+
+ nsresult rv;
+ mMLSFallbackProvider = do_CreateInstance("@mozilla.org/geolocation/mls-provider;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (mMLSFallbackProvider) {
+ rv = mMLSFallbackProvider->Startup();
+ if (NS_SUCCEEDED(rv)) {
+ mMLSFallbackProvider->Watch(mUpdateWatcher);
+ }
+ }
+ mUpdateWatcher = nullptr;
+ return rv;
+}
+
diff --git a/dom/geolocation/MLSFallback.h b/dom/geolocation/MLSFallback.h
new file mode 100644
index 0000000000..a61379297e
--- /dev/null
+++ b/dom/geolocation/MLSFallback.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "nsCOMPtr.h"
+#include "nsITimer.h"
+
+class nsIGeolocationUpdate;
+class nsIGeolocationProvider;
+
+/*
+ This class wraps the NetworkGeolocationProvider in a delayed startup.
+ It is for providing a fallback to MLS when:
+ 1) using another provider as the primary provider, and
+ 2) that primary provider may fail to return a result (i.e. the error returned
+ is indeterminate, or no error callback occurs)
+
+ The intent is that the primary provider is started, then MLSFallback
+ is started with sufficient delay that the primary provider will respond first
+ if successful (in the majority of cases).
+
+ MLS has an average response of 3s, so with the 2s default delay, a response can
+ be expected in 5s.
+
+ Telemetry is recommended to monitor that the primary provider is responding
+ first when expected to do so.
+*/
+class MLSFallback : public nsITimerCallback
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSITIMERCALLBACK
+
+ explicit MLSFallback(uint32_t delayMs = 2000);
+ nsresult Startup(nsIGeolocationUpdate* aWatcher);
+ nsresult Shutdown();
+
+private:
+ nsresult CreateMLSFallbackProvider();
+ virtual ~MLSFallback();
+ nsCOMPtr<nsITimer> mHandoffTimer;
+ nsCOMPtr<nsIGeolocationProvider> mMLSFallbackProvider;
+ nsCOMPtr<nsIGeolocationUpdate> mUpdateWatcher;
+ const uint32_t mDelayMs;
+};
+
diff --git a/dom/geolocation/moz.build b/dom/geolocation/moz.build
new file mode 100644
index 0000000000..bb3fcb583a
--- /dev/null
+++ b/dom/geolocation/moz.build
@@ -0,0 +1,50 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS += [
+ 'nsGeoPosition.h',
+ 'nsGeoPositionIPCSerialiser.h',
+]
+
+SOURCES += [
+ 'nsGeolocation.cpp',
+]
+
+UNIFIED_SOURCES += [
+ 'MLSFallback.cpp',
+ 'nsGeoPosition.cpp',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+LOCAL_INCLUDES += [
+ '/dom/base',
+ '/dom/ipc',
+]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
+ LOCAL_INCLUDES += [
+ '/dom/system/android',
+ ]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
+ LOCAL_INCLUDES += [
+ '/dom/system/gonk',
+ ]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ LOCAL_INCLUDES += [
+ '/dom/system/mac',
+ ]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+ LOCAL_INCLUDES += [
+ '/dom/system/windows',
+ ]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3'):
+ if CONFIG['MOZ_GPSD']:
+ LOCAL_INCLUDES += [
+ '/dom/system/linux',
+ ]
+ DEFINES['MOZ_GPSD'] = True
diff --git a/dom/geolocation/nsGeoPosition.cpp b/dom/geolocation/nsGeoPosition.cpp
new file mode 100644
index 0000000000..c4732457af
--- /dev/null
+++ b/dom/geolocation/nsGeoPosition.cpp
@@ -0,0 +1,263 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "nsGeoPosition.h"
+
+#include "mozilla/dom/PositionBinding.h"
+#include "mozilla/dom/CoordinatesBinding.h"
+
+////////////////////////////////////////////////////
+// nsGeoPositionCoords
+////////////////////////////////////////////////////
+nsGeoPositionCoords::nsGeoPositionCoords(double aLat, double aLong,
+ double aAlt, double aHError,
+ double aVError, double aHeading,
+ double aSpeed)
+ : mLat(aLat)
+ , mLong(aLong)
+ , mAlt(aAlt)
+ , mHError(aHError)
+ , mVError(aVError)
+ , mHeading(aHeading)
+ , mSpeed(aSpeed)
+{
+}
+
+nsGeoPositionCoords::~nsGeoPositionCoords()
+{
+}
+
+NS_INTERFACE_MAP_BEGIN(nsGeoPositionCoords)
+NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMGeoPositionCoords)
+NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPositionCoords)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(nsGeoPositionCoords)
+NS_IMPL_RELEASE(nsGeoPositionCoords)
+
+NS_IMETHODIMP
+nsGeoPositionCoords::GetLatitude(double *aLatitude)
+{
+ *aLatitude = mLat;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGeoPositionCoords::GetLongitude(double *aLongitude)
+{
+ *aLongitude = mLong;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGeoPositionCoords::GetAltitude(double *aAltitude)
+{
+ *aAltitude = mAlt;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGeoPositionCoords::GetAccuracy(double *aAccuracy)
+{
+ *aAccuracy = mHError;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGeoPositionCoords::GetAltitudeAccuracy(double *aAltitudeAccuracy)
+{
+ *aAltitudeAccuracy = mVError;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGeoPositionCoords::GetHeading(double *aHeading)
+{
+ *aHeading = mHeading;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGeoPositionCoords::GetSpeed(double *aSpeed)
+{
+ *aSpeed = mSpeed;
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////
+// nsGeoPosition
+////////////////////////////////////////////////////
+
+nsGeoPosition::nsGeoPosition(double aLat, double aLong,
+ double aAlt, double aHError,
+ double aVError, double aHeading,
+ double aSpeed, long long aTimestamp) :
+ mTimestamp(aTimestamp)
+{
+ mCoords = new nsGeoPositionCoords(aLat, aLong,
+ aAlt, aHError,
+ aVError, aHeading,
+ aSpeed);
+}
+
+nsGeoPosition::nsGeoPosition(nsIDOMGeoPositionCoords *aCoords,
+ long long aTimestamp) :
+ mTimestamp(aTimestamp),
+ mCoords(aCoords)
+{
+}
+
+nsGeoPosition::nsGeoPosition(nsIDOMGeoPositionCoords *aCoords,
+ DOMTimeStamp aTimestamp) :
+ mTimestamp(aTimestamp),
+ mCoords(aCoords)
+{
+}
+
+nsGeoPosition::~nsGeoPosition()
+{
+}
+
+NS_INTERFACE_MAP_BEGIN(nsGeoPosition)
+NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMGeoPosition)
+NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPosition)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(nsGeoPosition)
+NS_IMPL_RELEASE(nsGeoPosition)
+
+NS_IMETHODIMP
+nsGeoPosition::GetTimestamp(DOMTimeStamp* aTimestamp)
+{
+ *aTimestamp = mTimestamp;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGeoPosition::GetCoords(nsIDOMGeoPositionCoords * *aCoords)
+{
+ NS_IF_ADDREF(*aCoords = mCoords);
+ return NS_OK;
+}
+
+namespace mozilla {
+namespace dom {
+
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Position, mParent, mCoordinates)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(Position)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(Position)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Position)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+Position::Position(nsISupports* aParent, nsIDOMGeoPosition* aGeoPosition)
+ : mParent(aParent)
+ , mGeoPosition(aGeoPosition)
+{
+}
+
+Position::~Position()
+{
+}
+
+nsISupports*
+Position::GetParentObject() const
+{
+ return mParent;
+}
+
+JSObject*
+Position::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+ return PositionBinding::Wrap(aCx, this, aGivenProto);
+}
+
+Coordinates*
+Position::Coords()
+{
+ if (!mCoordinates) {
+ nsCOMPtr<nsIDOMGeoPositionCoords> coords;
+ mGeoPosition->GetCoords(getter_AddRefs(coords));
+ MOZ_ASSERT(coords, "coords should not be null");
+
+ mCoordinates = new Coordinates(this, coords);
+ }
+
+ return mCoordinates;
+}
+
+uint64_t
+Position::Timestamp() const
+{
+ uint64_t rv;
+
+ mGeoPosition->GetTimestamp(&rv);
+ return rv;
+}
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Coordinates, mPosition)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(Coordinates)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(Coordinates)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Coordinates)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+Coordinates::Coordinates(Position* aPosition, nsIDOMGeoPositionCoords* aCoords)
+ : mPosition(aPosition)
+ , mCoords(aCoords)
+{
+}
+
+Coordinates::~Coordinates()
+{
+}
+
+Position*
+Coordinates::GetParentObject() const
+{
+ return mPosition;
+}
+
+JSObject*
+Coordinates::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+ return CoordinatesBinding::Wrap(aCx, this, aGivenProto);
+}
+
+#define GENERATE_COORDS_WRAPPED_GETTER(name) \
+double \
+Coordinates::name() const \
+{ \
+ double rv; \
+ mCoords->Get##name(&rv); \
+ return rv; \
+}
+
+#define GENERATE_COORDS_WRAPPED_GETTER_NULLABLE(name) \
+Nullable<double> \
+Coordinates::Get##name() const \
+{ \
+ double rv; \
+ mCoords->Get##name(&rv); \
+ return Nullable<double>(rv); \
+}
+
+GENERATE_COORDS_WRAPPED_GETTER(Latitude)
+GENERATE_COORDS_WRAPPED_GETTER(Longitude)
+GENERATE_COORDS_WRAPPED_GETTER_NULLABLE(Altitude)
+GENERATE_COORDS_WRAPPED_GETTER(Accuracy)
+GENERATE_COORDS_WRAPPED_GETTER_NULLABLE(AltitudeAccuracy)
+GENERATE_COORDS_WRAPPED_GETTER_NULLABLE(Heading)
+GENERATE_COORDS_WRAPPED_GETTER_NULLABLE(Speed)
+
+#undef GENERATE_COORDS_WRAPPED_GETTER
+#undef GENERATE_COORDS_WRAPPED_GETTER_NULLABLE
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/geolocation/nsGeoPosition.h b/dom/geolocation/nsGeoPosition.h
new file mode 100644
index 0000000000..06b406c32d
--- /dev/null
+++ b/dom/geolocation/nsGeoPosition.h
@@ -0,0 +1,145 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef nsGeoPosition_h
+#define nsGeoPosition_h
+
+#include "nsIDOMGeoPositionCoords.h"
+#include "nsIDOMGeoPosition.h"
+#include "nsString.h"
+#include "mozilla/Attributes.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsWrapperCache.h"
+#include "mozilla/dom/Nullable.h"
+#include "js/TypeDecls.h"
+
+////////////////////////////////////////////////////
+// nsGeoPositionCoords
+////////////////////////////////////////////////////
+
+/**
+ * Simple object that holds a single point in space.
+ */
+class nsGeoPositionCoords final : public nsIDOMGeoPositionCoords
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIDOMGEOPOSITIONCOORDS
+
+ nsGeoPositionCoords(double aLat, double aLong,
+ double aAlt, double aHError,
+ double aVError, double aHeading,
+ double aSpeed);
+private:
+ ~nsGeoPositionCoords();
+ const double mLat, mLong, mAlt, mHError, mVError, mHeading, mSpeed;
+};
+
+
+////////////////////////////////////////////////////
+// nsGeoPosition
+////////////////////////////////////////////////////
+
+class nsGeoPosition final : public nsIDOMGeoPosition
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIDOMGEOPOSITION
+
+ nsGeoPosition(double aLat, double aLong,
+ double aAlt, double aHError,
+ double aVError, double aHeading,
+ double aSpeed, long long aTimestamp);
+
+
+ nsGeoPosition(nsIDOMGeoPositionCoords *aCoords,
+ long long aTimestamp);
+
+ nsGeoPosition(nsIDOMGeoPositionCoords *aCoords,
+ DOMTimeStamp aTimestamp);
+
+private:
+ ~nsGeoPosition();
+ long long mTimestamp;
+ RefPtr<nsIDOMGeoPositionCoords> mCoords;
+};
+
+////////////////////////////////////////////////////
+// WebIDL wrappers for the classes above
+////////////////////////////////////////////////////
+
+namespace mozilla {
+namespace dom {
+
+class Coordinates;
+
+class Position final : public nsISupports,
+ public nsWrapperCache
+{
+ ~Position();
+
+public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Position)
+
+public:
+ Position(nsISupports* aParent, nsIDOMGeoPosition* aGeoPosition);
+
+ nsISupports* GetParentObject() const;
+
+ virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+ Coordinates* Coords();
+
+ uint64_t Timestamp() const;
+
+ nsIDOMGeoPosition* GetWrappedGeoPosition() { return mGeoPosition; }
+
+private:
+ RefPtr<Coordinates> mCoordinates;
+ nsCOMPtr<nsISupports> mParent;
+ nsCOMPtr<nsIDOMGeoPosition> mGeoPosition;
+};
+
+class Coordinates final : public nsISupports,
+ public nsWrapperCache
+{
+ ~Coordinates();
+
+public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Coordinates)
+
+public:
+ Coordinates(Position* aPosition, nsIDOMGeoPositionCoords* aCoords);
+
+ Position* GetParentObject() const;
+
+ virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+ double Latitude() const;
+
+ double Longitude() const;
+
+ Nullable<double> GetAltitude() const;
+
+ double Accuracy() const;
+
+ Nullable<double> GetAltitudeAccuracy() const;
+
+ Nullable<double> GetHeading() const;
+
+ Nullable<double> GetSpeed() const;
+private:
+ RefPtr<Position> mPosition;
+ nsCOMPtr<nsIDOMGeoPositionCoords> mCoords;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* nsGeoPosition_h */
+
diff --git a/dom/geolocation/nsGeoPositionIPCSerialiser.h b/dom/geolocation/nsGeoPositionIPCSerialiser.h
new file mode 100644
index 0000000000..3965ef1920
--- /dev/null
+++ b/dom/geolocation/nsGeoPositionIPCSerialiser.h
@@ -0,0 +1,152 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef dom_src_geolocation_IPC_serialiser
+#define dom_src_geolocation_IPC_serialiser
+
+#include "ipc/IPCMessageUtils.h"
+#include "nsGeoPosition.h"
+#include "nsIDOMGeoPosition.h"
+
+typedef nsIDOMGeoPosition* GeoPosition;
+
+namespace IPC {
+
+template <>
+struct ParamTraits<nsIDOMGeoPositionCoords*>
+{
+ typedef nsIDOMGeoPositionCoords* paramType;
+
+ // Function to serialize a geoposition
+ static void Write(Message *aMsg, const paramType& aParam)
+ {
+ bool isNull = !aParam;
+ WriteParam(aMsg, isNull);
+ // If it is a null object, then we are done
+ if (isNull) return;
+
+ double coordData;
+
+ aParam->GetLatitude(&coordData);
+ WriteParam(aMsg, coordData);
+
+ aParam->GetLongitude(&coordData);
+ WriteParam(aMsg, coordData);
+
+ aParam->GetAltitude(&coordData);
+ WriteParam(aMsg, coordData);
+
+ aParam->GetAccuracy(&coordData);
+ WriteParam(aMsg, coordData);
+
+ aParam->GetAltitudeAccuracy(&coordData);
+ WriteParam(aMsg, coordData);
+
+ aParam->GetHeading(&coordData);
+ WriteParam(aMsg, coordData);
+
+ aParam->GetSpeed(&coordData);
+ WriteParam(aMsg, coordData);
+ }
+
+ // Function to de-serialize a geoposition
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ // Check if it is the null pointer we have transfered
+ bool isNull;
+ if (!ReadParam(aMsg, aIter, &isNull)) return false;
+
+ if (isNull) {
+ *aResult = 0;
+ return true;
+ }
+
+ double latitude;
+ double longitude;
+ double altitude;
+ double accuracy;
+ double altitudeAccuracy;
+ double heading;
+ double speed;
+
+ // It's not important to us where it fails, but rather if it fails
+ if (!( ReadParam(aMsg, aIter, &latitude )
+ && ReadParam(aMsg, aIter, &longitude )
+ && ReadParam(aMsg, aIter, &altitude )
+ && ReadParam(aMsg, aIter, &accuracy )
+ && ReadParam(aMsg, aIter, &altitudeAccuracy )
+ && ReadParam(aMsg, aIter, &heading )
+ && ReadParam(aMsg, aIter, &speed ))) return false;
+
+ // We now have all the data
+ *aResult = new nsGeoPositionCoords(latitude, /* aLat */
+ longitude, /* aLong */
+ altitude, /* aAlt */
+ accuracy, /* aHError */
+ altitudeAccuracy, /* aVError */
+ heading, /* aHeading */
+ speed /* aSpeed */
+ );
+ return true;
+
+ }
+
+};
+
+template <>
+struct ParamTraits<nsIDOMGeoPosition*>
+{
+ typedef nsIDOMGeoPosition* paramType;
+
+ // Function to serialize a geoposition
+ static void Write(Message *aMsg, const paramType& aParam)
+ {
+ bool isNull = !aParam;
+ WriteParam(aMsg, isNull);
+ // If it is a null object, then we are done
+ if (isNull) return;
+
+ DOMTimeStamp timeStamp;
+ aParam->GetTimestamp(&timeStamp);
+ WriteParam(aMsg, timeStamp);
+
+ nsCOMPtr<nsIDOMGeoPositionCoords> coords;
+ aParam->GetCoords(getter_AddRefs(coords));
+ WriteParam(aMsg, coords.get());
+ }
+
+ // Function to de-serialize a geoposition
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ // Check if it is the null pointer we have transfered
+ bool isNull;
+ if (!ReadParam(aMsg, aIter, &isNull)) return false;
+
+ if (isNull) {
+ *aResult = 0;
+ return true;
+ }
+
+ DOMTimeStamp timeStamp;
+ nsIDOMGeoPositionCoords* coords = nullptr;
+
+ // It's not important to us where it fails, but rather if it fails
+ if (!ReadParam(aMsg, aIter, &timeStamp) ||
+ !ReadParam(aMsg, aIter, &coords)) {
+ nsCOMPtr<nsIDOMGeoPositionCoords> tmpcoords = coords;
+ return false;
+ }
+
+ *aResult = new nsGeoPosition(coords, timeStamp);
+
+ return true;
+ };
+
+};
+
+} // namespace IPC
+
+#endif
diff --git a/dom/geolocation/nsGeolocation.cpp b/dom/geolocation/nsGeolocation.cpp
new file mode 100644
index 0000000000..9fcdb350a4
--- /dev/null
+++ b/dom/geolocation/nsGeolocation.cpp
@@ -0,0 +1,1432 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "nsXULAppAPI.h"
+
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/UniquePtr.h"
+
+#include "nsGeolocation.h"
+#include "nsDOMClassInfoID.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsContentUtils.h"
+#include "nsContentPermissionHelper.h"
+#include "nsIDocument.h"
+#include "nsIObserverService.h"
+#include "nsPIDOMWindow.h"
+#include "nsThreadUtils.h"
+#include "mozilla/Services.h"
+#include "mozilla/Unused.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/WeakPtr.h"
+#include "mozilla/dom/PermissionMessageUtils.h"
+
+class nsIPrincipal;
+
+#ifdef MOZ_WIDGET_ANDROID
+#include "AndroidLocationProvider.h"
+#endif
+
+#ifdef MOZ_WIDGET_GONK
+#include "GonkGPSGeolocationProvider.h"
+#endif
+
+#ifdef MOZ_GPSD
+#include "GpsdLocationProvider.h"
+#endif
+
+#ifdef MOZ_WIDGET_COCOA
+#include "CoreLocationLocationProvider.h"
+#endif
+
+#ifdef XP_WIN
+#include "WindowsLocationProvider.h"
+#include "mozilla/WindowsVersion.h"
+#endif
+
+// Some limit to the number of get or watch geolocation requests
+// that a window can make.
+#define MAX_GEO_REQUESTS_PER_WINDOW 1500
+
+using mozilla::Unused; // <snicker>
+using namespace mozilla;
+using namespace mozilla::dom;
+
+class nsGeolocationRequest final
+ : public nsIContentPermissionRequest
+ , public nsIGeolocationUpdate
+ , public SupportsWeakPtr<nsGeolocationRequest>
+{
+ public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_NSICONTENTPERMISSIONREQUEST
+ NS_DECL_NSIGEOLOCATIONUPDATE
+
+ NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsGeolocationRequest, nsIContentPermissionRequest)
+
+ nsGeolocationRequest(Geolocation* aLocator,
+ GeoPositionCallback aCallback,
+ GeoPositionErrorCallback aErrorCallback,
+ UniquePtr<PositionOptions>&& aOptions,
+ uint8_t aProtocolType,
+ bool aWatchPositionRequest = false,
+ int32_t aWatchId = 0);
+
+ MOZ_DECLARE_WEAKREFERENCE_TYPENAME(nsGeolocationRequest)
+
+ void Shutdown();
+
+ void SendLocation(nsIDOMGeoPosition* aLocation);
+ bool WantsHighAccuracy() {return !mShutdown && mOptions && mOptions->mEnableHighAccuracy;}
+ void SetTimeoutTimer();
+ void StopTimeoutTimer();
+ void NotifyErrorAndShutdown(uint16_t);
+ nsIPrincipal* GetPrincipal();
+
+ bool IsWatch() { return mIsWatchPositionRequest; }
+ int32_t WatchId() { return mWatchId; }
+ private:
+ virtual ~nsGeolocationRequest();
+
+ class TimerCallbackHolder final : public nsITimerCallback
+ {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSITIMERCALLBACK
+
+ explicit TimerCallbackHolder(nsGeolocationRequest* aRequest)
+ : mRequest(aRequest)
+ {}
+
+ private:
+ ~TimerCallbackHolder() {}
+ WeakPtr<nsGeolocationRequest> mRequest;
+ };
+
+ void Notify();
+
+ bool mIsWatchPositionRequest;
+
+ nsCOMPtr<nsITimer> mTimeoutTimer;
+ GeoPositionCallback mCallback;
+ GeoPositionErrorCallback mErrorCallback;
+ UniquePtr<PositionOptions> mOptions;
+
+ RefPtr<Geolocation> mLocator;
+
+ int32_t mWatchId;
+ bool mShutdown;
+ nsCOMPtr<nsIContentPermissionRequester> mRequester;
+ uint8_t mProtocolType;
+};
+
+static UniquePtr<PositionOptions>
+CreatePositionOptionsCopy(const PositionOptions& aOptions)
+{
+ UniquePtr<PositionOptions> geoOptions = MakeUnique<PositionOptions>();
+
+ geoOptions->mEnableHighAccuracy = aOptions.mEnableHighAccuracy;
+ geoOptions->mMaximumAge = aOptions.mMaximumAge;
+ geoOptions->mTimeout = aOptions.mTimeout;
+
+ return geoOptions;
+}
+
+class RequestPromptEvent : public Runnable
+{
+public:
+ RequestPromptEvent(nsGeolocationRequest* aRequest, nsWeakPtr aWindow)
+ : mRequest(aRequest)
+ , mWindow(aWindow)
+ {
+ }
+
+ NS_IMETHOD Run() override
+ {
+ nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mWindow);
+ nsContentPermissionUtils::AskPermission(mRequest, window);
+ return NS_OK;
+ }
+
+private:
+ RefPtr<nsGeolocationRequest> mRequest;
+ nsWeakPtr mWindow;
+};
+
+class RequestAllowEvent : public Runnable
+{
+public:
+ RequestAllowEvent(int allow, nsGeolocationRequest* request)
+ : mAllow(allow),
+ mRequest(request)
+ {
+ }
+
+ NS_IMETHOD Run() override {
+ if (mAllow) {
+ mRequest->Allow(JS::UndefinedHandleValue);
+ } else {
+ mRequest->Cancel();
+ }
+ return NS_OK;
+ }
+
+private:
+ bool mAllow;
+ RefPtr<nsGeolocationRequest> mRequest;
+};
+
+class RequestSendLocationEvent : public Runnable
+{
+public:
+ RequestSendLocationEvent(nsIDOMGeoPosition* aPosition,
+ nsGeolocationRequest* aRequest)
+ : mPosition(aPosition),
+ mRequest(aRequest)
+ {
+ }
+
+ NS_IMETHOD Run() override {
+ mRequest->SendLocation(mPosition);
+ return NS_OK;
+ }
+
+private:
+ nsCOMPtr<nsIDOMGeoPosition> mPosition;
+ RefPtr<nsGeolocationRequest> mRequest;
+ RefPtr<Geolocation> mLocator;
+};
+
+////////////////////////////////////////////////////
+// PositionError
+////////////////////////////////////////////////////
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PositionError)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMGeoPositionError)
+ NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPositionError)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PositionError, mParent)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(PositionError)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(PositionError)
+
+PositionError::PositionError(Geolocation* aParent, int16_t aCode)
+ : mCode(aCode)
+ , mParent(aParent)
+{
+}
+
+PositionError::~PositionError(){}
+
+
+NS_IMETHODIMP
+PositionError::GetCode(int16_t *aCode)
+{
+ NS_ENSURE_ARG_POINTER(aCode);
+ *aCode = Code();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+PositionError::GetMessage(nsAString& aMessage)
+{
+ switch (mCode)
+ {
+ case nsIDOMGeoPositionError::PERMISSION_DENIED:
+ aMessage = NS_LITERAL_STRING("User denied geolocation prompt");
+ break;
+ case nsIDOMGeoPositionError::POSITION_UNAVAILABLE:
+ aMessage = NS_LITERAL_STRING("Unknown error acquiring position");
+ break;
+ case nsIDOMGeoPositionError::TIMEOUT:
+ aMessage = NS_LITERAL_STRING("Position acquisition timed out");
+ break;
+ default:
+ break;
+ }
+ return NS_OK;
+}
+
+Geolocation*
+PositionError::GetParentObject() const
+{
+ return mParent;
+}
+
+JSObject*
+PositionError::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+ return PositionErrorBinding::Wrap(aCx, this, aGivenProto);
+}
+
+void
+PositionError::NotifyCallback(const GeoPositionErrorCallback& aCallback)
+{
+ nsAutoMicroTask mt;
+ if (aCallback.HasWebIDLCallback()) {
+ PositionErrorCallback* callback = aCallback.GetWebIDLCallback();
+
+ if (callback) {
+ callback->Call(*this);
+ }
+ } else {
+ nsIDOMGeoPositionErrorCallback* callback = aCallback.GetXPCOMCallback();
+ if (callback) {
+ callback->HandleEvent(this);
+ }
+ }
+}
+////////////////////////////////////////////////////
+// nsGeolocationRequest
+////////////////////////////////////////////////////
+
+nsGeolocationRequest::nsGeolocationRequest(Geolocation* aLocator,
+ GeoPositionCallback aCallback,
+ GeoPositionErrorCallback aErrorCallback,
+ UniquePtr<PositionOptions>&& aOptions,
+ uint8_t aProtocolType,
+ bool aWatchPositionRequest,
+ int32_t aWatchId)
+ : mIsWatchPositionRequest(aWatchPositionRequest),
+ mCallback(Move(aCallback)),
+ mErrorCallback(Move(aErrorCallback)),
+ mOptions(Move(aOptions)),
+ mLocator(aLocator),
+ mWatchId(aWatchId),
+ mShutdown(false),
+ mProtocolType(aProtocolType)
+{
+ if (nsCOMPtr<nsPIDOMWindowInner> win =
+ do_QueryReferent(mLocator->GetOwner())) {
+ mRequester = new nsContentPermissionRequester(win);
+ }
+}
+
+nsGeolocationRequest::~nsGeolocationRequest()
+{
+ StopTimeoutTimer();
+}
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGeolocationRequest)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
+ NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
+ NS_INTERFACE_MAP_ENTRY(nsIGeolocationUpdate)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGeolocationRequest)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGeolocationRequest)
+NS_IMPL_CYCLE_COLLECTION(nsGeolocationRequest, mCallback, mErrorCallback, mLocator)
+
+void
+nsGeolocationRequest::Notify()
+{
+ SetTimeoutTimer();
+ NotifyErrorAndShutdown(nsIDOMGeoPositionError::TIMEOUT);
+}
+
+void
+nsGeolocationRequest::NotifyErrorAndShutdown(uint16_t aErrorCode)
+{
+ MOZ_ASSERT(!mShutdown, "timeout after shutdown");
+ if (!mIsWatchPositionRequest) {
+ Shutdown();
+ mLocator->RemoveRequest(this);
+ }
+
+ NotifyError(aErrorCode);
+}
+
+NS_IMETHODIMP
+nsGeolocationRequest::GetPrincipal(nsIPrincipal * *aRequestingPrincipal)
+{
+ NS_ENSURE_ARG_POINTER(aRequestingPrincipal);
+
+ nsCOMPtr<nsIPrincipal> principal = mLocator->GetPrincipal();
+ principal.forget(aRequestingPrincipal);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGeolocationRequest::GetTypes(nsIArray** aTypes)
+{
+ nsTArray<nsString> emptyOptions;
+ return nsContentPermissionUtils::CreatePermissionArray(NS_LITERAL_CSTRING("geolocation"),
+ NS_LITERAL_CSTRING("unused"),
+ emptyOptions,
+ aTypes);
+}
+
+NS_IMETHODIMP
+nsGeolocationRequest::GetWindow(mozIDOMWindow** aRequestingWindow)
+{
+ NS_ENSURE_ARG_POINTER(aRequestingWindow);
+
+ nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mLocator->GetOwner());
+ window.forget(aRequestingWindow);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGeolocationRequest::GetElement(nsIDOMElement * *aRequestingElement)
+{
+ NS_ENSURE_ARG_POINTER(aRequestingElement);
+ *aRequestingElement = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGeolocationRequest::Cancel()
+{
+ if (mRequester) {
+ // Record the number of denied requests for regular web content.
+ // This method is only called when the user explicitly denies the request,
+ // and is not called when the page is simply unloaded, or similar.
+ Telemetry::Accumulate(Telemetry::GEOLOCATION_REQUEST_GRANTED, mProtocolType);
+ }
+
+ if (mLocator->ClearPendingRequest(this)) {
+ return NS_OK;
+ }
+
+ NotifyError(nsIDOMGeoPositionError::PERMISSION_DENIED);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGeolocationRequest::Allow(JS::HandleValue aChoices)
+{
+ MOZ_ASSERT(aChoices.isUndefined());
+
+ if (mRequester) {
+ // Record the number of granted requests for regular web content.
+ Telemetry::Accumulate(Telemetry::GEOLOCATION_REQUEST_GRANTED, mProtocolType + 10);
+
+ // Record whether a location callback is fulfilled while the owner window
+ // is not visible.
+ bool isVisible = false;
+ nsCOMPtr<nsPIDOMWindowInner> window = mLocator->GetParentObject();
+
+ if (window) {
+ nsCOMPtr<nsIDocument> doc = window->GetDoc();
+ isVisible = doc && !doc->Hidden();
+ }
+
+ if (IsWatch()) {
+ mozilla::Telemetry::Accumulate(mozilla::Telemetry::GEOLOCATION_WATCHPOSITION_VISIBLE, isVisible);
+ } else {
+ mozilla::Telemetry::Accumulate(mozilla::Telemetry::GEOLOCATION_GETCURRENTPOSITION_VISIBLE, isVisible);
+ }
+ }
+
+ if (mLocator->ClearPendingRequest(this)) {
+ return NS_OK;
+ }
+
+ RefPtr<nsGeolocationService> gs = nsGeolocationService::GetGeolocationService();
+
+ bool canUseCache = false;
+ CachedPositionAndAccuracy lastPosition = gs->GetCachedPosition();
+ if (lastPosition.position) {
+ DOMTimeStamp cachedPositionTime_ms;
+ lastPosition.position->GetTimestamp(&cachedPositionTime_ms);
+ // check to see if we can use a cached value
+ // if the user has specified a maximumAge, return a cached value.
+ if (mOptions && mOptions->mMaximumAge > 0) {
+ uint32_t maximumAge_ms = mOptions->mMaximumAge;
+ bool isCachedWithinRequestedAccuracy = WantsHighAccuracy() <= lastPosition.isHighAccuracy;
+ bool isCachedWithinRequestedTime =
+ DOMTimeStamp(PR_Now() / PR_USEC_PER_MSEC - maximumAge_ms) <= cachedPositionTime_ms;
+ canUseCache = isCachedWithinRequestedAccuracy && isCachedWithinRequestedTime;
+ }
+ }
+
+ gs->UpdateAccuracy(WantsHighAccuracy());
+ if (canUseCache) {
+ // okay, we can return a cached position
+ // getCurrentPosition requests serviced by the cache
+ // will now be owned by the RequestSendLocationEvent
+ Update(lastPosition.position);
+
+ // After Update is called, getCurrentPosition finishes it's job.
+ if (!mIsWatchPositionRequest) {
+ return NS_OK;
+ }
+
+ } else {
+ // if it is not a watch request and timeout is 0,
+ // invoke the errorCallback (if present) with TIMEOUT code
+ if (mOptions && mOptions->mTimeout == 0 && !mIsWatchPositionRequest) {
+ NotifyError(nsIDOMGeoPositionError::TIMEOUT);
+ return NS_OK;
+ }
+
+ }
+
+ // Kick off the geo device, if it isn't already running
+ nsresult rv = gs->StartDevice(GetPrincipal());
+
+ if (NS_FAILED(rv)) {
+ // Location provider error
+ NotifyError(nsIDOMGeoPositionError::POSITION_UNAVAILABLE);
+ return NS_OK;
+ }
+
+ if (mIsWatchPositionRequest || !canUseCache) {
+ // let the locator know we're pending
+ // we will now be owned by the locator
+ mLocator->NotifyAllowedRequest(this);
+ }
+
+ SetTimeoutTimer();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGeolocationRequest::GetRequester(nsIContentPermissionRequester** aRequester)
+{
+ NS_ENSURE_ARG_POINTER(aRequester);
+
+ nsCOMPtr<nsIContentPermissionRequester> requester = mRequester;
+ requester.forget(aRequester);
+
+ return NS_OK;
+}
+
+void
+nsGeolocationRequest::SetTimeoutTimer()
+{
+ MOZ_ASSERT(!mShutdown, "set timeout after shutdown");
+
+ StopTimeoutTimer();
+
+ if (mOptions && mOptions->mTimeout != 0 && mOptions->mTimeout != 0x7fffffff) {
+ mTimeoutTimer = do_CreateInstance("@mozilla.org/timer;1");
+ RefPtr<TimerCallbackHolder> holder = new TimerCallbackHolder(this);
+ mTimeoutTimer->InitWithCallback(holder, mOptions->mTimeout, nsITimer::TYPE_ONE_SHOT);
+ }
+}
+
+void
+nsGeolocationRequest::StopTimeoutTimer()
+{
+ if (mTimeoutTimer) {
+ mTimeoutTimer->Cancel();
+ mTimeoutTimer = nullptr;
+ }
+}
+
+void
+nsGeolocationRequest::SendLocation(nsIDOMGeoPosition* aPosition)
+{
+ if (mShutdown) {
+ // Ignore SendLocationEvents issued before we were cleared.
+ return;
+ }
+
+ if (mOptions && mOptions->mMaximumAge > 0) {
+ DOMTimeStamp positionTime_ms;
+ aPosition->GetTimestamp(&positionTime_ms);
+ const uint32_t maximumAge_ms = mOptions->mMaximumAge;
+ const bool isTooOld =
+ DOMTimeStamp(PR_Now() / PR_USEC_PER_MSEC - maximumAge_ms) > positionTime_ms;
+ if (isTooOld) {
+ return;
+ }
+ }
+
+ RefPtr<Position> wrapped;
+
+ if (aPosition) {
+ nsCOMPtr<nsIDOMGeoPositionCoords> coords;
+ aPosition->GetCoords(getter_AddRefs(coords));
+ if (coords) {
+ wrapped = new Position(ToSupports(mLocator), aPosition);
+ }
+ }
+
+ if (!wrapped) {
+ NotifyError(nsIDOMGeoPositionError::POSITION_UNAVAILABLE);
+ return;
+ }
+
+ if (!mIsWatchPositionRequest) {
+ // Cancel timer and position updates in case the position
+ // callback spins the event loop
+ Shutdown();
+ }
+
+ nsAutoMicroTask mt;
+ if (mCallback.HasWebIDLCallback()) {
+ PositionCallback* callback = mCallback.GetWebIDLCallback();
+
+ MOZ_ASSERT(callback);
+ callback->Call(*wrapped);
+ } else {
+ nsIDOMGeoPositionCallback* callback = mCallback.GetXPCOMCallback();
+ MOZ_ASSERT(callback);
+ callback->HandleEvent(aPosition);
+ }
+
+ if (mIsWatchPositionRequest && !mShutdown) {
+ SetTimeoutTimer();
+ }
+ MOZ_ASSERT(mShutdown || mIsWatchPositionRequest,
+ "non-shutdown getCurrentPosition request after callback!");
+}
+
+nsIPrincipal*
+nsGeolocationRequest::GetPrincipal()
+{
+ if (!mLocator) {
+ return nullptr;
+ }
+ return mLocator->GetPrincipal();
+}
+
+NS_IMETHODIMP
+nsGeolocationRequest::Update(nsIDOMGeoPosition* aPosition)
+{
+ nsCOMPtr<nsIRunnable> ev = new RequestSendLocationEvent(aPosition, this);
+ NS_DispatchToMainThread(ev);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGeolocationRequest::NotifyError(uint16_t aErrorCode)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ RefPtr<PositionError> positionError = new PositionError(mLocator, aErrorCode);
+ positionError->NotifyCallback(mErrorCallback);
+ return NS_OK;
+}
+
+void
+nsGeolocationRequest::Shutdown()
+{
+ MOZ_ASSERT(!mShutdown, "request shutdown twice");
+ mShutdown = true;
+
+ StopTimeoutTimer();
+
+ // 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) {
+ RefPtr<nsGeolocationService> gs = nsGeolocationService::GetGeolocationService();
+ if (gs) {
+ gs->UpdateAccuracy();
+ }
+ }
+}
+
+
+////////////////////////////////////////////////////
+// nsGeolocationRequest::TimerCallbackHolder
+////////////////////////////////////////////////////
+
+NS_IMPL_ISUPPORTS(nsGeolocationRequest::TimerCallbackHolder, nsISupports, nsITimerCallback)
+
+NS_IMETHODIMP
+nsGeolocationRequest::TimerCallbackHolder::Notify(nsITimer*)
+{
+ if (mRequest && mRequest->mLocator) {
+ RefPtr<nsGeolocationRequest> request(mRequest);
+ request->Notify();
+ }
+
+ return NS_OK;
+}
+
+
+////////////////////////////////////////////////////
+// nsGeolocationService
+////////////////////////////////////////////////////
+NS_INTERFACE_MAP_BEGIN(nsGeolocationService)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIGeolocationUpdate)
+ NS_INTERFACE_MAP_ENTRY(nsIGeolocationUpdate)
+ NS_INTERFACE_MAP_ENTRY(nsIObserver)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(nsGeolocationService)
+NS_IMPL_RELEASE(nsGeolocationService)
+
+
+static bool sGeoEnabled = true;
+static int32_t sProviderTimeout = 6000; // Time, in milliseconds, to wait for the location provider to spin up.
+
+nsresult nsGeolocationService::Init()
+{
+ Preferences::AddIntVarCache(&sProviderTimeout, "geo.timeout", sProviderTimeout);
+ Preferences::AddBoolVarCache(&sGeoEnabled, "geo.enabled", sGeoEnabled);
+
+ if (!sGeoEnabled) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (XRE_IsContentProcess()) {
+ return NS_OK;
+ }
+
+ // geolocation service can be enabled -> now register observer
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ if (!obs) {
+ return NS_ERROR_FAILURE;
+ }
+
+ obs->AddObserver(this, "xpcom-shutdown", false);
+
+#ifdef MOZ_WIDGET_ANDROID
+ mProvider = new AndroidLocationProvider();
+#endif
+
+#ifdef MOZ_WIDGET_GONK
+ // GonkGPSGeolocationProvider can be started at boot up time for initialization reasons.
+ // do_getService gets hold of the already initialized component and starts
+ // processing location requests immediately.
+ // do_Createinstance will create multiple instances of the provider which is not right.
+ // bug 993041
+ mProvider = do_GetService(GONK_GPS_GEOLOCATION_PROVIDER_CONTRACTID);
+#endif
+
+#ifdef MOZ_WIDGET_GTK
+#ifdef MOZ_GPSD
+ if (Preferences::GetBool("geo.provider.use_gpsd", false)) {
+ mProvider = new GpsdLocationProvider();
+ }
+#endif
+#endif
+
+#ifdef MOZ_WIDGET_COCOA
+ if (Preferences::GetBool("geo.provider.use_corelocation", true)) {
+ mProvider = new CoreLocationLocationProvider();
+ }
+#endif
+
+#ifdef XP_WIN
+ if (Preferences::GetBool("geo.provider.ms-windows-location", false) &&
+ IsWin8OrLater()) {
+ mProvider = new WindowsLocationProvider();
+ }
+#endif
+
+ if (Preferences::GetBool("geo.provider.use_mls", false)) {
+ mProvider = do_CreateInstance("@mozilla.org/geolocation/mls-provider;1");
+ }
+
+ // Override platform-specific providers with the default (network)
+ // provider while testing. Our tests are currently not meant to exercise
+ // the provider, and some tests rely on the network provider being used.
+ // "geo.provider.testing" is always set for all plain and browser chrome
+ // mochitests, and also for xpcshell tests.
+ if (!mProvider || Preferences::GetBool("geo.provider.testing", false)) {
+ nsCOMPtr<nsIGeolocationProvider> override =
+ do_GetService(NS_GEOLOCATION_PROVIDER_CONTRACTID);
+
+ if (override) {
+ mProvider = override;
+ }
+ }
+
+ return NS_OK;
+}
+
+nsGeolocationService::~nsGeolocationService()
+{
+}
+
+NS_IMETHODIMP
+nsGeolocationService::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aData)
+{
+ if (!strcmp("xpcom-shutdown", aTopic)) {
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ if (obs) {
+ obs->RemoveObserver(this, "xpcom-shutdown");
+ }
+
+ for (uint32_t i = 0; i< mGeolocators.Length(); i++) {
+ mGeolocators[i]->Shutdown();
+ }
+ StopDevice();
+
+ return NS_OK;
+ }
+
+ if (!strcmp("timer-callback", aTopic)) {
+ // decide if we can close down the service.
+ for (uint32_t i = 0; i< mGeolocators.Length(); i++)
+ if (mGeolocators[i]->HasActiveCallbacks()) {
+ SetDisconnectTimer();
+ return NS_OK;
+ }
+
+ // okay to close up.
+ StopDevice();
+ Update(nullptr);
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsGeolocationService::Update(nsIDOMGeoPosition *aSomewhere)
+{
+ if (aSomewhere) {
+ SetCachedPosition(aSomewhere);
+ }
+
+ for (uint32_t i = 0; i< mGeolocators.Length(); i++) {
+ mGeolocators[i]->Update(aSomewhere);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGeolocationService::NotifyError(uint16_t aErrorCode)
+{
+ for (uint32_t i = 0; i < mGeolocators.Length(); i++) {
+ mGeolocators[i]->NotifyError(aErrorCode);
+ }
+ return NS_OK;
+}
+
+void
+nsGeolocationService::SetCachedPosition(nsIDOMGeoPosition* aPosition)
+{
+ mLastPosition.position = aPosition;
+ mLastPosition.isHighAccuracy = mHigherAccuracy;
+}
+
+CachedPositionAndAccuracy
+nsGeolocationService::GetCachedPosition()
+{
+ return mLastPosition;
+}
+
+nsresult
+nsGeolocationService::StartDevice(nsIPrincipal *aPrincipal)
+{
+ if (!sGeoEnabled) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ // We do not want to keep the geolocation devices online
+ // indefinitely.
+ // Close them down after a reasonable period of inactivivity.
+ SetDisconnectTimer();
+
+ if (XRE_IsContentProcess()) {
+ ContentChild* cpc = ContentChild::GetSingleton();
+ cpc->SendAddGeolocationListener(IPC::Principal(aPrincipal),
+ HighAccuracyRequested());
+ return NS_OK;
+ }
+
+ // Start them up!
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ if (!obs) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!mProvider) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv;
+
+ if (NS_FAILED(rv = mProvider->Startup()) ||
+ NS_FAILED(rv = mProvider->Watch(this))) {
+
+ NotifyError(nsIDOMGeoPositionError::POSITION_UNAVAILABLE);
+ return rv;
+ }
+
+ obs->NotifyObservers(mProvider,
+ "geolocation-device-events",
+ u"starting");
+
+ return NS_OK;
+}
+
+void
+nsGeolocationService::SetDisconnectTimer()
+{
+ if (!mDisconnectTimer) {
+ mDisconnectTimer = do_CreateInstance("@mozilla.org/timer;1");
+ } else {
+ 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::UpdateAccuracy(bool aForceHigh)
+{
+ bool highRequired = aForceHigh || HighAccuracyRequested();
+
+ if (XRE_IsContentProcess()) {
+ ContentChild* cpc = ContentChild::GetSingleton();
+ if (cpc->IsAlive()) {
+ cpc->SendSetGeolocationHigherAccuracy(highRequired);
+ }
+
+ return;
+ }
+
+ mProvider->SetHighAccuracy(!mHigherAccuracy && highRequired);
+ mHigherAccuracy = highRequired;
+}
+
+void
+nsGeolocationService::StopDevice()
+{
+ if (mDisconnectTimer) {
+ mDisconnectTimer->Cancel();
+ mDisconnectTimer = nullptr;
+ }
+
+ if (XRE_IsContentProcess()) {
+ ContentChild* cpc = ContentChild::GetSingleton();
+ cpc->SendRemoveGeolocationListener();
+
+ return; // bail early
+ }
+
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ if (!obs) {
+ return;
+ }
+
+ if (!mProvider) {
+ return;
+ }
+
+ mHigherAccuracy = false;
+
+ mProvider->Shutdown();
+ obs->NotifyObservers(mProvider,
+ "geolocation-device-events",
+ u"shutdown");
+}
+
+StaticRefPtr<nsGeolocationService> nsGeolocationService::sService;
+
+already_AddRefed<nsGeolocationService>
+nsGeolocationService::GetGeolocationService()
+{
+ RefPtr<nsGeolocationService> result;
+ if (nsGeolocationService::sService) {
+ result = nsGeolocationService::sService;
+
+ return result.forget();
+ }
+
+ result = new nsGeolocationService();
+ if (NS_FAILED(result->Init())) {
+ return nullptr;
+ }
+
+ ClearOnShutdown(&nsGeolocationService::sService);
+ nsGeolocationService::sService = result;
+ return result.forget();
+}
+
+void
+nsGeolocationService::AddLocator(Geolocation* aLocator)
+{
+ mGeolocators.AppendElement(aLocator);
+}
+
+void
+nsGeolocationService::RemoveLocator(Geolocation* aLocator)
+{
+ mGeolocators.RemoveElement(aLocator);
+}
+
+////////////////////////////////////////////////////
+// Geolocation
+////////////////////////////////////////////////////
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Geolocation)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMGeoGeolocation)
+ NS_INTERFACE_MAP_ENTRY(nsIDOMGeoGeolocation)
+ NS_INTERFACE_MAP_ENTRY(nsIGeolocationUpdate)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(Geolocation)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(Geolocation)
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Geolocation,
+ mPendingCallbacks,
+ mWatchingCallbacks,
+ mPendingRequests)
+
+Geolocation::Geolocation()
+: mProtocolType(ProtocolType::OTHER)
+, mLastWatchId(0)
+{
+}
+
+Geolocation::~Geolocation()
+{
+ if (mService) {
+ Shutdown();
+ }
+}
+
+nsresult
+Geolocation::Init(nsPIDOMWindowInner* aContentDom)
+{
+ // Remember the window
+ if (aContentDom) {
+ mOwner = do_GetWeakReference(aContentDom);
+ if (!mOwner) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Grab the principal of the document
+ nsCOMPtr<nsIDocument> doc = aContentDom->GetDoc();
+ if (!doc) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mPrincipal = doc->NodePrincipal();
+
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = mPrincipal->GetURI(getter_AddRefs(uri));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (uri) {
+ bool isHttp;
+ rv = uri->SchemeIs("http", &isHttp);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool isHttps;
+ rv = uri->SchemeIs("https", &isHttps);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Store the protocol to send via telemetry later.
+ if (isHttp) {
+ mProtocolType = ProtocolType::HTTP;
+ } else if (isHttps) {
+ mProtocolType = ProtocolType::HTTPS;
+ }
+ }
+ }
+
+ // If no aContentDom was passed into us, we are being used
+ // by chrome/c++ and have no mOwner, no mPrincipal, and no need
+ // to prompt.
+ mService = nsGeolocationService::GetGeolocationService();
+ if (mService) {
+ mService->AddLocator(this);
+ }
+
+ return NS_OK;
+}
+
+void
+Geolocation::Shutdown()
+{
+ // Release all callbacks
+ mPendingCallbacks.Clear();
+ mWatchingCallbacks.Clear();
+
+ if (mService) {
+ mService->RemoveLocator(this);
+ mService->UpdateAccuracy();
+ }
+
+ mService = nullptr;
+ mPrincipal = nullptr;
+}
+
+nsPIDOMWindowInner*
+Geolocation::GetParentObject() const {
+ nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mOwner);
+ return window.get();
+}
+
+bool
+Geolocation::HasActiveCallbacks()
+{
+ return mPendingCallbacks.Length() || mWatchingCallbacks.Length();
+}
+
+bool
+Geolocation::HighAccuracyRequested()
+{
+ for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) {
+ if (mWatchingCallbacks[i]->WantsHighAccuracy()) {
+ return true;
+ }
+ }
+
+ for (uint32_t i = 0; i < mPendingCallbacks.Length(); i++) {
+ if (mPendingCallbacks[i]->WantsHighAccuracy()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void
+Geolocation::RemoveRequest(nsGeolocationRequest* aRequest)
+{
+ bool requestWasKnown =
+ (mPendingCallbacks.RemoveElement(aRequest) !=
+ mWatchingCallbacks.RemoveElement(aRequest));
+
+ Unused << requestWasKnown;
+}
+
+NS_IMETHODIMP
+Geolocation::Update(nsIDOMGeoPosition *aSomewhere)
+{
+ if (!WindowOwnerStillExists()) {
+ Shutdown();
+ return NS_OK;
+ }
+
+ if (aSomewhere) {
+ nsCOMPtr<nsIDOMGeoPositionCoords> coords;
+ aSomewhere->GetCoords(getter_AddRefs(coords));
+ if (coords) {
+ double accuracy = -1;
+ coords->GetAccuracy(&accuracy);
+ mozilla::Telemetry::Accumulate(mozilla::Telemetry::GEOLOCATION_ACCURACY_EXPONENTIAL, accuracy);
+ }
+ }
+
+ for (uint32_t i = mPendingCallbacks.Length(); i > 0; i--) {
+ mPendingCallbacks[i-1]->Update(aSomewhere);
+ RemoveRequest(mPendingCallbacks[i-1]);
+ }
+
+ // notify everyone that is watching
+ for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) {
+ mWatchingCallbacks[i]->Update(aSomewhere);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+Geolocation::NotifyError(uint16_t aErrorCode)
+{
+ if (!WindowOwnerStillExists()) {
+ Shutdown();
+ return NS_OK;
+ }
+
+ mozilla::Telemetry::Accumulate(mozilla::Telemetry::GEOLOCATION_ERROR, true);
+
+ for (uint32_t i = mPendingCallbacks.Length(); i > 0; i--) {
+ mPendingCallbacks[i-1]->NotifyErrorAndShutdown(aErrorCode);
+ //NotifyErrorAndShutdown() removes the request from the array
+ }
+
+ // notify everyone that is watching
+ for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) {
+ mWatchingCallbacks[i]->NotifyErrorAndShutdown(aErrorCode);
+ }
+
+ return NS_OK;
+}
+
+bool
+Geolocation::IsAlreadyCleared(nsGeolocationRequest* aRequest)
+{
+ for (uint32_t i = 0, length = mClearedWatchIDs.Length(); i < length; ++i) {
+ if (mClearedWatchIDs[i] == aRequest->WatchId()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+Geolocation::ClearPendingRequest(nsGeolocationRequest* aRequest)
+{
+ if (aRequest->IsWatch() && this->IsAlreadyCleared(aRequest)) {
+ this->NotifyAllowedRequest(aRequest);
+ this->ClearWatch(aRequest->WatchId());
+ return true;
+ }
+
+ return false;
+}
+
+void
+Geolocation::GetCurrentPosition(PositionCallback& aCallback,
+ PositionErrorCallback* aErrorCallback,
+ const PositionOptions& aOptions,
+ ErrorResult& aRv)
+{
+ nsresult rv = GetCurrentPosition(GeoPositionCallback(&aCallback),
+ GeoPositionErrorCallback(aErrorCallback),
+ Move(CreatePositionOptionsCopy(aOptions)));
+
+ if (NS_FAILED(rv)) {
+ aRv.Throw(rv);
+ }
+
+ return;
+}
+
+NS_IMETHODIMP
+Geolocation::GetCurrentPosition(nsIDOMGeoPositionCallback* aCallback,
+ nsIDOMGeoPositionErrorCallback* aErrorCallback,
+ UniquePtr<PositionOptions>&& aOptions)
+{
+ NS_ENSURE_ARG_POINTER(aCallback);
+
+ return GetCurrentPosition(GeoPositionCallback(aCallback),
+ GeoPositionErrorCallback(aErrorCallback),
+ Move(aOptions));
+}
+
+nsresult
+Geolocation::GetCurrentPosition(GeoPositionCallback callback,
+ GeoPositionErrorCallback errorCallback,
+ UniquePtr<PositionOptions>&& options)
+{
+ if (mPendingCallbacks.Length() > MAX_GEO_REQUESTS_PER_WINDOW) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ // After this we hand over ownership of options to our nsGeolocationRequest.
+
+ // Count the number of requests per protocol/scheme.
+ Telemetry::Accumulate(Telemetry::GEOLOCATION_GETCURRENTPOSITION_SECURE_ORIGIN,
+ static_cast<uint8_t>(mProtocolType));
+
+ RefPtr<nsGeolocationRequest> request =
+ new nsGeolocationRequest(this, Move(callback), Move(errorCallback),
+ Move(options), static_cast<uint8_t>(mProtocolType),
+ false);
+
+ if (!sGeoEnabled) {
+ nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(false, request);
+ NS_DispatchToMainThread(ev);
+ return NS_OK;
+ }
+
+ if (!mOwner && !nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (mOwner) {
+ if (!RegisterRequestWithPrompt(request)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ return NS_OK;
+ }
+
+ if (!nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(true, request);
+ NS_DispatchToMainThread(ev);
+
+ return NS_OK;
+}
+
+int32_t
+Geolocation::WatchPosition(PositionCallback& aCallback,
+ PositionErrorCallback* aErrorCallback,
+ const PositionOptions& aOptions,
+ ErrorResult& aRv)
+{
+ int32_t ret = 0;
+ nsresult rv = WatchPosition(GeoPositionCallback(&aCallback),
+ GeoPositionErrorCallback(aErrorCallback),
+ Move(CreatePositionOptionsCopy(aOptions)), &ret);
+
+ if (NS_FAILED(rv)) {
+ aRv.Throw(rv);
+ }
+
+ return ret;
+}
+
+NS_IMETHODIMP
+Geolocation::WatchPosition(nsIDOMGeoPositionCallback *aCallback,
+ nsIDOMGeoPositionErrorCallback *aErrorCallback,
+ UniquePtr<PositionOptions>&& aOptions,
+ int32_t* aRv)
+{
+ NS_ENSURE_ARG_POINTER(aCallback);
+
+ return WatchPosition(GeoPositionCallback(aCallback),
+ GeoPositionErrorCallback(aErrorCallback),
+ Move(aOptions), aRv);
+}
+
+nsresult
+Geolocation::WatchPosition(GeoPositionCallback aCallback,
+ GeoPositionErrorCallback aErrorCallback,
+ UniquePtr<PositionOptions>&& aOptions,
+ int32_t* aRv)
+{
+ if (mWatchingCallbacks.Length() > MAX_GEO_REQUESTS_PER_WINDOW) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ // Count the number of requests per protocol/scheme.
+ Telemetry::Accumulate(Telemetry::GEOLOCATION_WATCHPOSITION_SECURE_ORIGIN,
+ static_cast<uint8_t>(mProtocolType));
+
+ // The watch ID:
+ *aRv = mLastWatchId++;
+
+ RefPtr<nsGeolocationRequest> request =
+ new nsGeolocationRequest(this, Move(aCallback), Move(aErrorCallback),
+ Move(aOptions),
+ static_cast<uint8_t>(mProtocolType), true, *aRv);
+
+ if (!sGeoEnabled) {
+ nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(false, request);
+ NS_DispatchToMainThread(ev);
+ return NS_OK;
+ }
+
+ if (!mOwner && !nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (mOwner) {
+ if (!RegisterRequestWithPrompt(request))
+ return NS_ERROR_NOT_AVAILABLE;
+
+ return NS_OK;
+ }
+
+ if (!nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ request->Allow(JS::UndefinedHandleValue);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+Geolocation::ClearWatch(int32_t aWatchId)
+{
+ if (aWatchId < 0) {
+ return NS_OK;
+ }
+
+ if (!mClearedWatchIDs.Contains(aWatchId)) {
+ mClearedWatchIDs.AppendElement(aWatchId);
+ }
+
+ for (uint32_t i = 0, length = mWatchingCallbacks.Length(); i < length; ++i) {
+ if (mWatchingCallbacks[i]->WatchId() == aWatchId) {
+ mWatchingCallbacks[i]->Shutdown();
+ RemoveRequest(mWatchingCallbacks[i]);
+ mClearedWatchIDs.RemoveElement(aWatchId);
+ break;
+ }
+ }
+
+ // make sure we also search through the pending requests lists for
+ // watches to clear...
+ for (uint32_t i = 0, length = mPendingRequests.Length(); i < length; ++i) {
+ if (mPendingRequests[i]->IsWatch() &&
+ (mPendingRequests[i]->WatchId() == aWatchId)) {
+ mPendingRequests[i]->Shutdown();
+ mPendingRequests.RemoveElementAt(i);
+ break;
+ }
+ }
+
+ return NS_OK;
+}
+
+bool
+Geolocation::WindowOwnerStillExists()
+{
+ // an owner was never set when Geolocation
+ // was created, which means that this object
+ // is being used without a window.
+ if (mOwner == nullptr) {
+ return true;
+ }
+
+ nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mOwner);
+
+ if (window) {
+ nsPIDOMWindowOuter* outer = window->GetOuterWindow();
+ if (!outer || outer->GetCurrentInnerWindow() != window ||
+ outer->Closed()) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void
+Geolocation::NotifyAllowedRequest(nsGeolocationRequest* aRequest)
+{
+ if (aRequest->IsWatch()) {
+ mWatchingCallbacks.AppendElement(aRequest);
+ } else {
+ mPendingCallbacks.AppendElement(aRequest);
+ }
+}
+
+bool
+Geolocation::RegisterRequestWithPrompt(nsGeolocationRequest* request)
+{
+ if (Preferences::GetBool("geo.prompt.testing", false)) {
+ bool allow = Preferences::GetBool("geo.prompt.testing.allow", false);
+ nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(allow, request);
+ NS_DispatchToMainThread(ev);
+ return true;
+ }
+
+ nsCOMPtr<nsIRunnable> ev = new RequestPromptEvent(request, mOwner);
+ NS_DispatchToMainThread(ev);
+ return true;
+}
+
+JSObject*
+Geolocation::WrapObject(JSContext *aCtx, JS::Handle<JSObject*> aGivenProto)
+{
+ return GeolocationBinding::Wrap(aCtx, this, aGivenProto);
+}
diff --git a/dom/geolocation/nsGeolocation.h b/dom/geolocation/nsGeolocation.h
new file mode 100644
index 0000000000..0bc527e342
--- /dev/null
+++ b/dom/geolocation/nsGeolocation.h
@@ -0,0 +1,260 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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/. */
+
+#ifndef nsGeoLocation_h
+#define nsGeoLocation_h
+
+// Microsoft's API Name hackery sucks
+#undef CreateEvent
+
+#include "mozilla/StaticPtr.h"
+#include "nsCOMPtr.h"
+#include "nsTArray.h"
+#include "nsITimer.h"
+#include "nsIObserver.h"
+#include "nsWrapperCache.h"
+
+#include "nsWeakPtr.h"
+#include "nsCycleCollectionParticipant.h"
+
+#include "nsGeoPosition.h"
+#include "nsIDOMGeoGeolocation.h"
+#include "nsIDOMGeoPosition.h"
+#include "nsIDOMGeoPositionError.h"
+#include "nsIDOMGeoPositionCallback.h"
+#include "nsIDOMGeoPositionErrorCallback.h"
+#include "mozilla/dom/GeolocationBinding.h"
+#include "mozilla/dom/PositionErrorBinding.h"
+#include "mozilla/dom/CallbackObject.h"
+
+#include "nsIGeolocationProvider.h"
+#include "nsIContentPermissionPrompt.h"
+#include "mozilla/Attributes.h"
+
+class nsGeolocationService;
+class nsGeolocationRequest;
+
+namespace mozilla {
+namespace dom {
+class Geolocation;
+typedef CallbackObjectHolder<PositionCallback, nsIDOMGeoPositionCallback> GeoPositionCallback;
+typedef CallbackObjectHolder<PositionErrorCallback, nsIDOMGeoPositionErrorCallback> GeoPositionErrorCallback;
+} // namespace dom
+} // namespace mozilla
+
+struct CachedPositionAndAccuracy {
+ nsCOMPtr<nsIDOMGeoPosition> position;
+ bool isHighAccuracy;
+};
+
+/**
+ * Singleton that manages the geolocation provider
+ */
+class nsGeolocationService final : public nsIGeolocationUpdate,
+ public nsIObserver
+{
+public:
+
+ static already_AddRefed<nsGeolocationService> GetGeolocationService();
+ static mozilla::StaticRefPtr<nsGeolocationService> sService;
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIGEOLOCATIONUPDATE
+ NS_DECL_NSIOBSERVER
+
+ nsGeolocationService() {
+ mHigherAccuracy = false;
+ }
+
+ nsresult Init();
+
+ // Management of the Geolocation objects
+ void AddLocator(mozilla::dom::Geolocation* locator);
+ void RemoveLocator(mozilla::dom::Geolocation* locator);
+
+ void SetCachedPosition(nsIDOMGeoPosition* aPosition);
+ CachedPositionAndAccuracy GetCachedPosition();
+
+ // Find and startup a geolocation device (gps, nmea, etc.)
+ nsresult StartDevice(nsIPrincipal* aPrincipal);
+
+ // Stop the started geolocation device (gps, nmea, etc.)
+ void StopDevice();
+
+ // create, or reinitalize the callback timer
+ void SetDisconnectTimer();
+
+ // 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
+ // case, we disable the disconnect timer.
+ nsCOMPtr<nsITimer> mDisconnectTimer;
+
+ // The object providing geo location information to us.
+ nsCOMPtr<nsIGeolocationProvider> mProvider;
+
+ // mGeolocators are not owned here. Their constructor
+ // adds them to this list, and their destructor removes
+ // them from this list.
+ nsTArray<mozilla::dom::Geolocation*> mGeolocators;
+
+ // This is the last geo position that we have seen.
+ CachedPositionAndAccuracy mLastPosition;
+
+ // Current state of requests for higher accuracy
+ bool mHigherAccuracy;
+};
+
+namespace mozilla {
+namespace dom {
+
+/**
+ * Can return a geolocation info
+ */
+class Geolocation final : public nsIDOMGeoGeolocation,
+ public nsIGeolocationUpdate,
+ public nsWrapperCache
+{
+public:
+
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(Geolocation, nsIDOMGeoGeolocation)
+
+ NS_DECL_NSIGEOLOCATIONUPDATE
+ NS_DECL_NSIDOMGEOGEOLOCATION
+
+ Geolocation();
+
+ nsresult Init(nsPIDOMWindowInner* aContentDom = nullptr);
+
+ nsPIDOMWindowInner* GetParentObject() const;
+ virtual JSObject* WrapObject(JSContext *aCtx, JS::Handle<JSObject*> aGivenProto) override;
+
+ int32_t WatchPosition(PositionCallback& aCallback, PositionErrorCallback* aErrorCallback, const PositionOptions& aOptions, ErrorResult& aRv);
+ void GetCurrentPosition(PositionCallback& aCallback, PositionErrorCallback* aErrorCallback, const PositionOptions& aOptions, ErrorResult& aRv);
+
+ // 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);
+
+ // Check if there is already ClearWatch called for current
+ // request & clear if yes
+ bool ClearPendingRequest(nsGeolocationRequest* aRequest);
+
+ // Shutting down.
+ void Shutdown();
+
+ // Getter for the principal that this Geolocation was loaded from
+ nsIPrincipal* GetPrincipal() { return mPrincipal; }
+
+ // Getter for the window that this Geolocation is owned by
+ nsIWeakReference* GetOwner() { return mOwner; }
+
+ // Check to see if the window still exists
+ bool WindowOwnerStillExists();
+
+ // Check to see if any active request requires high accuracy
+ bool HighAccuracyRequested();
+
+private:
+
+ ~Geolocation();
+
+ nsresult GetCurrentPosition(GeoPositionCallback aCallback,
+ GeoPositionErrorCallback aErrorCallback,
+ UniquePtr<PositionOptions>&& aOptions);
+ nsresult WatchPosition(GeoPositionCallback aCallback,
+ GeoPositionErrorCallback aErrorCallback,
+ UniquePtr<PositionOptions>&& aOptions, int32_t* aRv);
+
+ bool RegisterRequestWithPrompt(nsGeolocationRequest* request);
+
+ // Check if clearWatch is already called
+ bool IsAlreadyCleared(nsGeolocationRequest* aRequest);
+
+ // 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. All requests held by either array are active, that
+ // is, they have been allowed and expect to be fulfilled.
+
+ nsTArray<RefPtr<nsGeolocationRequest> > mPendingCallbacks;
+ nsTArray<RefPtr<nsGeolocationRequest> > mWatchingCallbacks;
+
+ // window that this was created for. Weak reference.
+ nsWeakPtr mOwner;
+
+ // where the content was loaded from
+ nsCOMPtr<nsIPrincipal> mPrincipal;
+
+ // the protocols we want to measure
+ enum class ProtocolType: uint8_t { OTHER, HTTP, HTTPS };
+
+ // the protocol used to load the content
+ ProtocolType mProtocolType;
+
+ // owning back pointer.
+ RefPtr<nsGeolocationService> mService;
+
+ // Watch ID
+ uint32_t mLastWatchId;
+
+ // Pending requests are used when the service is not ready
+ nsTArray<RefPtr<nsGeolocationRequest> > mPendingRequests;
+
+ // Array containing already cleared watch IDs
+ nsTArray<int32_t> mClearedWatchIDs;
+};
+
+class PositionError final : public nsIDOMGeoPositionError,
+ public nsWrapperCache
+{
+public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PositionError)
+
+ NS_DECL_NSIDOMGEOPOSITIONERROR
+
+ PositionError(Geolocation* aParent, int16_t aCode);
+
+ Geolocation* GetParentObject() const;
+
+ virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+
+ int16_t Code() const {
+ return mCode;
+ }
+
+ void NotifyCallback(const GeoPositionErrorCallback& callback);
+private:
+ ~PositionError();
+ int16_t mCode;
+ RefPtr<Geolocation> mParent;
+};
+
+} // namespace dom
+
+inline nsISupports*
+ToSupports(dom::Geolocation* aGeolocation)
+{
+ return ToSupports(static_cast<nsIDOMGeoGeolocation*>(aGeolocation));
+}
+
+} // namespace mozilla
+
+#endif /* nsGeoLocation_h */