WindowsLocationChild.cpp (7969B)
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 #include "WindowsLocationChild.h" 7 8 #include <locationapi.h> 9 10 #include "WindowsLocationProvider.h" 11 #include "mozilla/dom/GeolocationPosition.h" 12 #include "mozilla/dom/GeolocationPositionErrorBinding.h" 13 #include "nsCOMPtr.h" 14 #include "nsIGeolocationProvider.h" 15 #include "prtime.h" 16 17 namespace mozilla::dom { 18 19 extern LazyLogModule gWindowsLocationProviderLog; 20 #define LOG(...) \ 21 MOZ_LOG(gWindowsLocationProviderLog, LogLevel::Debug, (__VA_ARGS__)) 22 23 class LocationEvent final : public ILocationEvents { 24 public: 25 explicit LocationEvent(WindowsLocationChild* aActor) 26 : mActor(aActor), mRefCnt(0) {} 27 28 // IUnknown interface 29 STDMETHODIMP_(ULONG) AddRef() override; 30 STDMETHODIMP_(ULONG) Release() override; 31 STDMETHODIMP QueryInterface(REFIID iid, void** ppv) override; 32 33 // ILocationEvents interface 34 STDMETHODIMP OnStatusChanged(REFIID aReportType, 35 LOCATION_REPORT_STATUS aStatus) override; 36 STDMETHODIMP OnLocationChanged(REFIID aReportType, 37 ILocationReport* aReport) override; 38 39 private: 40 // Making this a WeakPtr breaks the following cycle of strong references: 41 // WindowsLocationChild -> ILocation -> ILocationEvents (this) 42 // -> WindowsLocationChild. 43 WeakPtr<WindowsLocationChild> mActor; 44 45 ULONG mRefCnt; 46 }; 47 48 STDMETHODIMP_(ULONG) 49 LocationEvent::AddRef() { return InterlockedIncrement(&mRefCnt); } 50 51 STDMETHODIMP_(ULONG) 52 LocationEvent::Release() { 53 ULONG count = InterlockedDecrement(&mRefCnt); 54 if (!count) { 55 delete this; 56 return 0; 57 } 58 return count; 59 } 60 61 STDMETHODIMP 62 LocationEvent::QueryInterface(REFIID iid, void** ppv) { 63 if (!ppv) { 64 return E_INVALIDARG; 65 } 66 67 if (iid == IID_IUnknown) { 68 *ppv = static_cast<IUnknown*>(this); 69 } else if (iid == IID_ILocationEvents) { 70 *ppv = static_cast<ILocationEvents*>(this); 71 } else { 72 *ppv = nullptr; 73 return E_NOINTERFACE; 74 } 75 76 AddRef(); 77 return S_OK; 78 } 79 80 STDMETHODIMP 81 LocationEvent::OnStatusChanged(REFIID aReportType, 82 LOCATION_REPORT_STATUS aStatus) { 83 LOG("LocationEvent::OnStatusChanged(%p, %p, %s, %04x)", this, mActor.get(), 84 aReportType == IID_ILatLongReport ? "true" : "false", 85 static_cast<uint32_t>(aStatus)); 86 87 if (!mActor || aReportType != IID_ILatLongReport) { 88 return S_OK; 89 } 90 91 // When registering event, REPORT_INITIALIZING is fired at first. 92 // Then, when the location is found, REPORT_RUNNING is fired. 93 // We ignore those messages. 94 uint16_t err; 95 switch (aStatus) { 96 case REPORT_ACCESS_DENIED: 97 err = GeolocationPositionError_Binding::PERMISSION_DENIED; 98 break; 99 case REPORT_NOT_SUPPORTED: 100 case REPORT_ERROR: 101 err = GeolocationPositionError_Binding::POSITION_UNAVAILABLE; 102 break; 103 default: 104 return S_OK; 105 } 106 107 mActor->SendFailed(err); 108 return S_OK; 109 } 110 111 STDMETHODIMP 112 LocationEvent::OnLocationChanged(REFIID aReportType, ILocationReport* aReport) { 113 LOG("LocationEvent::OnLocationChanged(%p, %p, %s)", this, mActor.get(), 114 aReportType == IID_ILatLongReport ? "true" : "false"); 115 116 if (!mActor || aReportType != IID_ILatLongReport) { 117 return S_OK; 118 } 119 120 RefPtr<ILatLongReport> latLongReport; 121 if (FAILED(aReport->QueryInterface(IID_ILatLongReport, 122 getter_AddRefs(latLongReport)))) { 123 return E_FAIL; 124 } 125 126 DOUBLE latitude = 0.0; 127 latLongReport->GetLatitude(&latitude); 128 129 DOUBLE longitude = 0.0; 130 latLongReport->GetLongitude(&longitude); 131 132 DOUBLE alt = UnspecifiedNaN<double>(); 133 latLongReport->GetAltitude(&alt); 134 135 DOUBLE herror = 0.0; 136 latLongReport->GetErrorRadius(&herror); 137 138 DOUBLE verror = UnspecifiedNaN<double>(); 139 latLongReport->GetAltitudeError(&verror); 140 141 double heading = UnspecifiedNaN<double>(); 142 double speed = UnspecifiedNaN<double>(); 143 144 // nsGeoPositionCoords will convert NaNs to null for optional properties of 145 // the JavaScript Coordinates object. 146 RefPtr<nsGeoPosition> position = 147 new nsGeoPosition(latitude, longitude, alt, herror, verror, heading, 148 speed, PR_Now() / PR_USEC_PER_MSEC); 149 mActor->SendUpdate(position); 150 151 return S_OK; 152 } 153 154 WindowsLocationChild::WindowsLocationChild() { 155 LOG("WindowsLocationChild::WindowsLocationChild(%p)", this); 156 } 157 158 WindowsLocationChild::~WindowsLocationChild() { 159 LOG("WindowsLocationChild::~WindowsLocationChild(%p)", this); 160 } 161 162 ::mozilla::ipc::IPCResult WindowsLocationChild::RecvStartup() { 163 LOG("WindowsLocationChild::RecvStartup(%p, %p)", this, mLocation.get()); 164 if (mLocation) { 165 return IPC_OK(); 166 } 167 168 RefPtr<ILocation> location; 169 if (FAILED(::CoCreateInstance(CLSID_Location, nullptr, CLSCTX_INPROC_SERVER, 170 IID_ILocation, getter_AddRefs(location)))) { 171 LOG("WindowsLocationChild(%p) failed to create ILocation", this); 172 // We will use MLS provider 173 SendFailed(GeolocationPositionError_Binding::POSITION_UNAVAILABLE); 174 return IPC_OK(); 175 } 176 177 IID reportTypes[] = {IID_ILatLongReport}; 178 if (FAILED(location->RequestPermissions(nullptr, reportTypes, 1, FALSE))) { 179 LOG("WindowsLocationChild(%p) failed to set ILocation permissions", this); 180 // We will use MLS provider 181 SendFailed(GeolocationPositionError_Binding::POSITION_UNAVAILABLE); 182 return IPC_OK(); 183 } 184 185 mLocation = location; 186 return IPC_OK(); 187 } 188 189 ::mozilla::ipc::IPCResult WindowsLocationChild::RecvSetHighAccuracy( 190 bool aEnable) { 191 LOG("WindowsLocationChild::RecvSetHighAccuracy(%p, %p, %s)", this, 192 mLocation.get(), aEnable ? "true" : "false"); 193 194 // We sometimes call SetHighAccuracy before Startup, so we save the 195 // request and set it later, in RegisterForReport. 196 mHighAccuracy = aEnable; 197 198 return IPC_OK(); 199 } 200 201 ::mozilla::ipc::IPCResult WindowsLocationChild::RecvRegisterForReport() { 202 LOG("WindowsLocationChild::RecvRegisterForReport(%p, %p)", this, 203 mLocation.get()); 204 205 if (!mLocation) { 206 SendFailed(GeolocationPositionError_Binding::POSITION_UNAVAILABLE); 207 return IPC_OK(); 208 } 209 210 LOCATION_DESIRED_ACCURACY desiredAccuracy; 211 if (mHighAccuracy) { 212 desiredAccuracy = LOCATION_DESIRED_ACCURACY_HIGH; 213 } else { 214 desiredAccuracy = LOCATION_DESIRED_ACCURACY_DEFAULT; 215 } 216 217 if (NS_WARN_IF(FAILED(mLocation->SetDesiredAccuracy(IID_ILatLongReport, 218 desiredAccuracy)))) { 219 SendFailed(GeolocationPositionError_Binding::POSITION_UNAVAILABLE); 220 return IPC_OK(); 221 } 222 223 auto event = MakeRefPtr<LocationEvent>(this); 224 if (NS_WARN_IF( 225 FAILED(mLocation->RegisterForReport(event, IID_ILatLongReport, 0)))) { 226 SendFailed(GeolocationPositionError_Binding::POSITION_UNAVAILABLE); 227 } 228 229 LOG("WindowsLocationChild::RecvRegisterForReport successfully registered"); 230 return IPC_OK(); 231 } 232 233 ::mozilla::ipc::IPCResult WindowsLocationChild::RecvUnregisterForReport() { 234 LOG("WindowsLocationChild::RecvUnregisterForReport(%p, %p)", this, 235 mLocation.get()); 236 237 if (!mLocation) { 238 return IPC_OK(); 239 } 240 241 // This will free the LocationEvent we created in RecvRegisterForReport. 242 (void)NS_WARN_IF(FAILED(mLocation->UnregisterForReport(IID_ILatLongReport))); 243 244 // The ILocation object is not reusable. Unregistering, restarting and 245 // re-registering for reports does not work; the callback is never 246 // called in that case. For that reason, we re-create the ILocation 247 // object with a call to Startup after unregistering if we need it again. 248 mLocation = nullptr; 249 return IPC_OK(); 250 } 251 252 void WindowsLocationChild::ActorDestroy(ActorDestroyReason aWhy) { 253 LOG("WindowsLocationChild::ActorDestroy(%p, %p)", this, mLocation.get()); 254 mLocation = nullptr; 255 } 256 257 } // namespace mozilla::dom