tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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