356 lines
10 KiB
C++
356 lines
10 KiB
C++
/* -*- 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 "mozilla/glean/DomGeolocationMetrics.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/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;
|
|
}
|
|
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> const& 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](::mozilla::ipc::LaunchError&& err) {
|
|
LOG("WindowsLocationProvider failed to connect to actor: [%s, %lX] "
|
|
"(%p,%p,%p)",
|
|
err.FunctionName().get(), err.ErrorCode(), 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());
|
|
|
|
if (!mEverUpdated) {
|
|
mEverUpdated = true;
|
|
// Saw signal without MLS fallback
|
|
glean::geolocation::fallback
|
|
.EnumGet(glean::geolocation::FallbackLabel::eNone)
|
|
.Add();
|
|
}
|
|
}
|
|
|
|
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(MLSFallback::ShutdownReason::ProviderShutdown);
|
|
mMLSProvider = nullptr;
|
|
}
|
|
|
|
#undef LOG
|
|
|
|
} // namespace mozilla::dom
|