summaryrefslogtreecommitdiff
path: root/dom/geolocation
diff options
context:
space:
mode:
authorPale Moon <git-repo@palemoon.org>2016-09-01 13:39:08 +0200
committerPale Moon <git-repo@palemoon.org>2016-09-01 13:39:08 +0200
commit3d8ce1a11a7347cc94a937719c4bc8df46fb8d14 (patch)
tree8c26ca375a6312751c00a27e1653fb6f189f0463 /dom/geolocation
parente449bdb1ec3a82f204bffdd9c3c54069d086eee3 (diff)
downloadpalemoon-gre-3d8ce1a11a7347cc94a937719c4bc8df46fb8d14.tar.gz
Base import of Tycho code (warning: huge commit)
Diffstat (limited to 'dom/geolocation')
-rw-r--r--dom/geolocation/moz.build54
-rw-r--r--dom/geolocation/nsGeoGridFuzzer.cpp135
-rw-r--r--dom/geolocation/nsGeoGridFuzzer.h25
-rw-r--r--dom/geolocation/nsGeoPosition.cpp262
-rw-r--r--dom/geolocation/nsGeoPosition.h145
-rw-r--r--dom/geolocation/nsGeoPositionIPCSerialiser.h150
-rw-r--r--dom/geolocation/nsGeolocation.cpp1627
-rw-r--r--dom/geolocation/nsGeolocation.h258
-rw-r--r--dom/geolocation/nsGeolocationSettings.cpp464
-rw-r--r--dom/geolocation/nsGeolocationSettings.h165
10 files changed, 3285 insertions, 0 deletions
diff --git a/dom/geolocation/moz.build b/dom/geolocation/moz.build
new file mode 100644
index 000000000..ba34983a4
--- /dev/null
+++ b/dom/geolocation/moz.build
@@ -0,0 +1,54 @@
+# -*- Mode: python; c-basic-offset: 4; 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 += [
+ 'nsGeolocationSettings.h',
+ 'nsGeoPosition.h',
+ 'nsGeoPositionIPCSerialiser.h',
+]
+
+SOURCES += [
+ 'nsGeolocation.cpp',
+]
+
+UNIFIED_SOURCES += [
+ 'nsGeoGridFuzzer.cpp',
+ 'nsGeolocationSettings.cpp',
+ 'nsGeoPosition.cpp',
+]
+
+FAIL_ON_WARNINGS = True
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+LOCAL_INCLUDES += [
+ '/dom/base',
+ '/dom/ipc',
+]
+
+if CONFIG['MOZ_ENABLE_QT5GEOPOSITION']:
+ LOCAL_INCLUDES += [
+ '/dom/system/qt',
+ ]
+ CXXFLAGS += CONFIG['MOZ_QT_CFLAGS']
+
+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',
+ ]
diff --git a/dom/geolocation/nsGeoGridFuzzer.cpp b/dom/geolocation/nsGeoGridFuzzer.cpp
new file mode 100644
index 000000000..d0be94936
--- /dev/null
+++ b/dom/geolocation/nsGeoGridFuzzer.cpp
@@ -0,0 +1,135 @@
+/* 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 <math.h>
+#include "nsGeoGridFuzzer.h"
+#include "nsGeoPosition.h"
+
+
+#ifdef MOZ_APPROX_LOCATION
+
+/* The following constants are taken from the World Geodetic System 1984 (WGS84)
+ * reference model for the earth ellipsoid [1]. The values in the model are
+ * an accepted standard for GPS and other navigational systems.
+ *
+ * [1] http://www.oosa.unvienna.org/pdf/icg/2012/template/WGS_84.pdf
+ */
+#define WGS84_a (6378137.0) // equitorial axis
+#define WGS84_b (6356752.314245179) // polar axis (a * (1-f))
+#define WGS84_f (1.0/298.257223563) // inverse flattening
+#define WGS84_EPSILON (5.72957795e-9) // 1e-10 radians in degrees
+#define sq(f) ((f) * (f))
+#define sign(f) (((f) < 0) ? -1 : 1)
+
+/* if you have an ellipsoid with semi-major axis A and semi-minor axis B, the
+ * radius at angle phi along the semi-major axis can be calculated with this
+ * formula. by using the WGS84 values for A and B, we calculate the radius of
+ * earth, given the angle of latitude, phi.*/
+#define LON_RADIUS(phi) (sqrt((sq(sq(WGS84_a) * cos(phi)) + sq(sq(WGS84_b) * sin(phi))) / \
+ (sq(WGS84_a * cos(phi)) + sq(WGS84_b * sin(phi)))))
+/* the radius of earth changes as a function of latitude, to simplify I am
+ * assuming the fixed radius of the earth halfway between the poles and the
+ * equator. this is calculated from LON_RADIUS(M_PI/4), or the radius at
+ * 45 degrees N.*/
+#define LAT_RADIUS (6367489.543863)
+
+/* This function figures out the latitudinal grid square that the given
+ * latitude coordinate falls into and then returns the latitudinal center of
+ * that grid square. It handles the proper wrapping at the poles +/- 90
+ * (e.g. +95 wraps to +85 and -95 wraps to -85) */
+static double GridAlgorithmLat(int32_t aDistMeters, double aLatDeg)
+{
+ /* switch to radians */
+ double phi = (aLatDeg * M_PI) / 180;
+
+ /* properly wrap the latitude */
+ phi = atan(sin(phi) / fabs(cos(phi)));
+
+ /* calculate grid size in radians */
+ double gridSizeRad = aDistMeters / LAT_RADIUS;
+
+ /* find the southern edge, in radians, of the grid cell, then add half of a
+ * grid cell to find the center latitude in radians */
+ double gridCenterPhi = gridSizeRad * floor(phi / gridSizeRad) + gridSizeRad / 2;
+
+ /* properly wrap it and return it in degrees */
+ return atan(sin(gridCenterPhi) / fabs(cos(gridCenterPhi))) * (180.0 / M_PI);
+}
+
+/* This function figures out the longitudinal grid square that the given longitude
+ * coordinate falls into and then returns the longitudinal center of that grid
+ * square. It handles the proper wrapping at +/- 180 (e.g. +185 wraps to -175
+ * and -185 wraps to +175) */
+static double GridAlgorithmLon(int32_t aDistMeters, double aLatDeg, double aLonDeg)
+{
+ /* switch to radians */
+ double phi = (aLatDeg * M_PI) / 180;
+ double theta = (aLonDeg * M_PI) / 180;
+
+ /* properly wrap the lat/lon */
+ phi = atan(sin(phi) / fabs(cos(phi)));
+ theta = atan2(sin(theta), cos(theta));
+
+ /* calculate grid size in radians */
+ double gridSizeRad = aDistMeters / LON_RADIUS(phi);
+
+ /* find the western edge, in radians, of the grid cell, then add half of a
+ * grid cell to find the center longitude in radians */
+ double gridCenterTheta = gridSizeRad * floor(theta / gridSizeRad) + gridSizeRad / 2;
+
+ /* properly wrap it and return it in degrees */
+ return atan2(sin(gridCenterTheta), cos(gridCenterTheta)) * (180.0 / M_PI);
+}
+
+/* This function takes the grid size and the graticule coordinates of a
+ * location and calculates which grid cell the coordinates fall within and
+ * then returns the coordinates of the geographical center of the grid square.
+ */
+static void CalculateGridCoords(int32_t aDistKm, double& aLatDeg, double& aLonDeg)
+{
+ // a grid size of 0 is the same as precise
+ if (aDistKm == 0) {
+ return;
+ }
+ aLonDeg = GridAlgorithmLon(aDistKm * 1000, aLatDeg, aLonDeg);
+ aLatDeg = GridAlgorithmLat(aDistKm * 1000, aLatDeg);
+}
+
+already_AddRefed<nsIDOMGeoPosition>
+nsGeoGridFuzzer::FuzzLocation(const GeolocationSetting & aSetting,
+ nsIDOMGeoPosition * aPosition)
+{
+ if (!aPosition) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIDOMGeoPositionCoords> coords;
+ nsresult rv = aPosition->GetCoords(getter_AddRefs(coords));
+ NS_ENSURE_SUCCESS(rv, nullptr);
+ if (!coords) {
+ return nullptr;
+ }
+
+ double lat = 0.0, lon = 0.0;
+ coords->GetLatitude(&lat);
+ coords->GetLongitude(&lon);
+
+ // adjust lat/lon to be the center of the grid square
+ CalculateGridCoords(aSetting.GetApproxDistance(), lat, lon);
+ GPSLOG("approximate location with delta %d is %f, %f",
+ aSetting.GetApproxDistance(), lat, lon);
+
+ // reusing the timestamp
+ DOMTimeStamp ts;
+ rv = aPosition->GetTimestamp(&ts);
+ NS_ENSURE_SUCCESS(rv, nullptr);
+
+ // return a position at sea level, N heading, 0 speed, 0 error.
+ nsRefPtr<nsGeoPosition> pos = new nsGeoPosition(lat, lon, 0.0, 0.0,
+ 0.0, 0.0, 0.0, ts);
+ return pos.forget();
+}
+
+#endif
diff --git a/dom/geolocation/nsGeoGridFuzzer.h b/dom/geolocation/nsGeoGridFuzzer.h
new file mode 100644
index 000000000..2a6f6d6bc
--- /dev/null
+++ b/dom/geolocation/nsGeoGridFuzzer.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 nsGeoGridFuzzer_h
+#define nsGeoGridFuzzer_h
+
+#include "nsCOMPtr.h"
+#include "nsIDOMGeoPosition.h"
+#include "nsGeolocationSettings.h"
+
+class nsGeoGridFuzzer final
+{
+public:
+
+ static already_AddRefed<nsIDOMGeoPosition>
+ FuzzLocation(const GeolocationSetting& aSetting, nsIDOMGeoPosition* aPosition);
+
+private:
+ nsGeoGridFuzzer() {} // can't construct
+ nsGeoGridFuzzer(const nsGeoGridFuzzer&) {} // can't copy
+};
+
+#endif
diff --git a/dom/geolocation/nsGeoPosition.cpp b/dom/geolocation/nsGeoPosition.cpp
new file mode 100644
index 000000000..b92280784
--- /dev/null
+++ b/dom/geolocation/nsGeoPosition.cpp
@@ -0,0 +1,262 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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)
+{
+ return PositionBinding::Wrap(aCx, this);
+}
+
+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)
+{
+ return CoordinatesBinding::Wrap(aCx, this);
+}
+
+#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 000000000..3b569ee99
--- /dev/null
+++ b/dom/geolocation/nsGeoPosition.h
@@ -0,0 +1,145 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsAutoPtr.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;
+ nsRefPtr<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) override;
+
+ Coordinates* Coords();
+
+ uint64_t Timestamp() const;
+
+ nsIDOMGeoPosition* GetWrappedGeoPosition() { return mGeoPosition; }
+
+private:
+ nsRefPtr<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) 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:
+ nsRefPtr<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 000000000..7fbbabc22
--- /dev/null
+++ b/dom/geolocation/nsGeoPositionIPCSerialiser.h
@@ -0,0 +1,150 @@
+/* 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, void **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, void **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;
+ };
+
+};
+
+}
+
+#endif
diff --git a/dom/geolocation/nsGeolocation.cpp b/dom/geolocation/nsGeolocation.cpp
new file mode 100644
index 000000000..eb9cd5c16
--- /dev/null
+++ b/dom/geolocation/nsGeolocation.cpp
@@ -0,0 +1,1627 @@
+/* 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 "nsISettingsService.h"
+
+#include "nsGeolocation.h"
+#include "nsGeoGridFuzzer.h"
+#include "nsGeolocationSettings.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/dom/PermissionMessageUtils.h"
+#include "mozilla/dom/SettingChangeNotificationBinding.h"
+
+#include "nsJSUtils.h"
+#include "prdtoa.h"
+
+class nsIPrincipal;
+
+#ifdef MOZ_ENABLE_QT5GEOPOSITION
+#include "QTMLocationProvider.h"
+#endif
+
+#ifdef MOZ_WIDGET_ANDROID
+#include "AndroidLocationProvider.h"
+#endif
+
+#ifdef MOZ_WIDGET_GONK
+#include "GonkGPSGeolocationProvider.h"
+#endif
+
+#ifdef MOZ_WIDGET_COCOA
+#include "CoreLocationLocationProvider.h"
+#endif
+
+#ifdef XP_WIN
+#include "WindowsLocationProvider.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
+
+// the geolocation enabled setting
+#define GEO_SETTINGS_ENABLED "geolocation.enabled"
+
+using mozilla::unused; // <snicker>
+using namespace mozilla;
+using namespace mozilla::dom;
+
+class nsGeolocationRequest final
+ : public nsIContentPermissionRequest
+ , public nsITimerCallback
+ , public nsIGeolocationUpdate
+{
+ public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_NSICONTENTPERMISSIONREQUEST
+ NS_DECL_NSITIMERCALLBACK
+ NS_DECL_NSIGEOLOCATIONUPDATE
+
+ NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsGeolocationRequest, nsIContentPermissionRequest)
+
+ nsGeolocationRequest(Geolocation* aLocator,
+ const GeoPositionCallback& aCallback,
+ const GeoPositionErrorCallback& aErrorCallback,
+ PositionOptions* aOptions,
+ bool aWatchPositionRequest = false,
+ int32_t aWatchId = 0);
+ 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();
+
+ already_AddRefed<nsIDOMGeoPosition> AdjustedLocation(nsIDOMGeoPosition*);
+
+ bool mIsWatchPositionRequest;
+
+ nsCOMPtr<nsITimer> mTimeoutTimer;
+ GeoPositionCallback mCallback;
+ GeoPositionErrorCallback mErrorCallback;
+ nsAutoPtr<PositionOptions> mOptions;
+
+ nsRefPtr<Geolocation> mLocator;
+
+ int32_t mWatchId;
+ bool mShutdown;
+};
+
+static PositionOptions*
+CreatePositionOptionsCopy(const PositionOptions& aOptions)
+{
+ nsAutoPtr<PositionOptions> geoOptions(new PositionOptions());
+
+ geoOptions->mEnableHighAccuracy = aOptions.mEnableHighAccuracy;
+ geoOptions->mMaximumAge = aOptions.mMaximumAge;
+ geoOptions->mTimeout = aOptions.mTimeout;
+
+ return geoOptions.forget();
+}
+
+class GeolocationSettingsCallback : public nsISettingsServiceCallback
+{
+ virtual ~GeolocationSettingsCallback() {
+ MOZ_COUNT_DTOR(GeolocationSettingsCallback);
+ }
+
+public:
+ NS_DECL_ISUPPORTS
+
+ GeolocationSettingsCallback() {
+ MOZ_COUNT_CTOR(GeolocationSettingsCallback);
+ }
+
+ NS_IMETHOD Handle(const nsAString& aName, JS::Handle<JS::Value> aResult) override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (aName.EqualsASCII(GEO_SETTINGS_ENABLED)) {
+ // The geolocation is enabled by default:
+ bool value = true;
+ if (aResult.isBoolean()) {
+ value = aResult.toBoolean();
+ }
+
+ GPSLOG("%s set to %s",
+ NS_ConvertUTF16toUTF8(aName).get(),
+ (value ? "ENABLED" : "DISABLED"));
+ MozSettingValue(value);
+
+ } else {
+ nsRefPtr<nsGeolocationSettings> gs = nsGeolocationSettings::GetGeolocationSettings();
+ if (gs) {
+ gs->HandleGeolocationSettingsChange(aName, aResult);
+ }
+ }
+
+ return NS_OK;
+ }
+
+ NS_IMETHOD HandleError(const nsAString& aName) override
+ {
+ if (aName.EqualsASCII(GEO_SETTINGS_ENABLED)) {
+ GPSLOG("Unable to get value for '" GEO_SETTINGS_ENABLED "'");
+
+ // Default it's enabled:
+ MozSettingValue(true);
+ } else {
+ nsRefPtr<nsGeolocationSettings> gs = nsGeolocationSettings::GetGeolocationSettings();
+ if (gs) {
+ gs->HandleGeolocationSettingsError(aName);
+ }
+ }
+
+ return NS_OK;
+ }
+
+ void MozSettingValue(const bool aValue)
+ {
+ nsRefPtr<nsGeolocationService> gs = nsGeolocationService::GetGeolocationService();
+ if (gs) {
+ gs->HandleMozsettingValue(aValue);
+ }
+ }
+};
+
+NS_IMPL_ISUPPORTS(GeolocationSettingsCallback, nsISettingsServiceCallback)
+
+class RequestPromptEvent : public nsRunnable
+{
+public:
+ RequestPromptEvent(nsGeolocationRequest* aRequest, nsWeakPtr aWindow)
+ : mRequest(aRequest)
+ , mWindow(aWindow)
+ {
+ }
+
+ NS_IMETHOD Run()
+ {
+ nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
+ nsContentPermissionUtils::AskPermission(mRequest, window);
+ return NS_OK;
+ }
+
+private:
+ nsRefPtr<nsGeolocationRequest> mRequest;
+ nsWeakPtr mWindow;
+};
+
+class RequestAllowEvent : public nsRunnable
+{
+public:
+ RequestAllowEvent(int allow, nsGeolocationRequest* request)
+ : mAllow(allow),
+ mRequest(request)
+ {
+ }
+
+ NS_IMETHOD Run() {
+ if (mAllow) {
+ mRequest->Allow(JS::UndefinedHandleValue);
+ } else {
+ mRequest->Cancel();
+ }
+ return NS_OK;
+ }
+
+private:
+ bool mAllow;
+ nsRefPtr<nsGeolocationRequest> mRequest;
+};
+
+class RequestSendLocationEvent : public nsRunnable
+{
+public:
+ RequestSendLocationEvent(nsIDOMGeoPosition* aPosition,
+ nsGeolocationRequest* aRequest)
+ : mPosition(aPosition),
+ mRequest(aRequest)
+ {
+ }
+
+ NS_IMETHOD Run() {
+ mRequest->SendLocation(mPosition);
+ return NS_OK;
+ }
+
+private:
+ nsCOMPtr<nsIDOMGeoPosition> mPosition;
+ nsRefPtr<nsGeolocationRequest> mRequest;
+ nsRefPtr<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)
+{
+ return PositionErrorBinding::Wrap(aCx, this);
+}
+
+void
+PositionError::NotifyCallback(const GeoPositionErrorCallback& aCallback)
+{
+ nsAutoMicroTask mt;
+ if (aCallback.HasWebIDLCallback()) {
+ PositionErrorCallback* callback = aCallback.GetWebIDLCallback();
+
+ if (callback) {
+ ErrorResult err;
+ callback->Call(*this, err);
+ }
+ } else {
+ nsIDOMGeoPositionErrorCallback* callback = aCallback.GetXPCOMCallback();
+ if (callback) {
+ callback->HandleEvent(this);
+ }
+ }
+}
+////////////////////////////////////////////////////
+// nsGeolocationRequest
+////////////////////////////////////////////////////
+
+nsGeolocationRequest::nsGeolocationRequest(Geolocation* aLocator,
+ const GeoPositionCallback& aCallback,
+ const GeoPositionErrorCallback& aErrorCallback,
+ PositionOptions* aOptions,
+ bool aWatchPositionRequest,
+ int32_t aWatchId)
+ : mIsWatchPositionRequest(aWatchPositionRequest),
+ mCallback(aCallback),
+ mErrorCallback(aErrorCallback),
+ mOptions(aOptions),
+ mLocator(aLocator),
+ mWatchId(aWatchId),
+ mShutdown(false)
+{
+}
+
+nsGeolocationRequest::~nsGeolocationRequest()
+{
+}
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGeolocationRequest)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
+ NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
+ NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
+ 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)
+
+NS_IMETHODIMP
+nsGeolocationRequest::Notify(nsITimer* aTimer)
+{
+ StopTimeoutTimer();
+ NotifyErrorAndShutdown(nsIDOMGeoPositionError::TIMEOUT);
+ return NS_OK;
+}
+
+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(nsIDOMWindow * *aRequestingWindow)
+{
+ NS_ENSURE_ARG_POINTER(aRequestingWindow);
+
+ nsCOMPtr<nsIDOMWindow> 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 (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 (mLocator->ClearPendingRequest(this)) {
+ return NS_OK;
+ }
+
+ // Kick off the geo device, if it isn't already running
+ nsRefPtr<nsGeolocationService> gs = nsGeolocationService::GetGeolocationService();
+ nsresult rv = gs->StartDevice(GetPrincipal());
+
+ if (NS_FAILED(rv)) {
+ // Location provider error
+ NotifyError(nsIDOMGeoPositionError::POSITION_UNAVAILABLE);
+ return NS_OK;
+ }
+
+ 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);
+ }
+
+ 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;
+}
+
+void
+nsGeolocationRequest::SetTimeoutTimer()
+{
+ StopTimeoutTimer();
+
+ int32_t timeout;
+ if (mOptions && (timeout = mOptions->mTimeout) != 0) {
+
+ if (timeout < 0) {
+ timeout = 0;
+ } else if (timeout < 10) {
+ timeout = 10;
+ }
+
+ mTimeoutTimer = do_CreateInstance("@mozilla.org/timer;1");
+ mTimeoutTimer->InitWithCallback(this, timeout, nsITimer::TYPE_ONE_SHOT);
+ }
+}
+
+void
+nsGeolocationRequest::StopTimeoutTimer()
+{
+ if (mTimeoutTimer) {
+ mTimeoutTimer->Cancel();
+ mTimeoutTimer = nullptr;
+ }
+}
+
+static already_AddRefed<nsIDOMGeoPosition>
+SynthesizeLocation(DOMTimeStamp aTimestamp, double aLatitude, double aLongitude)
+{
+ // return a position at sea level, N heading, 0 speed, 0 error.
+ nsRefPtr<nsGeoPosition> pos = new nsGeoPosition(aLatitude, aLongitude,
+ 0.0, 0.0, 0.0, 0.0, 0.0,
+ aTimestamp);
+ return pos.forget();
+}
+
+
+already_AddRefed<nsIDOMGeoPosition>
+nsGeolocationRequest::AdjustedLocation(nsIDOMGeoPosition *aPosition)
+{
+ nsCOMPtr<nsIDOMGeoPosition> pos = aPosition;
+ if (XRE_GetProcessType() == GoannaProcessType_Content) {
+ GPSLOG("child process just copying position");
+ return pos.forget();
+ }
+
+ // get the settings cache
+ nsRefPtr<nsGeolocationSettings> gs = nsGeolocationSettings::GetGeolocationSettings();
+ if (!gs) {
+ return pos.forget();
+ }
+
+ // make sure ALA is enabled
+ if (!gs->IsAlaEnabled()) {
+ GPSLOG("ALA is disabled, returning precise location");
+ return pos.forget();
+ }
+
+ // look up the geolocation settings via the watch ID
+ DOMTimeStamp ts(PR_Now() / PR_USEC_PER_MSEC);
+ GeolocationSetting setting = gs->LookupGeolocationSetting(mWatchId);
+ switch (setting.GetType()) {
+ case GEO_ALA_TYPE_PRECISE:
+ GPSLOG("returning precise location watch ID: %d", mWatchId);
+ return pos.forget();
+#ifdef MOZ_APPROX_LOCATION
+ case GEO_ALA_TYPE_APPROX:
+ GPSLOG("returning approximate location for watch ID: %d", mWatchId);
+ return nsGeoGridFuzzer::FuzzLocation(setting, aPosition);
+#endif
+ case GEO_ALA_TYPE_FIXED:
+ GPSLOG("returning fixed location for watch ID:: %d", mWatchId);
+ // use "now" as time stamp
+ return SynthesizeLocation(ts, setting.GetFixedLatitude(),
+ setting.GetFixedLongitude());
+ case GEO_ALA_TYPE_NONE:
+ GPSLOG("returning no location for watch ID: %d", mWatchId);
+ // return nullptr so no location callback happens
+ return nullptr;
+ }
+
+ return 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;
+ }
+ }
+
+ nsRefPtr<Position> wrapped;
+
+ if (aPosition) {
+ nsCOMPtr<nsIDOMGeoPositionCoords> coords;
+ aPosition->GetCoords(getter_AddRefs(coords));
+ if (coords) {
+#ifdef MOZ_GPS_DEBUG
+ double lat = 0.0, lon = 0.0;
+ coords->GetLatitude(&lat);
+ coords->GetLongitude(&lon);
+ GPSLOG("returning coordinates: %f, %f", lat, lon);
+#endif
+ 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()) {
+ ErrorResult err;
+ PositionCallback* callback = mCallback.GetWebIDLCallback();
+
+ MOZ_ASSERT(callback);
+ callback->Call(*wrapped, err);
+ } else {
+ nsIDOMGeoPositionCallback* callback = mCallback.GetXPCOMCallback();
+
+ MOZ_ASSERT(callback);
+ callback->HandleEvent(aPosition);
+ }
+
+ StopTimeoutTimer();
+ 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<nsIDOMGeoPosition> pos = AdjustedLocation(aPosition);
+ nsCOMPtr<nsIRunnable> ev = new RequestSendLocationEvent(pos, this);
+ NS_DispatchToMainThread(ev);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGeolocationRequest::LocationUpdatePending()
+{
+ if (!mTimeoutTimer) {
+ SetTimeoutTimer();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGeolocationRequest::NotifyError(uint16_t aErrorCode)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsRefPtr<PositionError> positionError = new PositionError(mLocator, aErrorCode);
+ positionError->NotifyCallback(mErrorCallback);
+ return NS_OK;
+}
+
+void
+nsGeolocationRequest::Shutdown()
+{
+ MOZ_ASSERT(!mShutdown, "request shutdown twice");
+ mShutdown = true;
+
+ if (mTimeoutTimer) {
+ mTimeoutTimer->Cancel();
+ mTimeoutTimer = nullptr;
+ }
+
+ // 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->UpdateAccuracy();
+ }
+ }
+}
+
+////////////////////////////////////////////////////
+// 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 bool sGeoInitPending = 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_GetProcessType() == GoannaProcessType_Content) {
+ sGeoInitPending = false;
+ return NS_OK;
+ }
+
+ // check if the geolocation service is enable from settings
+ nsCOMPtr<nsISettingsService> settings =
+ do_GetService("@mozilla.org/settingsService;1");
+
+ if (settings) {
+ nsCOMPtr<nsISettingsServiceLock> settingsLock;
+ nsresult rv = settings->CreateLock(nullptr, getter_AddRefs(settingsLock));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsRefPtr<GeolocationSettingsCallback> callback = new GeolocationSettingsCallback();
+ rv = settingsLock->Get(GEO_SETTINGS_ENABLED, callback);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // look up the geolocation settings
+ callback = new GeolocationSettingsCallback();
+ rv = settingsLock->Get(GEO_ALA_ENABLED, callback);
+ NS_ENSURE_SUCCESS(rv, rv);
+ callback = new GeolocationSettingsCallback();
+ rv = settingsLock->Get(GEO_ALA_TYPE, callback);
+ NS_ENSURE_SUCCESS(rv, rv);
+#ifdef MOZ_APPROX_LOCATION
+ callback = new GeolocationSettingsCallback();
+ rv = settingsLock->Get(GEO_ALA_APPROX_DISTANCE, callback);
+ NS_ENSURE_SUCCESS(rv, rv);
+#endif
+ callback = new GeolocationSettingsCallback();
+ rv = settingsLock->Get(GEO_ALA_FIXED_COORDS, callback);
+ NS_ENSURE_SUCCESS(rv, rv);
+ callback = new GeolocationSettingsCallback();
+ rv = settingsLock->Get(GEO_ALA_APP_SETTINGS, callback);
+ NS_ENSURE_SUCCESS(rv, rv);
+ callback = new GeolocationSettingsCallback();
+ rv = settingsLock->Get(GEO_ALA_ALWAYS_PRECISE, callback);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ } else {
+ // If we cannot obtain the settings service, we continue
+ // assuming that the geolocation is enabled:
+ sGeoInitPending = false;
+ }
+
+ // geolocation service can be enabled -> now register observer
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ if (!obs) {
+ return NS_ERROR_FAILURE;
+ }
+
+ obs->AddObserver(this, "quit-application", false);
+ obs->AddObserver(this, "mozsettings-changed", false);
+
+#ifdef MOZ_ENABLE_QT5GEOPOSITION
+ mProvider = new QTMLocationProvider();
+#endif
+
+#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_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)) {
+ 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()
+{
+}
+
+void
+nsGeolocationService::HandleMozsettingChanged(nsISupports* aSubject)
+{
+ // The string that we're interested in will be a JSON string that looks like:
+ // {"key":"gelocation.enabled","value":true}
+
+ RootedDictionary<SettingChangeNotification> setting(nsContentUtils::RootingCxForThread());
+ if (!WrappedJSToDictionary(aSubject, setting)) {
+ return;
+ }
+ if (!setting.mKey.EqualsASCII(GEO_SETTINGS_ENABLED)) {
+ return;
+ }
+ if (!setting.mValue.isBoolean()) {
+ return;
+ }
+
+ GPSLOG("mozsetting changed: %s == %s",
+ NS_ConvertUTF16toUTF8(setting.mKey).get(),
+ (setting.mValue.toBoolean() ? "TRUE" : "FALSE"));
+
+ HandleMozsettingValue(setting.mValue.toBoolean());
+}
+
+void
+nsGeolocationService::HandleMozsettingValue(const bool aValue)
+{
+ if (!aValue) {
+ // turn things off
+ StopDevice();
+ Update(nullptr);
+ mLastPosition.position = nullptr;
+ sGeoEnabled = false;
+ } else {
+ sGeoEnabled = true;
+ }
+
+ if (sGeoInitPending) {
+ sGeoInitPending = false;
+ for (uint32_t i = 0, length = mGeolocators.Length(); i < length; ++i) {
+ mGeolocators[i]->ServiceReady();
+ }
+ }
+}
+
+NS_IMETHODIMP
+nsGeolocationService::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aData)
+{
+ if (!strcmp("quit-application", aTopic)) {
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ if (obs) {
+ obs->RemoveObserver(this, "quit-application");
+ obs->RemoveObserver(this, "mozsettings-changed");
+ }
+
+ for (uint32_t i = 0; i< mGeolocators.Length(); i++) {
+ mGeolocators[i]->Shutdown();
+ }
+ StopDevice();
+
+ return NS_OK;
+ }
+
+ if (!strcmp("mozsettings-changed", aTopic)) {
+ HandleMozsettingChanged(aSubject);
+ 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)
+{
+ SetCachedPosition(aSomewhere);
+
+ for (uint32_t i = 0; i< mGeolocators.Length(); i++) {
+ mGeolocators[i]->Update(aSomewhere);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGeolocationService::LocationUpdatePending()
+{
+ for (uint32_t i = 0; i< mGeolocators.Length(); i++) {
+ mGeolocators[i]->LocationUpdatePending();
+ }
+
+ 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 || sGeoInitPending) {
+ 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_GetProcessType() == GoannaProcessType_Content) {
+ 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",
+ MOZ_UTF16("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_GetProcessType() == GoannaProcessType_Content) {
+ ContentChild* cpc = ContentChild::GetSingleton();
+ if (cpc->IsAlive()) {
+ cpc->SendSetGeolocationHigherAccuracy(highRequired);
+ }
+ return;
+ }
+
+ if (!mHigherAccuracy && highRequired) {
+ mProvider->SetHighAccuracy(true);
+ }
+
+ if (mHigherAccuracy && !highRequired) {
+ mProvider->SetHighAccuracy(false);
+ }
+
+ mHigherAccuracy = highRequired;
+}
+
+void
+nsGeolocationService::StopDevice()
+{
+ if(mDisconnectTimer) {
+ mDisconnectTimer->Cancel();
+ mDisconnectTimer = nullptr;
+ }
+
+ if (XRE_GetProcessType() == GoannaProcessType_Content) {
+ 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",
+ MOZ_UTF16("shutdown"));
+}
+
+StaticRefPtr<nsGeolocationService> nsGeolocationService::sService;
+
+already_AddRefed<nsGeolocationService>
+nsGeolocationService::GetGeolocationService()
+{
+ nsRefPtr<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()
+: mLastWatchId(0)
+{
+}
+
+Geolocation::~Geolocation()
+{
+ if (mService) {
+ Shutdown();
+ }
+}
+
+nsresult
+Geolocation::Init(nsIDOMWindow* aContentDom)
+{
+ // Remember the window
+ if (aContentDom) {
+ nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aContentDom);
+ if (!window) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mOwner = do_GetWeakReference(window->GetCurrentInnerWindow());
+ if (!mOwner) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Grab the principal of the document
+ nsCOMPtr<nsIDocument> doc = window->GetDoc();
+ if (!doc) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mPrincipal = doc->NodePrincipal();
+ }
+
+ // 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;
+}
+
+nsIDOMWindow*
+Geolocation::GetParentObject() const {
+ nsCOMPtr<nsPIDOMWindow> 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::LocationUpdatePending()
+{
+ // this event is only really interesting for watch callbacks
+ for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) {
+ mWatchingCallbacks[i]->LocationUpdatePending();
+ }
+
+ 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)
+{
+ GeoPositionCallback successCallback(&aCallback);
+ GeoPositionErrorCallback errorCallback(aErrorCallback);
+
+ nsresult rv = GetCurrentPosition(successCallback, errorCallback,
+ CreatePositionOptionsCopy(aOptions));
+
+ if (NS_FAILED(rv)) {
+ aRv.Throw(rv);
+ }
+
+ return;
+}
+
+NS_IMETHODIMP
+Geolocation::GetCurrentPosition(nsIDOMGeoPositionCallback* aCallback,
+ nsIDOMGeoPositionErrorCallback* aErrorCallback,
+ PositionOptions* aOptions)
+{
+ NS_ENSURE_ARG_POINTER(aCallback);
+
+ GeoPositionCallback successCallback(aCallback);
+ GeoPositionErrorCallback errorCallback(aErrorCallback);
+
+ return GetCurrentPosition(successCallback, errorCallback, aOptions);
+}
+
+nsresult
+Geolocation::GetCurrentPosition(GeoPositionCallback& callback,
+ GeoPositionErrorCallback& errorCallback,
+ PositionOptions *options)
+{
+ if (mPendingCallbacks.Length() > MAX_GEO_REQUESTS_PER_WINDOW) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsRefPtr<nsGeolocationRequest> request = new nsGeolocationRequest(this,
+ callback,
+ errorCallback,
+ options,
+ false);
+
+ if (!sGeoEnabled) {
+ nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(false, request);
+ NS_DispatchToMainThread(ev);
+ return NS_OK;
+ }
+
+ if (!mOwner && !nsContentUtils::IsCallerChrome()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (sGeoInitPending) {
+ mPendingRequests.AppendElement(request);
+ return NS_OK;
+ }
+
+ return GetCurrentPositionReady(request);
+}
+
+nsresult
+Geolocation::GetCurrentPositionReady(nsGeolocationRequest* aRequest)
+{
+ if (mOwner) {
+ if (!RegisterRequestWithPrompt(aRequest)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ return NS_OK;
+ }
+
+ if (!nsContentUtils::IsCallerChrome()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(true, aRequest);
+ NS_DispatchToMainThread(ev);
+
+ return NS_OK;
+}
+
+int32_t
+Geolocation::WatchPosition(PositionCallback& aCallback,
+ PositionErrorCallback* aErrorCallback,
+ const PositionOptions& aOptions,
+ ErrorResult& aRv)
+{
+ int32_t ret;
+ GeoPositionCallback successCallback(&aCallback);
+ GeoPositionErrorCallback errorCallback(aErrorCallback);
+
+ nsresult rv = WatchPosition(successCallback, errorCallback,
+ CreatePositionOptionsCopy(aOptions), &ret);
+
+ if (NS_FAILED(rv)) {
+ aRv.Throw(rv);
+ }
+
+ return ret;
+}
+
+NS_IMETHODIMP
+Geolocation::WatchPosition(nsIDOMGeoPositionCallback *aCallback,
+ nsIDOMGeoPositionErrorCallback *aErrorCallback,
+ PositionOptions *aOptions,
+ int32_t* aRv)
+{
+ NS_ENSURE_ARG_POINTER(aCallback);
+
+ GeoPositionCallback successCallback(aCallback);
+ GeoPositionErrorCallback errorCallback(aErrorCallback);
+
+ return WatchPosition(successCallback, errorCallback, aOptions, aRv);
+}
+
+nsresult
+Geolocation::WatchPosition(GeoPositionCallback& aCallback,
+ GeoPositionErrorCallback& aErrorCallback,
+ PositionOptions* aOptions,
+ int32_t* aRv)
+{
+ if (mWatchingCallbacks.Length() > MAX_GEO_REQUESTS_PER_WINDOW) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ // The watch ID:
+ *aRv = mLastWatchId++;
+
+ nsRefPtr<nsGeolocationRequest> request = new nsGeolocationRequest(this,
+ aCallback,
+ aErrorCallback,
+ aOptions,
+ true,
+ *aRv);
+
+ if (!sGeoEnabled) {
+ GPSLOG("request allow event");
+ nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(false, request);
+ NS_DispatchToMainThread(ev);
+ return NS_OK;
+ }
+
+ if (!mOwner && !nsContentUtils::IsCallerChrome()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (sGeoInitPending) {
+ mPendingRequests.AppendElement(request);
+ return NS_OK;
+ }
+
+ return WatchPositionReady(request);
+}
+
+nsresult
+Geolocation::WatchPositionReady(nsGeolocationRequest* aRequest)
+{
+ if (mOwner) {
+ if (!RegisterRequestWithPrompt(aRequest))
+ return NS_ERROR_NOT_AVAILABLE;
+
+ return NS_OK;
+ }
+
+ if (!nsContentUtils::IsCallerChrome()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ aRequest->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;
+}
+
+void
+Geolocation::ServiceReady()
+{
+ for (uint32_t length = mPendingRequests.Length(); length > 0; --length) {
+ if (mPendingRequests[0]->IsWatch()) {
+ WatchPositionReady(mPendingRequests[0]);
+ } else {
+ GetCurrentPositionReady(mPendingRequests[0]);
+ }
+
+ mPendingRequests.RemoveElementAt(0);
+ }
+}
+
+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<nsPIDOMWindow> window = do_QueryReferent(mOwner);
+
+ if (window) {
+ bool closed = false;
+ window->GetClosed(&closed);
+ if (closed) {
+ return false;
+ }
+
+ nsPIDOMWindow* outer = window->GetOuterWindow();
+ if (!outer || outer->GetCurrentInnerWindow() != window) {
+ 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)
+{
+ return GeolocationBinding::Wrap(aCtx, this);
+}
diff --git a/dom/geolocation/nsGeolocation.h b/dom/geolocation/nsGeolocation.h
new file mode 100644
index 000000000..d71d48b25
--- /dev/null
+++ b/dom/geolocation/nsGeolocation.h
@@ -0,0 +1,258 @@
+/* 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 "nsAutoPtr.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 "nsIDOMWindow.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;
+}
+}
+
+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();
+
+ void HandleMozsettingChanged(nsISupports* aSubject);
+ void HandleMozsettingValue(const bool aValue);
+
+ // 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(nsIDOMWindow* contentDom=nullptr);
+
+ nsIDOMWindow* GetParentObject() const;
+ virtual JSObject* WrapObject(JSContext *aCtx) 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 widnow still exists
+ bool WindowOwnerStillExists();
+
+ // Check to see if any active request requires high accuracy
+ bool HighAccuracyRequested();
+
+ // Notification from the service:
+ void ServiceReady();
+
+private:
+
+ ~Geolocation();
+
+ nsresult GetCurrentPosition(GeoPositionCallback& aCallback, GeoPositionErrorCallback& aErrorCallback, PositionOptions* aOptions);
+ nsresult WatchPosition(GeoPositionCallback& aCallback, GeoPositionErrorCallback& aErrorCallback, PositionOptions* aOptions, int32_t* aRv);
+
+ bool RegisterRequestWithPrompt(nsGeolocationRequest* request);
+
+ // Methods for the service when it's ready to process requests:
+ nsresult GetCurrentPositionReady(nsGeolocationRequest* aRequest);
+ nsresult WatchPositionReady(nsGeolocationRequest* aRequest);
+
+ // 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<nsRefPtr<nsGeolocationRequest> > mPendingCallbacks;
+ nsTArray<nsRefPtr<nsGeolocationRequest> > mWatchingCallbacks;
+
+ // window that this was created for. Weak reference.
+ nsWeakPtr mOwner;
+
+ // where the content was loaded from
+ nsCOMPtr<nsIPrincipal> mPrincipal;
+
+ // owning back pointer.
+ nsRefPtr<nsGeolocationService> mService;
+
+ // Watch ID
+ uint32_t mLastWatchId;
+
+ // Pending requests are used when the service is not ready
+ nsTArray<nsRefPtr<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) override;
+
+ int16_t Code() const {
+ return mCode;
+ }
+
+ void NotifyCallback(const GeoPositionErrorCallback& callback);
+private:
+ ~PositionError();
+ int16_t mCode;
+ nsRefPtr<Geolocation> mParent;
+};
+
+}
+
+inline nsISupports*
+ToSupports(dom::Geolocation* aGeolocation)
+{
+ return ToSupports(static_cast<nsIDOMGeoGeolocation*>(aGeolocation));
+}
+}
+
+#endif /* nsGeoLocation_h */
diff --git a/dom/geolocation/nsGeolocationSettings.cpp b/dom/geolocation/nsGeolocationSettings.cpp
new file mode 100644
index 000000000..c186c3e4e
--- /dev/null
+++ b/dom/geolocation/nsGeolocationSettings.cpp
@@ -0,0 +1,464 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsISettingsService.h"
+
+#include "nsGeolocation.h"
+#include "nsGeolocationSettings.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/dom/PermissionMessageUtils.h"
+#include "mozilla/dom/SettingChangeNotificationBinding.h"
+
+#include "nsJSUtils.h"
+#include "prdtoa.h"
+
+#define GEO_ALA_TYPE_VALUE_PRECISE "precise"
+#define GEO_ALA_TYPE_VALUE_APPROX "blur"
+#define GEO_ALA_TYPE_VALUE_FIXED "user-defined"
+#define GEO_ALA_TYPE_VALUE_NONE "no-location"
+
+using mozilla::unused;
+using namespace mozilla;
+using namespace mozilla::dom;
+
+NS_IMPL_ISUPPORTS(nsGeolocationSettings, nsIObserver)
+
+StaticRefPtr<nsGeolocationSettings> nsGeolocationSettings::sSettings;
+
+already_AddRefed<nsGeolocationSettings>
+nsGeolocationSettings::GetGeolocationSettings()
+{
+ // this singleton is only needed in the parent process...
+ if (XRE_GetProcessType() == GoannaProcessType_Content) {
+ return nullptr;
+ }
+
+ nsRefPtr<nsGeolocationSettings> result;
+ if (nsGeolocationSettings::sSettings) {
+ result = nsGeolocationSettings::sSettings;
+ return result.forget();
+ }
+
+ result = new nsGeolocationSettings();
+ if (NS_FAILED(result->Init())) {
+ return nullptr;
+ }
+ ClearOnShutdown(&nsGeolocationSettings::sSettings);
+ nsGeolocationSettings::sSettings = result;
+ return result.forget();
+}
+
+nsresult nsGeolocationSettings::Init()
+{
+ // query for the current settings...
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ if (!obs) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // hook up observers
+ obs->AddObserver(this, "quit-application", false);
+ obs->AddObserver(this, "mozsettings-changed", false);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGeolocationSettings::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aData)
+{
+ // remove observers
+ if (!strcmp("quit-application", aTopic)) {
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ if (obs) {
+ obs->RemoveObserver(this, "quit-application");
+ obs->RemoveObserver(this, "mozsettings-changed");
+ }
+ return NS_OK;
+ }
+
+ if (!strcmp("mozsettings-changed", aTopic)) {
+ HandleMozsettingsChanged(aSubject);
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+
+GeolocationSetting
+nsGeolocationSettings::LookupGeolocationSetting(int32_t aWatchID)
+{
+ nsCString *origin;
+ if (!mCurrentWatches.Get(aWatchID, &origin) || !origin) {
+ return mGlobalSetting;
+ }
+
+ // if there is no per-app setting for the given origin, this will
+ // set gb == nullptr
+ GeolocationSetting const * const gb =
+ mPerOriginSettings.Get(NS_ConvertUTF8toUTF16(*origin));
+
+ // return a copy of the per-app or global settings
+ return gb ? *gb : mGlobalSetting;
+}
+
+
+void
+nsGeolocationSettings::HandleGeolocationSettingsChange(const nsAString& aKey,
+ const JS::Value& aVal)
+{
+ if (aKey.EqualsASCII(GEO_ALA_ENABLED)) {
+ HandleGeolocationAlaEnabledChange(aVal);
+ } else if (aKey.EqualsASCII(GEO_ALA_TYPE)) {
+ mGlobalSetting.HandleTypeChange(aVal);
+#ifdef MOZ_APPROX_LOCATION
+ } else if (aKey.EqualsASCII(GEO_ALA_APPROX_DISTANCE)) {
+ mGlobalSetting.HandleApproxDistanceChange(aVal);
+#endif
+ } else if (aKey.EqualsASCII(GEO_ALA_FIXED_COORDS)) {
+ mGlobalSetting.HandleFixedCoordsChange(aVal);
+ } else if (aKey.EqualsASCII(GEO_ALA_APP_SETTINGS)) {
+ HandleGeolocationPerOriginSettingsChange(aVal);
+ } else if (aKey.EqualsASCII(GEO_ALA_ALWAYS_PRECISE)) {
+ HandleGeolocationAlwaysPreciseChange(aVal);
+ }
+}
+
+void
+nsGeolocationSettings::HandleMozsettingsChanged(nsISupports* aSubject)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ RootedDictionary<SettingChangeNotification> setting(nsContentUtils::RootingCx());
+ if (!WrappedJSToDictionary(aSubject, setting)) {
+ return;
+ }
+
+ GPSLOG("mozsettings changed: %s", NS_ConvertUTF16toUTF8(setting.mKey).get());
+
+ // and handle the geolocation settings change...
+ HandleGeolocationSettingsChange(setting.mKey, setting.mValue);
+}
+
+
+void
+nsGeolocationSettings::HandleGeolocationSettingsError(const nsAString& aName)
+{
+ if (aName.EqualsASCII(GEO_ALA_ENABLED)) {
+ GPSLOG("Unable to get value for '" GEO_ALA_ENABLED "'");
+ } else if (aName.EqualsASCII(GEO_ALA_TYPE)) {
+ GPSLOG("Unable to get value for '" GEO_ALA_TYPE "'");
+#ifdef MOZ_APPROX_LOCATION
+ } else if (aName.EqualsASCII(GEO_ALA_APPROX_DISTANCE)) {
+ GPSLOG("Unable to get value for '" GEO_ALA_APPROX_DISTANCE "'");
+#endif
+ } else if (aName.EqualsASCII(GEO_ALA_FIXED_COORDS)) {
+ GPSLOG("Unable to get value for '" GEO_ALA_FIXED_COORDS "'");
+ } else if (aName.EqualsASCII(GEO_ALA_APP_SETTINGS)) {
+ GPSLOG("Unable to get value for '" GEO_ALA_APP_SETTINGS "'");
+ } else if (aName.EqualsASCII(GEO_ALA_ALWAYS_PRECISE)) {
+ GPSLOG("Unable to get value for '" GEO_ALA_ALWAYS_PRECISE "'");
+ }
+}
+
+void
+nsGeolocationSettings::PutWatchOrigin(int32_t aWatchID,
+ const nsCString& aOrigin)
+{
+ if (aWatchID < 0) {
+ return;
+ }
+
+ GPSLOG("mapping watch ID %d to origin %s", aWatchID, aOrigin.get());
+ mCurrentWatches.Put(static_cast<uint32_t>(aWatchID), new nsCString(aOrigin));
+}
+
+void
+nsGeolocationSettings::RemoveWatchOrigin(int32_t aWatchID)
+{
+ GPSLOG("unmapping watch ID %d", aWatchID);
+ mCurrentWatches.Remove(static_cast<uint32_t>(aWatchID));
+}
+
+void
+nsGeolocationSettings::GetWatchOrigin(int32_t aWatchID, nsCString& aOrigin)
+{
+ nsCString *str;
+ if (!mCurrentWatches.Get(aWatchID, &str) || !str) {
+ return;
+ }
+ aOrigin = *str;
+ GPSLOG("returning origin %s for watch ID %d", aOrigin.get(), aWatchID);
+}
+
+void
+nsGeolocationSettings::HandleGeolocationAlaEnabledChange(const JS::Value& aVal)
+{
+ if (!aVal.isBoolean()) {
+ return;
+ }
+
+ mAlaEnabled = aVal.toBoolean();
+}
+
+void
+nsGeolocationSettings::HandleGeolocationPerOriginSettingsChange(const JS::Value& aVal)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!aVal.isObject()) {
+ return;
+ }
+
+ // clear the hash table
+ mPerOriginSettings.Clear();
+
+ // root the object and get the global
+ JS::Rooted<JSObject*> obj(nsContentUtils::RootingCx(), &aVal.toObject());
+ MOZ_ASSERT(obj);
+ nsIGlobalObject* global = xpc::NativeGlobal(obj);
+ NS_ENSURE_TRUE_VOID(global && global->GetGlobalJSObject());
+
+ // because the spec requires calling getters when enumerating the key of a
+ // dictionary
+ AutoEntryScript aes(global);
+ aes.TakeOwnershipOfErrorReporting();
+ JSContext *cx = aes.cx();
+ JS::AutoIdArray ids(cx, JS_Enumerate(cx, obj));
+
+ // if we get no ids then the exception list is empty and we can return here.
+ if (!ids) {
+ return;
+ }
+
+ // go through all of the objects in the exceptions dictionary
+ for (size_t i = 0; i < ids.length(); i++) {
+ JS::RootedId id(cx);
+ id = ids[i];
+
+ // if it is an app that is always precise, skip it
+ nsAutoJSString origin;
+ if (!origin.init(cx, id)) {
+ continue;
+ }
+ if (mAlwaysPreciseApps.Contains(origin)) {
+ continue;
+ }
+
+ // get the app setting object
+ JS::RootedValue propertyValue(cx);
+ if (!JS_GetPropertyById(cx, obj, id, &propertyValue) || !propertyValue.isObject()) {
+ continue;
+ }
+ JS::RootedObject settingObj(cx, &propertyValue.toObject());
+
+ GeolocationSetting *settings = new GeolocationSetting(origin);
+ GPSLOG("adding exception for %s", NS_ConvertUTF16toUTF8(origin).get());
+
+ // get the geolocation type
+ JS::RootedValue fm(cx);
+ if (JS_GetProperty(cx, settingObj, "type", &fm)) {
+ settings->HandleTypeChange(fm);
+ }
+
+#ifdef MOZ_APPROX_LOCATION
+ // get the approximate distance if there is one
+ JS::RootedValue distance(cx);
+ if (JS_GetProperty(cx, settingObj, "distance", &distance)) {
+ settings->HandleApproxDistanceChange(distance);
+ }
+#endif
+
+ // get and parse the coords, if any
+ JS::RootedValue coords(cx);
+ if (JS_GetProperty(cx, settingObj, "coords", &coords)) {
+ settings->HandleFixedCoordsChange(coords);
+ }
+
+ // add the per-app setting object to the hashtable
+ mPerOriginSettings.Put(origin, settings);
+ }
+}
+
+void
+nsGeolocationSettings::HandleGeolocationAlwaysPreciseChange(const JS::Value& aVal)
+{
+ if (!aVal.isObject()) {
+ return;
+ }
+
+ // clear the list of apps that are always precise
+ mAlwaysPreciseApps.Clear();
+
+ // root the object and get the global
+ JS::Rooted<JSObject*> obj(nsContentUtils::RootingCx(), &aVal.toObject());
+ MOZ_ASSERT(obj);
+ nsIGlobalObject* global = xpc::NativeGlobal(obj);
+ NS_ENSURE_TRUE_VOID(global && global->GetGlobalJSObject());
+
+ // the spec requires calling getters when accessing array by index
+ AutoEntryScript aes(global);
+ aes.TakeOwnershipOfErrorReporting();
+ JSContext *cx = aes.cx();
+
+ if (!JS_IsArrayObject(cx, obj)) {
+ return;
+ }
+
+ uint32_t length;
+ if (!JS_GetArrayLength(cx, obj, &length)) {
+ return;
+ }
+
+ // process the list of apps...
+ for (uint32_t i = 0; i < length; ++i) {
+ JS::RootedValue value(cx);
+
+ if (!JS_GetElement(cx, obj, i, &value) || !value.isString()) {
+ continue;
+ }
+
+ nsAutoJSString origin;
+ if (!origin.init(cx, value)) {
+ continue;
+ }
+
+ GPSLOG("adding always precise for %s", NS_ConvertUTF16toUTF8(origin).get());
+
+ // add the origin to the list of apps that will always receive
+ // precise location information
+ mAlwaysPreciseApps.AppendElement(origin);
+ }
+}
+
+
+void
+GeolocationSetting::HandleTypeChange(const JS::Value& aVal)
+{
+ nsAutoJSString str;
+ if (!str.init(aVal)) {
+ return;
+ }
+
+ GeolocationFuzzMethod fm = GEO_ALA_TYPE_DEFAULT;
+ if (str.EqualsASCII(GEO_ALA_TYPE_VALUE_PRECISE)) {
+ fm = GEO_ALA_TYPE_PRECISE;
+ } else if (str.EqualsASCII(GEO_ALA_TYPE_VALUE_APPROX)) {
+#ifdef MOZ_APPROX_LOCATION
+ fm = GEO_ALA_TYPE_APPROX;
+#else
+ // we are loading a profile from a build with MOZ_APPROX_LOCATION
+ // enabled, then we need to force the type to a sane value
+ fm = GEO_ALA_TYPE_NONE;
+#endif
+ } else if (str.EqualsASCII(GEO_ALA_TYPE_VALUE_FIXED)) {
+ fm = GEO_ALA_TYPE_FIXED;
+ } else if (str.EqualsASCII(GEO_ALA_TYPE_VALUE_NONE)) {
+ fm = GEO_ALA_TYPE_NONE;
+ }
+
+ if ((fm >= GEO_ALA_TYPE_FIRST) && (fm <= GEO_ALA_TYPE_LAST)) {
+ GPSLOG("changing type for %s to %s (%d)",
+ (mOrigin.IsEmpty() ? "global" : NS_ConvertUTF16toUTF8(mOrigin).get()),
+ NS_ConvertUTF16toUTF8(str).get(),
+ static_cast<int>(fm));
+ mFuzzMethod = fm;
+ }
+
+ // based on the new type, we need to clean up the other settings
+ switch (mFuzzMethod) {
+ case GEO_ALA_TYPE_PRECISE:
+ case GEO_ALA_TYPE_NONE:
+#ifdef MOZ_APPROX_LOCATION
+ mDistance = 0;
+#endif
+ mLatitude = 0.0;
+ mLongitude = 0.0;
+ break;
+#ifdef MOZ_APPROX_LOCATION
+ case GEO_ALA_TYPE_APPROX:
+ mLatitude = 0.0;
+ mLongitude = 0.0;
+ break;
+#endif
+ case GEO_ALA_TYPE_FIXED:
+#ifdef MOZ_APPROX_LOCATION
+ mDistance = 0;
+#endif
+ break;
+ }
+}
+
+#ifdef MOZ_APPROX_LOCATION
+void
+GeolocationSetting::HandleApproxDistanceChange(const JS::Value& aVal)
+{
+ if (!aVal.isInt32()) {
+ return;
+ }
+
+ GPSLOG("changing approx distance for %s to %d",
+ (mOrigin.IsEmpty() ? "global" : NS_ConvertUTF16toUTF8(mOrigin).get()),
+ aVal.toInt32());
+
+ // set the approximate distance
+ mDistance = aVal.toInt32();
+}
+#endif
+
+
+void
+GeolocationSetting::HandleFixedCoordsChange(const JS::Value& aVal)
+{
+ nsAutoJSString str;
+ if (!str.init(aVal)) {
+ return;
+ }
+
+ // parse the string and store the global lat/lon
+ // the string is in the form of @1.2345,6.7890
+ // check for leading '@' and a comma in the middle
+ int32_t const comma = str.Find(",");
+ if ( (str.CharAt(0) != '@') || (comma == -1) ) {
+ return;
+ }
+
+ // pull the lat and lon out of the string and convert to doubles
+ nsresult rv;
+ nsString slat(Substring(str, 1, comma - 1));
+ nsString slon(Substring(str, comma + 1));
+ double lat = slat.ToDouble(&rv);
+ NS_ENSURE_SUCCESS(rv,);
+ double lon = slon.ToDouble(&rv);
+ NS_ENSURE_SUCCESS(rv,);
+
+ // store the values
+ mLatitude = lat;
+ mLongitude = lon;
+
+ GPSLOG("changing coords for %s to %s (%f, %f)",
+ (mOrigin.IsEmpty() ? "global" : NS_ConvertUTF16toUTF8(mOrigin).get()),
+ NS_ConvertUTF16toUTF8(str).get(),
+ mLatitude, mLongitude);
+}
+
diff --git a/dom/geolocation/nsGeolocationSettings.h b/dom/geolocation/nsGeolocationSettings.h
new file mode 100644
index 000000000..a5b112947
--- /dev/null
+++ b/dom/geolocation/nsGeolocationSettings.h
@@ -0,0 +1,165 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 nsGeolocationSettings_h
+#define nsGeolocationSettings_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/StaticPtr.h"
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "nsClassHashtable.h"
+#include "nsString.h"
+#include "nsIObserver.h"
+#include "nsJSUtils.h"
+#include "nsTArray.h"
+
+#if (defined(MOZ_GPS_DEBUG) && defined(ANDROID))
+#include <android/log.h>
+#define GPSLOG(fmt, ...) __android_log_print(ANDROID_LOG_WARN, "GPS", "%12s:%-5d " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
+#else
+#define GPSLOG(...) {;}
+#endif // MOZ_GPS_DEBUG && ANDROID
+
+// The settings key.
+#define GEO_ENABLED "geolocation.enabled"
+#define GEO_ALA_ENABLED "ala.settings.enabled"
+#define GEO_ALA_TYPE "geolocation.type"
+#define GEO_ALA_FIXED_COORDS "geolocation.fixed_coords"
+#define GEO_ALA_APP_SETTINGS "geolocation.app_settings"
+#define GEO_ALA_ALWAYS_PRECISE "geolocation.always_precise"
+#ifdef MOZ_APPROX_LOCATION
+#define GEO_ALA_APPROX_DISTANCE "geolocation.approx_distance"
+#endif
+
+enum GeolocationFuzzMethod {
+ GEO_ALA_TYPE_PRECISE, // default, GPS/AGPS location
+ GEO_ALA_TYPE_FIXED, // user supplied lat/long
+ GEO_ALA_TYPE_NONE, // no location given
+#ifdef MOZ_APPROX_LOCATION
+ GEO_ALA_TYPE_APPROX // approximate, grid-based location
+#endif
+};
+
+#define GEO_ALA_TYPE_DEFAULT (GEO_ALA_TYPE_PRECISE)
+#define GEO_ALA_TYPE_FIRST (GEO_ALA_TYPE_PRECISE)
+#ifdef MOZ_APPROX_LOCATION
+#define GEO_ALA_TYPE_LAST (GEO_ALA_TYPE_APPROX)
+#else
+#define GEO_ALA_TYPE_LAST (GEO_ALA_TYPE_NONE)
+#endif
+
+/**
+ * Simple class for holding the geolocation settings values.
+ */
+
+class GeolocationSetting final {
+public:
+ explicit GeolocationSetting(const nsString& aOrigin) :
+ mFuzzMethod(GEO_ALA_TYPE_DEFAULT),
+#ifdef MOZ_APPROX_LOCATION
+ mDistance(0),
+#endif
+ mLatitude(0.0),
+ mLongitude(0.0),
+ mOrigin(aOrigin) {}
+
+ GeolocationSetting(const GeolocationSetting& rhs) :
+ mFuzzMethod(rhs.mFuzzMethod),
+#ifdef MOZ_APPROX_LOCATION
+ mDistance(rhs.mDistance),
+#endif
+ mLatitude(rhs.mLatitude),
+ mLongitude(rhs.mLongitude),
+ mOrigin(rhs.mOrigin) {}
+
+ ~GeolocationSetting() {}
+
+ GeolocationSetting& operator=(const GeolocationSetting& rhs) {
+ mFuzzMethod = rhs.mFuzzMethod;
+#ifdef MOZ_APPROX_LOCATION
+ mDistance = rhs.mDistance;
+#endif
+ mLatitude = rhs.mLatitude;
+ mLongitude = rhs.mLongitude;
+ mOrigin = rhs.mOrigin;
+ return *this;
+ }
+
+ void HandleTypeChange(const JS::Value& aVal);
+ void HandleApproxDistanceChange(const JS::Value& aVal);
+ void HandleFixedCoordsChange(const JS::Value& aVal);
+
+ inline GeolocationFuzzMethod GetType() const { return mFuzzMethod; }
+#ifdef MOZ_APPROX_LOCATION
+ inline int32_t GetApproxDistance() const { return mDistance; }
+#endif
+ inline double GetFixedLatitude() const { return mLatitude; }
+ inline double GetFixedLongitude() const { return mLongitude; }
+ inline const nsString& GetOrigin() const { return mOrigin; }
+
+private:
+ GeolocationSetting() {} // can't default construct
+ GeolocationFuzzMethod mFuzzMethod;
+#ifdef MOZ_APPROX_LOCATION
+ int32_t mDistance;
+#endif
+ double mLatitude,
+ mLongitude;
+ nsString mOrigin;
+};
+
+/**
+ * Singleton that holds the global and per-origin geolocation settings.
+ */
+class nsGeolocationSettings final : public nsIObserver
+{
+public:
+ static already_AddRefed<nsGeolocationSettings> GetGeolocationSettings();
+ static mozilla::StaticRefPtr<nsGeolocationSettings> sSettings;
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ nsGeolocationSettings() : mAlaEnabled(false), mGlobalSetting(NullString()) {}
+ nsresult Init();
+
+ void HandleGeolocationSettingsChange(const nsAString& aKey, const JS::Value& aVal);
+ void HandleGeolocationSettingsError(const nsAString& aName);
+
+ void PutWatchOrigin(int32_t aWatchID, const nsCString& aOrigin);
+ void RemoveWatchOrigin(int32_t aWatchID);
+ void GetWatchOrigin(int32_t aWatchID, nsCString& aOrigin);
+ inline bool IsAlaEnabled() const { return mAlaEnabled; }
+
+ // given a watch ID, retrieve the geolocation settings. the watch ID is
+ // mapped to the origin of the listener/request which is then used to
+ // retreive the geolocation settings for the origin.
+ // if the origin is in the always-precise list, the settings will always be
+ // 'precise'. if the origin has origin-specific settings, that will be returned
+ // otherwise the global geolocation settings will be returned.
+ // NOTE: this returns a copy of the settings to enforce read-only client access
+ GeolocationSetting LookupGeolocationSetting(int32_t aWatchID);
+
+private:
+ ~nsGeolocationSettings() {}
+ nsGeolocationSettings(const nsGeolocationSettings&) :
+ mGlobalSetting(NullString()) {} // can't copy obj
+
+ void HandleMozsettingsChanged(nsISupports* aSubject);
+ void HandleGeolocationAlaEnabledChange(const JS::Value& aVal);
+ void HandleGeolocationPerOriginSettingsChange(const JS::Value& aVal);
+ void HandleGeolocationAlwaysPreciseChange(const JS::Value& aVal);
+
+private:
+ bool mAlaEnabled;
+ GeolocationSetting mGlobalSetting;
+ nsClassHashtable<nsStringHashKey, GeolocationSetting> mPerOriginSettings;
+ nsTArray<nsString> mAlwaysPreciseApps;
+ nsClassHashtable<nsUint32HashKey, nsCString> mCurrentWatches;
+};
+
+#endif /* nsGeolocationSettings_h */
+