diff options
Diffstat (limited to 'dom/system/windows/location')
-rw-r--r-- | dom/system/windows/location/PWindowsLocation.ipdl | 38 | ||||
-rw-r--r-- | dom/system/windows/location/WindowsLocationChild.cpp | 257 | ||||
-rw-r--r-- | dom/system/windows/location/WindowsLocationChild.h | 44 | ||||
-rw-r--r-- | dom/system/windows/location/WindowsLocationParent.cpp | 36 | ||||
-rw-r--r-- | dom/system/windows/location/WindowsLocationParent.h | 52 | ||||
-rw-r--r-- | dom/system/windows/location/WindowsLocationProvider.cpp | 350 | ||||
-rw-r--r-- | dom/system/windows/location/WindowsLocationProvider.h | 78 | ||||
-rw-r--r-- | dom/system/windows/location/moz.build | 29 |
8 files changed, 884 insertions, 0 deletions
diff --git a/dom/system/windows/location/PWindowsLocation.ipdl b/dom/system/windows/location/PWindowsLocation.ipdl new file mode 100644 index 0000000000..db53b5c805 --- /dev/null +++ b/dom/system/windows/location/PWindowsLocation.ipdl @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et ft=cpp : */
+/* 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 protocol PWindowsUtils;
+
+[RefCounted] using class nsIDOMGeoPosition from "nsGeoPositionIPCSerialiser.h";
+
+namespace mozilla {
+namespace dom {
+
+// Proxies geolocation functions to a utility process so that we
+// can safely handle crashes in the ILocation API. Messages to the child
+// are proxies for the ILocation COM object. Messages to the parent
+// are proxied nsIGeolocationUpdate callbacks.
+protocol PWindowsLocation {
+ manager PWindowsUtils;
+
+child:
+ async Startup();
+ async RegisterForReport();
+ async UnregisterForReport();
+ async SetHighAccuracy(bool aEnable);
+
+ async __delete__();
+
+parent:
+ // Update geolocation with new position information.
+ async Update(nullable nsIDOMGeoPosition aPosition);
+
+ // The geolocation API has reported an error.
+ async Failed(uint16_t aError);
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/system/windows/location/WindowsLocationChild.cpp b/dom/system/windows/location/WindowsLocationChild.cpp new file mode 100644 index 0000000000..514ae4610f --- /dev/null +++ b/dom/system/windows/location/WindowsLocationChild.cpp @@ -0,0 +1,257 @@ +/* -*- 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 "WindowsLocationChild.h" +#include "nsCOMPtr.h" +#include "WindowsLocationProvider.h" +#include "mozilla/dom/GeolocationPosition.h" +#include "mozilla/dom/GeolocationPositionErrorBinding.h" +#include "mozilla/Telemetry.h" +#include "nsIGeolocationProvider.h" + +#include <locationapi.h> + +namespace mozilla::dom { + +extern LazyLogModule gWindowsLocationProviderLog; +#define LOG(...) \ + MOZ_LOG(gWindowsLocationProviderLog, LogLevel::Debug, (__VA_ARGS__)) + +class LocationEvent final : public ILocationEvents { + public: + explicit LocationEvent(WindowsLocationChild* aActor) + : mActor(aActor), mRefCnt(0) {} + + // IUnknown interface + STDMETHODIMP_(ULONG) AddRef() override; + STDMETHODIMP_(ULONG) Release() override; + STDMETHODIMP QueryInterface(REFIID iid, void** ppv) override; + + // ILocationEvents interface + STDMETHODIMP OnStatusChanged(REFIID aReportType, + LOCATION_REPORT_STATUS aStatus) override; + STDMETHODIMP OnLocationChanged(REFIID aReportType, + ILocationReport* aReport) override; + + private: + // Making this a WeakPtr breaks the following cycle of strong references: + // WindowsLocationChild -> ILocation -> ILocationEvents (this) + // -> WindowsLocationChild. + WeakPtr<WindowsLocationChild> mActor; + + ULONG mRefCnt; +}; + +STDMETHODIMP_(ULONG) +LocationEvent::AddRef() { return InterlockedIncrement(&mRefCnt); } + +STDMETHODIMP_(ULONG) +LocationEvent::Release() { + ULONG count = InterlockedDecrement(&mRefCnt); + if (!count) { + delete this; + return 0; + } + return count; +} + +STDMETHODIMP +LocationEvent::QueryInterface(REFIID iid, void** ppv) { + if (!ppv) { + return E_INVALIDARG; + } + + if (iid == IID_IUnknown) { + *ppv = static_cast<IUnknown*>(this); + } else if (iid == IID_ILocationEvents) { + *ppv = static_cast<ILocationEvents*>(this); + } else { + *ppv = nullptr; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +STDMETHODIMP +LocationEvent::OnStatusChanged(REFIID aReportType, + LOCATION_REPORT_STATUS aStatus) { + LOG("LocationEvent::OnStatusChanged(%p, %p, %s, %04x)", this, mActor.get(), + aReportType == IID_ILatLongReport ? "true" : "false", + static_cast<uint32_t>(aStatus)); + + if (!mActor || aReportType != IID_ILatLongReport) { + return S_OK; + } + + // When registering event, REPORT_INITIALIZING is fired at first. + // Then, when the location is found, REPORT_RUNNING is fired. + // We ignore those messages. + uint16_t err; + switch (aStatus) { + case REPORT_ACCESS_DENIED: + err = GeolocationPositionError_Binding::PERMISSION_DENIED; + break; + case REPORT_NOT_SUPPORTED: + case REPORT_ERROR: + err = GeolocationPositionError_Binding::POSITION_UNAVAILABLE; + break; + default: + return S_OK; + } + + mActor->SendFailed(err); + return S_OK; +} + +STDMETHODIMP +LocationEvent::OnLocationChanged(REFIID aReportType, ILocationReport* aReport) { + LOG("LocationEvent::OnLocationChanged(%p, %p, %s)", this, mActor.get(), + aReportType == IID_ILatLongReport ? "true" : "false"); + + if (!mActor || aReportType != IID_ILatLongReport) { + return S_OK; + } + + RefPtr<ILatLongReport> latLongReport; + if (FAILED(aReport->QueryInterface(IID_ILatLongReport, + getter_AddRefs(latLongReport)))) { + return E_FAIL; + } + + DOUBLE latitude = 0.0; + latLongReport->GetLatitude(&latitude); + + DOUBLE longitude = 0.0; + latLongReport->GetLongitude(&longitude); + + DOUBLE alt = UnspecifiedNaN<double>(); + latLongReport->GetAltitude(&alt); + + DOUBLE herror = 0.0; + latLongReport->GetErrorRadius(&herror); + + DOUBLE verror = UnspecifiedNaN<double>(); + latLongReport->GetAltitudeError(&verror); + + double heading = UnspecifiedNaN<double>(); + double speed = UnspecifiedNaN<double>(); + + // nsGeoPositionCoords will convert NaNs to null for optional properties of + // the JavaScript Coordinates object. + RefPtr<nsGeoPosition> position = + new nsGeoPosition(latitude, longitude, alt, herror, verror, heading, + speed, PR_Now() / PR_USEC_PER_MSEC); + mActor->SendUpdate(position); + + return S_OK; +} + +WindowsLocationChild::WindowsLocationChild() { + LOG("WindowsLocationChild::WindowsLocationChild(%p)", this); +} + +WindowsLocationChild::~WindowsLocationChild() { + LOG("WindowsLocationChild::~WindowsLocationChild(%p)", this); +} + +::mozilla::ipc::IPCResult WindowsLocationChild::RecvStartup() { + LOG("WindowsLocationChild::RecvStartup(%p, %p)", this, mLocation.get()); + if (mLocation) { + return IPC_OK(); + } + + RefPtr<ILocation> location; + if (FAILED(::CoCreateInstance(CLSID_Location, nullptr, CLSCTX_INPROC_SERVER, + IID_ILocation, getter_AddRefs(location)))) { + LOG("WindowsLocationChild(%p) failed to create ILocation", this); + // We will use MLS provider + SendFailed(GeolocationPositionError_Binding::POSITION_UNAVAILABLE); + return IPC_OK(); + } + + IID reportTypes[] = {IID_ILatLongReport}; + if (FAILED(location->RequestPermissions(nullptr, reportTypes, 1, FALSE))) { + LOG("WindowsLocationChild(%p) failed to set ILocation permissions", this); + // We will use MLS provider + SendFailed(GeolocationPositionError_Binding::POSITION_UNAVAILABLE); + return IPC_OK(); + } + + mLocation = location; + return IPC_OK(); +} + +::mozilla::ipc::IPCResult WindowsLocationChild::RecvSetHighAccuracy( + bool aEnable) { + LOG("WindowsLocationChild::RecvSetHighAccuracy(%p, %p, %s)", this, + mLocation.get(), aEnable ? "true" : "false"); + + // We sometimes call SetHighAccuracy before Startup, so we save the + // request and set it later, in RegisterForReport. + mHighAccuracy = aEnable; + + return IPC_OK(); +} + +::mozilla::ipc::IPCResult WindowsLocationChild::RecvRegisterForReport() { + LOG("WindowsLocationChild::RecvRegisterForReport(%p, %p)", this, + mLocation.get()); + + if (!mLocation) { + SendFailed(GeolocationPositionError_Binding::POSITION_UNAVAILABLE); + return IPC_OK(); + } + + LOCATION_DESIRED_ACCURACY desiredAccuracy; + if (mHighAccuracy) { + desiredAccuracy = LOCATION_DESIRED_ACCURACY_HIGH; + } else { + desiredAccuracy = LOCATION_DESIRED_ACCURACY_DEFAULT; + } + + if (NS_WARN_IF(FAILED(mLocation->SetDesiredAccuracy(IID_ILatLongReport, + desiredAccuracy)))) { + SendFailed(GeolocationPositionError_Binding::POSITION_UNAVAILABLE); + return IPC_OK(); + } + + auto event = MakeRefPtr<LocationEvent>(this); + if (NS_WARN_IF( + FAILED(mLocation->RegisterForReport(event, IID_ILatLongReport, 0)))) { + SendFailed(GeolocationPositionError_Binding::POSITION_UNAVAILABLE); + } + + LOG("WindowsLocationChild::RecvRegisterForReport successfully registered"); + return IPC_OK(); +} + +::mozilla::ipc::IPCResult WindowsLocationChild::RecvUnregisterForReport() { + LOG("WindowsLocationChild::RecvUnregisterForReport(%p, %p)", this, + mLocation.get()); + + if (!mLocation) { + return IPC_OK(); + } + + // This will free the LocationEvent we created in RecvRegisterForReport. + Unused << NS_WARN_IF( + FAILED(mLocation->UnregisterForReport(IID_ILatLongReport))); + + // The ILocation object is not reusable. Unregistering, restarting and + // re-registering for reports does not work; the callback is never + // called in that case. For that reason, we re-create the ILocation + // object with a call to Startup after unregistering if we need it again. + mLocation = nullptr; + return IPC_OK(); +} + +void WindowsLocationChild::ActorDestroy(ActorDestroyReason aWhy) { + LOG("WindowsLocationChild::ActorDestroy(%p, %p)", this, mLocation.get()); + mLocation = nullptr; +} + +} // namespace mozilla::dom diff --git a/dom/system/windows/location/WindowsLocationChild.h b/dom/system/windows/location/WindowsLocationChild.h new file mode 100644 index 0000000000..ac51cff213 --- /dev/null +++ b/dom/system/windows/location/WindowsLocationChild.h @@ -0,0 +1,44 @@ +/* -*- 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 mozilla_dom_WindowsLocationChild_h__ +#define mozilla_dom_WindowsLocationChild_h__ + +#include "mozilla/dom/PWindowsLocationChild.h" +#include "mozilla/WeakPtr.h" + +class ILocation; + +namespace mozilla::dom { + +// Geolocation actor in utility process. +class WindowsLocationChild final : public PWindowsLocationChild, + public SupportsWeakPtr { + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WindowsLocationChild, override); + + public: + WindowsLocationChild(); + + using IPCResult = ::mozilla::ipc::IPCResult; + + IPCResult RecvStartup(); + IPCResult RecvRegisterForReport(); + IPCResult RecvUnregisterForReport(); + IPCResult RecvSetHighAccuracy(bool aEnable); + void ActorDestroy(ActorDestroyReason aWhy) override; + + private: + ~WindowsLocationChild() override; + + // The COM object the actors are proxying calls for. + RefPtr<ILocation> mLocation; + + bool mHighAccuracy = false; +}; + +} // namespace mozilla::dom + +#endif // mozilla_dom_WindowsLocationChild_h__ diff --git a/dom/system/windows/location/WindowsLocationParent.cpp b/dom/system/windows/location/WindowsLocationParent.cpp new file mode 100644 index 0000000000..f19bcb8b0d --- /dev/null +++ b/dom/system/windows/location/WindowsLocationParent.cpp @@ -0,0 +1,36 @@ +/* -*- 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 "WindowsLocationParent.h" +#include "nsIDOMGeoPosition.h" +#include "WindowsLocationProvider.h" + +namespace mozilla::dom { + +::mozilla::ipc::IPCResult WindowsLocationParent::RecvUpdate( + RefPtr<nsIDOMGeoPosition> aGeoPosition) { + if (mProvider) { + mProvider->RecvUpdate(aGeoPosition); + } + return IPC_OK(); +} + +// A failure occurred. This may be translated into a +// nsIGeolocationUpdate::NotifyError or may be ignored if the MLS fallback +// is available. +::mozilla::ipc::IPCResult WindowsLocationParent::RecvFailed(uint16_t err) { + if (mProvider) { + mProvider->RecvFailed(err); + } + return IPC_OK(); +} + +void WindowsLocationParent::ActorDestroy(ActorDestroyReason aReason) { + if (mProvider) { + mProvider->ActorStopped(); + } +} + +} // namespace mozilla::dom diff --git a/dom/system/windows/location/WindowsLocationParent.h b/dom/system/windows/location/WindowsLocationParent.h new file mode 100644 index 0000000000..01538246ea --- /dev/null +++ b/dom/system/windows/location/WindowsLocationParent.h @@ -0,0 +1,52 @@ +/* -*- 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 mozilla_dom_WindowsLocationParent_h__ +#define mozilla_dom_WindowsLocationParent_h__ + +#include "nsCOMPtr.h" +#include "mozilla/dom/PWindowsLocationParent.h" + +class nsGeoPosition; +class nsIGeolocationUpdate; + +namespace mozilla::dom { + +class WindowsLocationProvider; + +// Geolocation actor in main process. +// This may receive messages asynchronously, even after it sends Unregister +// to the child. +class WindowsLocationParent final : public PWindowsLocationParent { + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WindowsLocationParent, override); + + using IPCResult = ::mozilla::ipc::IPCResult; + + explicit WindowsLocationParent(WindowsLocationProvider* aProvider) + : mProvider(aProvider) {} + + // Update geolocation with new position information. + IPCResult RecvUpdate(RefPtr<nsIDOMGeoPosition> aGeoPosition); + + // A failure occurred. This may be translated into a + // nsIGeolocationUpdate::NotifyError or may be ignored if the MLS fallback + // is available. + IPCResult RecvFailed(uint16_t err); + + void ActorDestroy(ActorDestroyReason aReason) override; + + // After this, the actor will simply ignore any incoming messages. + void DetachFromLocationProvider() { mProvider = nullptr; } + + private: + ~WindowsLocationParent() override = default; + + WindowsLocationProvider* mProvider; +}; + +} // namespace mozilla::dom + +#endif // mozilla_dom_WindowsLocationParent_h__ diff --git a/dom/system/windows/location/WindowsLocationProvider.cpp b/dom/system/windows/location/WindowsLocationProvider.cpp new file mode 100644 index 0000000000..92a6f2c9cc --- /dev/null +++ b/dom/system/windows/location/WindowsLocationProvider.cpp @@ -0,0 +1,350 @@ +/* -*- 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 "WindowsLocationProvider.h" +#include "WindowsLocationParent.h" +#include "mozilla/dom/WindowsUtilsParent.h" +#include "GeolocationPosition.h" +#include "nsComponentManagerUtils.h" +#include "mozilla/ipc/UtilityProcessManager.h" +#include "mozilla/ipc/UtilityProcessSandboxing.h" +#include "prtime.h" +#include "MLSFallback.h" +#include "mozilla/Attributes.h" +#include "mozilla/Logging.h" +#include "mozilla/FloatingPoint.h" +#include "mozilla/Telemetry.h" +#include "mozilla/dom/GeolocationPositionErrorBinding.h" + +namespace mozilla::dom { + +LazyLogModule gWindowsLocationProviderLog("WindowsLocationProvider"); +#define LOG(...) \ + MOZ_LOG(gWindowsLocationProviderLog, LogLevel::Debug, (__VA_ARGS__)) + +class MLSUpdate : public nsIGeolocationUpdate { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIGEOLOCATIONUPDATE + explicit MLSUpdate(nsIGeolocationUpdate* aCallback) : mCallback(aCallback) {} + + private: + nsCOMPtr<nsIGeolocationUpdate> mCallback; + virtual ~MLSUpdate() {} +}; + +NS_IMPL_ISUPPORTS(MLSUpdate, nsIGeolocationUpdate); + +NS_IMETHODIMP +MLSUpdate::Update(nsIDOMGeoPosition* aPosition) { + if (!mCallback) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr<nsIDOMGeoPositionCoords> coords; + aPosition->GetCoords(getter_AddRefs(coords)); + if (!coords) { + return NS_ERROR_FAILURE; + } + Telemetry::Accumulate(Telemetry::GEOLOCATION_WIN8_SOURCE_IS_MLS, true); + return mCallback->Update(aPosition); +} +NS_IMETHODIMP +MLSUpdate::NotifyError(uint16_t aError) { + if (!mCallback) { + return NS_ERROR_FAILURE; + } + nsCOMPtr<nsIGeolocationUpdate> callback(mCallback); + return callback->NotifyError(aError); +} + +NS_IMPL_ISUPPORTS(WindowsLocationProvider, nsIGeolocationProvider) + +WindowsLocationProvider::WindowsLocationProvider() { + LOG("WindowsLocationProvider::WindowsLocationProvider(%p)", this); + MOZ_ASSERT(XRE_IsParentProcess()); + MaybeCreateLocationActor(); +} + +WindowsLocationProvider::~WindowsLocationProvider() { + LOG("WindowsLocationProvider::~WindowsLocationProvider(%p,%p,%p)", this, + mActor.get(), mActorPromise.get()); + Send__delete__(); + ReleaseUtilityProcess(); + CancelMLSProvider(); +} + +void WindowsLocationProvider::MaybeCreateLocationActor() { + LOG("WindowsLocationProvider::MaybeCreateLocationActor(%p)", this); + if (mActor || mActorPromise) { + return; + } + + auto utilityProc = mozilla::ipc::UtilityProcessManager::GetSingleton(); + MOZ_ASSERT(utilityProc); + + // Create a PWindowsLocation actor in the Windows utility process. + // This will attempt to launch the process if it doesn't already exist. + RefPtr<WindowsLocationProvider> self = this; + auto wuPromise = utilityProc->GetWindowsUtilsPromise(); + mActorPromise = wuPromise->Then( + GetCurrentSerialEventTarget(), __func__, + [self](RefPtr<WindowsUtilsParent> wup) { + self->mActorPromise = nullptr; + auto actor = MakeRefPtr<WindowsLocationParent>(self); + if (!wup->SendPWindowsLocationConstructor(actor)) { + LOG("WindowsLocationProvider(%p) SendPWindowsLocationConstructor " + "failed", + self.get()); + actor->DetachFromLocationProvider(); + self->mActor = nullptr; + return WindowsLocationPromise::CreateAndReject(false, __func__); + } + LOG("WindowsLocationProvider connected to actor (%p,%p,%p)", self.get(), + self->mActor.get(), self->mActorPromise.get()); + self->mActor = actor; + return WindowsLocationPromise::CreateAndResolve(self->mActor, __func__); + }, + + [self](nsresult aError) { + LOG("WindowsLocationProvider failed to connect to actor (%p,%p,%p)", + self.get(), self->mActor.get(), self->mActorPromise.get()); + self->mActorPromise = nullptr; + return WindowsLocationPromise::CreateAndReject(false, __func__); + }); + + if (mActor) { + // Utility process already existed and mActorPromise was resolved + // immediately. + mActorPromise = nullptr; + } +} + +void WindowsLocationProvider::ReleaseUtilityProcess() { + LOG("WindowsLocationProvider::ReleaseUtilityProcess(%p)", this); + auto utilityProc = mozilla::ipc::UtilityProcessManager::GetIfExists(); + if (utilityProc) { + utilityProc->ReleaseWindowsUtils(); + } +} + +template <typename Fn> +bool WindowsLocationProvider::WhenActorIsReady(Fn&& fn) { + if (mActor) { + return fn(mActor); + } + + if (mActorPromise) { + mActorPromise->Then( + GetCurrentSerialEventTarget(), __func__, + [fn](const RefPtr<WindowsLocationParent>& actor) { + Unused << fn(actor.get()); + return actor; + }, + [](bool) { return false; }); + return true; + } + + // The remote process failed to start. + return false; +} + +bool WindowsLocationProvider::SendStartup() { + LOG("WindowsLocationProvider::SendStartup(%p)", this); + MaybeCreateLocationActor(); + return WhenActorIsReady( + [](WindowsLocationParent* actor) { return actor->SendStartup(); }); +} + +bool WindowsLocationProvider::SendRegisterForReport( + nsIGeolocationUpdate* aCallback) { + LOG("WindowsLocationProvider::SendRegisterForReport(%p)", this); + RefPtr<WindowsLocationProvider> self = this; + RefPtr<nsIGeolocationUpdate> cb = aCallback; + return WhenActorIsReady([self, cb](WindowsLocationParent* actor) { + MOZ_ASSERT(!self->mCallback); + if (actor->SendRegisterForReport()) { + self->mCallback = cb; + return true; + } + return false; + }); +} + +bool WindowsLocationProvider::SendUnregisterForReport() { + LOG("WindowsLocationProvider::SendUnregisterForReport(%p)", this); + RefPtr<WindowsLocationProvider> self = this; + return WhenActorIsReady([self](WindowsLocationParent* actor) { + self->mCallback = nullptr; + if (actor->SendUnregisterForReport()) { + return true; + } + return false; + }); +} + +bool WindowsLocationProvider::SendSetHighAccuracy(bool aEnable) { + LOG("WindowsLocationProvider::SendSetHighAccuracy(%p)", this); + return WhenActorIsReady([aEnable](WindowsLocationParent* actor) { + return actor->SendSetHighAccuracy(aEnable); + }); +} + +bool WindowsLocationProvider::Send__delete__() { + LOG("WindowsLocationProvider::Send__delete__(%p)", this); + return WhenActorIsReady([self = RefPtr{this}](WindowsLocationParent*) { + if (WindowsLocationParent::Send__delete__(self->mActor)) { + if (self->mActor) { + self->mActor->DetachFromLocationProvider(); + self->mActor = nullptr; + } + return true; + } + return false; + }); +} + +void WindowsLocationProvider::RecvUpdate( + RefPtr<nsIDOMGeoPosition> aGeoPosition) { + LOG("WindowsLocationProvider::RecvUpdate(%p)", this); + if (!mCallback) { + return; + } + + mCallback->Update(aGeoPosition.get()); + + Telemetry::Accumulate(Telemetry::GEOLOCATION_WIN8_SOURCE_IS_MLS, false); +} + +void WindowsLocationProvider::RecvFailed(uint16_t err) { + LOG("WindowsLocationProvider::RecvFailed(%p)", this); + // Cannot get current location at this time. We use MLS instead. + if (mMLSProvider || !mCallback) { + return; + } + + if (NS_SUCCEEDED(CreateAndWatchMLSProvider(mCallback))) { + return; + } + + // No ILocation and no MLS, so we have failed completely. + // We keep strong references to objects that we need to guarantee + // will live past the NotifyError callback. + RefPtr<WindowsLocationProvider> self = this; + nsCOMPtr<nsIGeolocationUpdate> callback = mCallback; + callback->NotifyError(err); +} + +void WindowsLocationProvider::ActorStopped() { + // ActorDestroy has run. Make sure UtilityProcessHost no longer tries to use + // it. + ReleaseUtilityProcess(); + + if (mWatching) { + // Treat as remote geolocation error, which will cause it to fallback + // to MLS if it hasn't already. + mWatching = false; + RecvFailed(GeolocationPositionError_Binding::POSITION_UNAVAILABLE); + return; + } + + MOZ_ASSERT(!mActorPromise); + if (mActor) { + mActor->DetachFromLocationProvider(); + mActor = nullptr; + } +} + +NS_IMETHODIMP +WindowsLocationProvider::Startup() { + LOG("WindowsLocationProvider::Startup(%p, %p, %p)", this, mActor.get(), + mActorPromise.get()); + // If this fails, we will use the MLS fallback. + SendStartup(); + return NS_OK; +} + +NS_IMETHODIMP +WindowsLocationProvider::Watch(nsIGeolocationUpdate* aCallback) { + LOG("WindowsLocationProvider::Watch(%p, %p, %p, %p, %d)", this, mActor.get(), + mActorPromise.get(), aCallback, mWatching); + if (mWatching) { + return NS_OK; + } + + if (SendRegisterForReport(aCallback)) { + mWatching = true; + return NS_OK; + } + + // Couldn't send request. We will use MLS instead. + return CreateAndWatchMLSProvider(aCallback); +} + +NS_IMETHODIMP +WindowsLocationProvider::Shutdown() { + LOG("WindowsLocationProvider::Shutdown(%p, %p, %p)", this, mActor.get(), + mActorPromise.get()); + + if (mWatching) { + SendUnregisterForReport(); + mWatching = false; + } + + CancelMLSProvider(); + return NS_OK; +} + +NS_IMETHODIMP +WindowsLocationProvider::SetHighAccuracy(bool enable) { + LOG("WindowsLocationProvider::SetHighAccuracy(%p, %p, %p, %s)", this, + mActor.get(), mActorPromise.get(), enable ? "true" : "false"); + if (mMLSProvider) { + // Ignored when running MLS fallback. + return NS_OK; + } + + if (!SendSetHighAccuracy(enable)) { + return NS_ERROR_FAILURE; + } + + // Since we SendSetHighAccuracy asynchronously, we cannot say for sure + // that it will succeed. If it does fail then we will get a + // RecvFailed IPC message, which will cause a fallback to MLS. + return NS_OK; +} + +nsresult WindowsLocationProvider::CreateAndWatchMLSProvider( + nsIGeolocationUpdate* aCallback) { + LOG("WindowsLocationProvider::CreateAndWatchMLSProvider" + "(%p, %p, %p, %p, %p)", + this, mMLSProvider.get(), mActor.get(), mActorPromise.get(), aCallback); + + if (mMLSProvider) { + return NS_OK; + } + + mMLSProvider = new MLSFallback(0); + return mMLSProvider->Startup(new MLSUpdate(aCallback)); +} + +void WindowsLocationProvider::CancelMLSProvider() { + LOG("WindowsLocationProvider::CancelMLSProvider" + "(%p, %p, %p, %p, %p)", + this, mMLSProvider.get(), mActor.get(), mActorPromise.get(), + mCallback.get()); + + if (!mMLSProvider) { + return; + } + + mMLSProvider->Shutdown(); + mMLSProvider = nullptr; +} + +#undef LOG + +} // namespace mozilla::dom diff --git a/dom/system/windows/location/WindowsLocationProvider.h b/dom/system/windows/location/WindowsLocationProvider.h new file mode 100644 index 0000000000..d1e4dfa936 --- /dev/null +++ b/dom/system/windows/location/WindowsLocationProvider.h @@ -0,0 +1,78 @@ +/* -*- 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 mozilla_dom_WindowsLocationProvider_h__ +#define mozilla_dom_WindowsLocationProvider_h__ + +#include "nsCOMPtr.h" +#include "nsIGeolocationProvider.h" +#include "mozilla/MozPromise.h" + +class MLSFallback; + +namespace mozilla::dom { + +class WindowsLocationParent; + +// Uses a PWindowsLocation actor to subscribe to geolocation updates from the +// Windows utility process and falls back to MLS when it is not available or +// fails. +class WindowsLocationProvider final : public nsIGeolocationProvider { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIGEOLOCATIONPROVIDER + + WindowsLocationProvider(); + + private: + friend WindowsLocationParent; + + ~WindowsLocationProvider(); + + nsresult CreateAndWatchMLSProvider(nsIGeolocationUpdate* aCallback); + void CancelMLSProvider(); + + void MaybeCreateLocationActor(); + void ReleaseUtilityProcess(); + + // These methods either send the message on the existing actor or queue + // the messages to be sent (in order) once the actor exists. + bool SendStartup(); + bool SendRegisterForReport(nsIGeolocationUpdate* aCallback); + bool SendUnregisterForReport(); + bool SendSetHighAccuracy(bool aEnable); + bool Send__delete__(); + + void RecvUpdate(RefPtr<nsIDOMGeoPosition> aGeoPosition); + // See bug 1539864 for MOZ_CAN_RUN_SCRIPT_BOUNDARY justification. + MOZ_CAN_RUN_SCRIPT_BOUNDARY void RecvFailed(uint16_t err); + + // The utility process actor has ended its connection, either successfully + // or with an error. + void ActorStopped(); + + // Run fn once actor is ready to send messages, which may be immediately. + template <typename Fn> + bool WhenActorIsReady(Fn&& fn); + + RefPtr<MLSFallback> mMLSProvider; + + nsCOMPtr<nsIGeolocationUpdate> mCallback; + + using WindowsLocationPromise = + MozPromise<RefPtr<WindowsLocationParent>, bool, false>; + + // Before the utility process exists, we have a promise that we will get our + // location actor. mActor and mActorPromise are never both set. + RefPtr<WindowsLocationPromise> mActorPromise; + RefPtr<WindowsLocationParent> mActor; + + bool mWatching = false; +}; + +} // namespace mozilla::dom + +#endif // mozilla_dom_WindowsLocationProvider_h__ diff --git a/dom/system/windows/location/moz.build b/dom/system/windows/location/moz.build new file mode 100644 index 0000000000..8fafb99fb1 --- /dev/null +++ b/dom/system/windows/location/moz.build @@ -0,0 +1,29 @@ +# -*- 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.mozilla.dom += [ + "WindowsLocationChild.h", + "WindowsLocationParent.h", +] + +UNIFIED_SOURCES += [ + "WindowsLocationParent.cpp", + "WindowsLocationProvider.cpp", +] + +SOURCES += [ + "WindowsLocationChild.cpp", # includes locationapi.h +] + +IPDL_SOURCES += [ + "PWindowsLocation.ipdl", +] + +LOCAL_INCLUDES += ["/dom/geolocation"] + +include("/ipc/chromium/chromium-config.mozbuild") + +FINAL_LIBRARY = "xul" |