WindowsLocationProvider.cpp (10662B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "WindowsLocationProvider.h" 8 9 #include "GeolocationPosition.h" 10 #include "MLSFallback.h" 11 #include "WindowsLocationParent.h" 12 #include "mozilla/Logging.h" 13 #include "mozilla/dom/GeolocationPositionErrorBinding.h" 14 #include "mozilla/dom/WindowsUtilsParent.h" 15 #include "mozilla/glean/DomGeolocationMetrics.h" 16 #include "mozilla/ipc/UtilityProcessManager.h" 17 #include "mozilla/ipc/UtilityProcessSandboxing.h" 18 #include "nsComponentManagerUtils.h" 19 #include "prtime.h" 20 21 namespace mozilla::dom { 22 23 LazyLogModule gWindowsLocationProviderLog("WindowsLocationProvider"); 24 #define LOG(...) \ 25 MOZ_LOG(gWindowsLocationProviderLog, LogLevel::Debug, (__VA_ARGS__)) 26 27 class MLSUpdate : public nsIGeolocationUpdate { 28 public: 29 NS_DECL_ISUPPORTS 30 NS_DECL_NSIGEOLOCATIONUPDATE 31 explicit MLSUpdate(nsIGeolocationUpdate* aCallback) : mCallback(aCallback) {} 32 33 private: 34 nsCOMPtr<nsIGeolocationUpdate> mCallback; 35 virtual ~MLSUpdate() {} 36 }; 37 38 NS_IMPL_ISUPPORTS(MLSUpdate, nsIGeolocationUpdate); 39 40 NS_IMETHODIMP 41 MLSUpdate::Update(nsIDOMGeoPosition* aPosition) { 42 if (!mCallback) { 43 return NS_ERROR_FAILURE; 44 } 45 46 nsCOMPtr<nsIDOMGeoPositionCoords> coords; 47 aPosition->GetCoords(getter_AddRefs(coords)); 48 if (!coords) { 49 return NS_ERROR_FAILURE; 50 } 51 return mCallback->Update(aPosition); 52 } 53 NS_IMETHODIMP 54 MLSUpdate::NotifyError(uint16_t aError) { 55 if (!mCallback) { 56 return NS_ERROR_FAILURE; 57 } 58 nsCOMPtr<nsIGeolocationUpdate> callback(mCallback); 59 return callback->NotifyError(aError); 60 } 61 62 NS_IMPL_ISUPPORTS(WindowsLocationProvider, nsIGeolocationProvider) 63 64 WindowsLocationProvider::WindowsLocationProvider() { 65 LOG("WindowsLocationProvider::WindowsLocationProvider(%p)", this); 66 MOZ_ASSERT(XRE_IsParentProcess()); 67 MaybeCreateLocationActor(); 68 } 69 70 WindowsLocationProvider::~WindowsLocationProvider() { 71 LOG("WindowsLocationProvider::~WindowsLocationProvider(%p,%p,%p)", this, 72 mActor.get(), mActorPromise.get()); 73 Send__delete__(); 74 ReleaseUtilityProcess(); 75 CancelMLSProvider(); 76 } 77 78 void WindowsLocationProvider::MaybeCreateLocationActor() { 79 LOG("WindowsLocationProvider::MaybeCreateLocationActor(%p)", this); 80 if (mActor || mActorPromise) { 81 return; 82 } 83 84 auto utilityProc = mozilla::ipc::UtilityProcessManager::GetSingleton(); 85 MOZ_ASSERT(utilityProc); 86 87 // Create a PWindowsLocation actor in the Windows utility process. 88 // This will attempt to launch the process if it doesn't already exist. 89 RefPtr<WindowsLocationProvider> self = this; 90 auto wuPromise = utilityProc->GetWindowsUtilsPromise(); 91 mActorPromise = wuPromise->Then( 92 GetCurrentSerialEventTarget(), __func__, 93 [self](RefPtr<WindowsUtilsParent> const& wup) { 94 self->mActorPromise = nullptr; 95 auto actor = MakeRefPtr<WindowsLocationParent>(self); 96 if (!wup->SendPWindowsLocationConstructor(actor)) { 97 LOG("WindowsLocationProvider(%p) SendPWindowsLocationConstructor " 98 "failed", 99 self.get()); 100 actor->DetachFromLocationProvider(); 101 self->mActor = nullptr; 102 return WindowsLocationPromise::CreateAndReject(false, __func__); 103 } 104 LOG("WindowsLocationProvider connected to actor (%p,%p,%p)", self.get(), 105 self->mActor.get(), self->mActorPromise.get()); 106 self->mActor = actor; 107 return WindowsLocationPromise::CreateAndResolve(self->mActor, __func__); 108 }, 109 [self](::mozilla::ipc::LaunchError&& err) { 110 LOG("WindowsLocationProvider failed to connect to actor: [%s, %lX] " 111 "(%p,%p,%p)", 112 err.FunctionName().get(), err.ErrorCode(), self.get(), 113 self->mActor.get(), self->mActorPromise.get()); 114 self->mActorPromise = nullptr; 115 return WindowsLocationPromise::CreateAndReject(false, __func__); 116 }); 117 118 if (mActor) { 119 // Utility process already existed and mActorPromise was resolved 120 // immediately. 121 mActorPromise = nullptr; 122 } 123 } 124 125 void WindowsLocationProvider::ReleaseUtilityProcess() { 126 LOG("WindowsLocationProvider::ReleaseUtilityProcess(%p)", this); 127 auto utilityProc = mozilla::ipc::UtilityProcessManager::GetIfExists(); 128 if (utilityProc) { 129 utilityProc->ReleaseWindowsUtils(); 130 } 131 } 132 133 template <typename Fn> 134 bool WindowsLocationProvider::WhenActorIsReady(Fn&& fn) { 135 if (mActor) { 136 return fn(mActor); 137 } 138 139 if (mActorPromise) { 140 mActorPromise->Then( 141 GetCurrentSerialEventTarget(), __func__, 142 [fn](const RefPtr<WindowsLocationParent>& actor) { 143 (void)fn(actor.get()); 144 return actor; 145 }, 146 [](bool) { return false; }); 147 return true; 148 } 149 150 // The remote process failed to start. 151 return false; 152 } 153 154 bool WindowsLocationProvider::SendStartup() { 155 LOG("WindowsLocationProvider::SendStartup(%p)", this); 156 MaybeCreateLocationActor(); 157 return WhenActorIsReady( 158 [](WindowsLocationParent* actor) { return actor->SendStartup(); }); 159 } 160 161 bool WindowsLocationProvider::SendRegisterForReport( 162 nsIGeolocationUpdate* aCallback) { 163 LOG("WindowsLocationProvider::SendRegisterForReport(%p)", this); 164 RefPtr<WindowsLocationProvider> self = this; 165 RefPtr<nsIGeolocationUpdate> cb = aCallback; 166 return WhenActorIsReady([self, cb](WindowsLocationParent* actor) { 167 MOZ_ASSERT(!self->mCallback); 168 if (actor->SendRegisterForReport()) { 169 self->mCallback = cb; 170 return true; 171 } 172 return false; 173 }); 174 } 175 176 bool WindowsLocationProvider::SendUnregisterForReport() { 177 LOG("WindowsLocationProvider::SendUnregisterForReport(%p)", this); 178 RefPtr<WindowsLocationProvider> self = this; 179 return WhenActorIsReady([self](WindowsLocationParent* actor) { 180 self->mCallback = nullptr; 181 if (actor->SendUnregisterForReport()) { 182 return true; 183 } 184 return false; 185 }); 186 } 187 188 bool WindowsLocationProvider::SendSetHighAccuracy(bool aEnable) { 189 LOG("WindowsLocationProvider::SendSetHighAccuracy(%p)", this); 190 return WhenActorIsReady([aEnable](WindowsLocationParent* actor) { 191 return actor->SendSetHighAccuracy(aEnable); 192 }); 193 } 194 195 bool WindowsLocationProvider::Send__delete__() { 196 LOG("WindowsLocationProvider::Send__delete__(%p)", this); 197 return WhenActorIsReady([self = RefPtr{this}](WindowsLocationParent*) { 198 if (WindowsLocationParent::Send__delete__(self->mActor)) { 199 if (self->mActor) { 200 self->mActor->DetachFromLocationProvider(); 201 self->mActor = nullptr; 202 } 203 return true; 204 } 205 return false; 206 }); 207 } 208 209 void WindowsLocationProvider::RecvUpdate( 210 RefPtr<nsIDOMGeoPosition> aGeoPosition) { 211 LOG("WindowsLocationProvider::RecvUpdate(%p)", this); 212 if (!mCallback) { 213 return; 214 } 215 216 mCallback->Update(aGeoPosition.get()); 217 218 if (!mEverUpdated) { 219 mEverUpdated = true; 220 // Saw signal without MLS fallback 221 glean::geolocation::fallback 222 .EnumGet(glean::geolocation::FallbackLabel::eNone) 223 .Add(); 224 } 225 } 226 227 void WindowsLocationProvider::RecvFailed(uint16_t err) { 228 LOG("WindowsLocationProvider::RecvFailed(%p)", this); 229 // Cannot get current location at this time. We use MLS instead. 230 if (mMLSProvider || !mCallback) { 231 return; 232 } 233 234 if (NS_SUCCEEDED(CreateAndWatchMLSProvider(mCallback))) { 235 return; 236 } 237 238 // No ILocation and no MLS, so we have failed completely. 239 // We keep strong references to objects that we need to guarantee 240 // will live past the NotifyError callback. 241 RefPtr<WindowsLocationProvider> self = this; 242 nsCOMPtr<nsIGeolocationUpdate> callback = mCallback; 243 callback->NotifyError(err); 244 } 245 246 void WindowsLocationProvider::ActorStopped() { 247 // ActorDestroy has run. Make sure UtilityProcessHost no longer tries to use 248 // it. 249 ReleaseUtilityProcess(); 250 251 if (mWatching) { 252 // Treat as remote geolocation error, which will cause it to fallback 253 // to MLS if it hasn't already. 254 mWatching = false; 255 RecvFailed(GeolocationPositionError_Binding::POSITION_UNAVAILABLE); 256 return; 257 } 258 259 MOZ_ASSERT(!mActorPromise); 260 if (mActor) { 261 mActor->DetachFromLocationProvider(); 262 mActor = nullptr; 263 } 264 } 265 266 NS_IMETHODIMP 267 WindowsLocationProvider::Startup() { 268 LOG("WindowsLocationProvider::Startup(%p, %p, %p)", this, mActor.get(), 269 mActorPromise.get()); 270 // If this fails, we will use the MLS fallback. 271 SendStartup(); 272 return NS_OK; 273 } 274 275 NS_IMETHODIMP 276 WindowsLocationProvider::Watch(nsIGeolocationUpdate* aCallback) { 277 LOG("WindowsLocationProvider::Watch(%p, %p, %p, %p, %d)", this, mActor.get(), 278 mActorPromise.get(), aCallback, mWatching); 279 if (mWatching) { 280 return NS_OK; 281 } 282 283 if (SendRegisterForReport(aCallback)) { 284 mWatching = true; 285 return NS_OK; 286 } 287 288 // Couldn't send request. We will use MLS instead. 289 return CreateAndWatchMLSProvider(aCallback); 290 } 291 292 NS_IMETHODIMP 293 WindowsLocationProvider::Shutdown() { 294 LOG("WindowsLocationProvider::Shutdown(%p, %p, %p)", this, mActor.get(), 295 mActorPromise.get()); 296 297 if (mWatching) { 298 SendUnregisterForReport(); 299 mWatching = false; 300 } 301 302 CancelMLSProvider(); 303 return NS_OK; 304 } 305 306 NS_IMETHODIMP 307 WindowsLocationProvider::SetHighAccuracy(bool enable) { 308 LOG("WindowsLocationProvider::SetHighAccuracy(%p, %p, %p, %s)", this, 309 mActor.get(), mActorPromise.get(), enable ? "true" : "false"); 310 if (mMLSProvider) { 311 // Ignored when running MLS fallback. 312 return NS_OK; 313 } 314 315 if (!SendSetHighAccuracy(enable)) { 316 return NS_ERROR_FAILURE; 317 } 318 319 // Since we SendSetHighAccuracy asynchronously, we cannot say for sure 320 // that it will succeed. If it does fail then we will get a 321 // RecvFailed IPC message, which will cause a fallback to MLS. 322 return NS_OK; 323 } 324 325 nsresult WindowsLocationProvider::CreateAndWatchMLSProvider( 326 nsIGeolocationUpdate* aCallback) { 327 LOG("WindowsLocationProvider::CreateAndWatchMLSProvider" 328 "(%p, %p, %p, %p, %p)", 329 this, mMLSProvider.get(), mActor.get(), mActorPromise.get(), aCallback); 330 331 if (mMLSProvider) { 332 return NS_OK; 333 } 334 335 mMLSProvider = new MLSFallback(0); 336 return mMLSProvider->Startup(new MLSUpdate(aCallback)); 337 } 338 339 void WindowsLocationProvider::CancelMLSProvider() { 340 LOG("WindowsLocationProvider::CancelMLSProvider" 341 "(%p, %p, %p, %p, %p)", 342 this, mMLSProvider.get(), mActor.get(), mActorPromise.get(), 343 mCallback.get()); 344 345 if (!mMLSProvider) { 346 return; 347 } 348 349 mMLSProvider->Shutdown(MLSFallback::ShutdownReason::ProviderShutdown); 350 mMLSProvider = nullptr; 351 } 352 353 #undef LOG 354 355 } // namespace mozilla::dom