diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /dom/geolocation | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | uxp-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz |
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/geolocation')
-rw-r--r-- | dom/geolocation/MLSFallback.cpp | 82 | ||||
-rw-r--r-- | dom/geolocation/MLSFallback.h | 48 | ||||
-rw-r--r-- | dom/geolocation/moz.build | 50 | ||||
-rw-r--r-- | dom/geolocation/nsGeoPosition.cpp | 263 | ||||
-rw-r--r-- | dom/geolocation/nsGeoPosition.h | 145 | ||||
-rw-r--r-- | dom/geolocation/nsGeoPositionIPCSerialiser.h | 152 | ||||
-rw-r--r-- | dom/geolocation/nsGeolocation.cpp | 1432 | ||||
-rw-r--r-- | dom/geolocation/nsGeolocation.h | 260 |
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 */ |