tor-browser

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

CoreLocationLocationProvider.mm (7303B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
      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 "CoreLocationLocationProvider.h"
      8 #include "GeolocationPosition.h"
      9 #include "MLSFallback.h"
     10 #include "mozilla/FloatingPoint.h"
     11 #include "mozilla/UniquePtr.h"
     12 #include "mozilla/dom/GeolocationPositionErrorBinding.h"
     13 #include "mozilla/glean/DomGeolocationMetrics.h"
     14 #include "nsCOMPtr.h"
     15 #include "nsIConsoleService.h"
     16 #include "nsServiceManagerUtils.h"
     17 #include "prtime.h"
     18 
     19 #include <CoreLocation/CLError.h>
     20 #include <CoreLocation/CLLocation.h>
     21 #include <CoreLocation/CLLocationManager.h>
     22 #include <CoreLocation/CLLocationManagerDelegate.h>
     23 
     24 #include <objc/objc-runtime.h>
     25 #include <objc/objc.h>
     26 
     27 #include "nsObjCExceptions.h"
     28 
     29 using namespace mozilla;
     30 
     31 #define kDefaultAccuracy kCLLocationAccuracyNearestTenMeters
     32 
     33 @interface LocationDelegate : NSObject <CLLocationManagerDelegate> {
     34  CoreLocationLocationProvider* mProvider;
     35 }
     36 
     37 - (id)init:(CoreLocationLocationProvider*)aProvider;
     38 - (void)locationManager:(CLLocationManager*)aManager
     39       didFailWithError:(NSError*)aError;
     40 - (void)locationManager:(CLLocationManager*)aManager
     41     didUpdateLocations:(NSArray*)locations;
     42 
     43 @end
     44 
     45 @implementation LocationDelegate
     46 - (id)init:(CoreLocationLocationProvider*)aProvider {
     47  if ((self = [super init])) {
     48    mProvider = aProvider;
     49  }
     50 
     51  return self;
     52 }
     53 
     54 - (void)locationManager:(CLLocationManager*)aManager
     55       didFailWithError:(NSError*)aError {
     56  nsCOMPtr<nsIConsoleService> console =
     57      do_GetService(NS_CONSOLESERVICE_CONTRACTID);
     58 
     59  NS_ENSURE_TRUE_VOID(console);
     60 
     61  NSString* message = [@"Failed to acquire position: "
     62      stringByAppendingString:[aError localizedDescription]];
     63 
     64  console->LogStringMessage(NS_ConvertUTF8toUTF16([message UTF8String]).get());
     65 
     66  // The CL provider does not fallback to GeoIP, so use
     67  // NetworkGeolocationProvider for this. The concept here is: on error, hand
     68  // off geolocation to MLS, which will then report back a location or error.
     69  mProvider->CreateMLSFallbackProvider();
     70 }
     71 
     72 - (void)locationManager:(CLLocationManager*)aManager
     73     didUpdateLocations:(NSArray*)aLocations {
     74  if (aLocations.count < 1) {
     75    return;
     76  }
     77 
     78  mProvider->CancelMLSFallbackProvider();
     79 
     80  CLLocation* location = [aLocations objectAtIndex:0];
     81 
     82  double altitude;
     83  double altitudeAccuracy;
     84 
     85  // A negative verticalAccuracy indicates that the altitude value is invalid.
     86  if (location.verticalAccuracy >= 0) {
     87    altitude = location.altitude;
     88    altitudeAccuracy = location.verticalAccuracy;
     89  } else {
     90    altitude = UnspecifiedNaN<double>();
     91    altitudeAccuracy = UnspecifiedNaN<double>();
     92  }
     93 
     94  double speed =
     95      location.speed >= 0 ? location.speed : UnspecifiedNaN<double>();
     96 
     97  double heading =
     98      location.course >= 0 ? location.course : UnspecifiedNaN<double>();
     99 
    100  // nsGeoPositionCoords will convert NaNs to null for optional properties of
    101  // the JavaScript Coordinates object.
    102  nsCOMPtr<nsIDOMGeoPosition> geoPosition = new nsGeoPosition(
    103      location.coordinate.latitude, location.coordinate.longitude, altitude,
    104      location.horizontalAccuracy, altitudeAccuracy, heading, speed,
    105      PR_Now() / PR_USEC_PER_MSEC);
    106 
    107  if (!mProvider->IsEverUpdated()) {
    108    // Saw signal without MLS fallback
    109    glean::geolocation::fallback
    110        .EnumGet(glean::geolocation::FallbackLabel::eNone)
    111        .Add();
    112  }
    113 
    114  mProvider->Update(geoPosition);
    115 }
    116 @end
    117 
    118 NS_IMPL_ISUPPORTS(CoreLocationLocationProvider::MLSUpdate,
    119                  nsIGeolocationUpdate);
    120 
    121 CoreLocationLocationProvider::MLSUpdate::MLSUpdate(
    122    CoreLocationLocationProvider& parentProvider)
    123    : mParentLocationProvider(parentProvider) {}
    124 
    125 NS_IMETHODIMP
    126 CoreLocationLocationProvider::MLSUpdate::Update(nsIDOMGeoPosition* position) {
    127  nsCOMPtr<nsIDOMGeoPositionCoords> coords;
    128  position->GetCoords(getter_AddRefs(coords));
    129  if (!coords) {
    130    return NS_ERROR_FAILURE;
    131  }
    132  mParentLocationProvider.Update(position);
    133  return NS_OK;
    134 }
    135 
    136 NS_IMETHODIMP
    137 CoreLocationLocationProvider::MLSUpdate::NotifyError(uint16_t error) {
    138  mParentLocationProvider.NotifyError(error);
    139  return NS_OK;
    140 }
    141 
    142 class CoreLocationObjects {
    143 public:
    144  nsresult Init(CoreLocationLocationProvider* aProvider) {
    145    mLocationManager = [[CLLocationManager alloc] init];
    146    NS_ENSURE_TRUE(mLocationManager, NS_ERROR_NOT_AVAILABLE);
    147 
    148    mLocationDelegate = [[LocationDelegate alloc] init:aProvider];
    149    NS_ENSURE_TRUE(mLocationDelegate, NS_ERROR_NOT_AVAILABLE);
    150 
    151    mLocationManager.desiredAccuracy = kDefaultAccuracy;
    152    mLocationManager.delegate = mLocationDelegate;
    153 
    154    return NS_OK;
    155  }
    156 
    157  ~CoreLocationObjects() {
    158    if (mLocationManager) {
    159      [mLocationManager release];
    160    }
    161 
    162    if (mLocationDelegate) {
    163      [mLocationDelegate release];
    164    }
    165  }
    166 
    167  LocationDelegate* mLocationDelegate;
    168  CLLocationManager* mLocationManager;
    169 };
    170 
    171 NS_IMPL_ISUPPORTS(CoreLocationLocationProvider, nsIGeolocationProvider)
    172 
    173 CoreLocationLocationProvider::CoreLocationLocationProvider()
    174    : mCLObjects(nullptr), mMLSFallbackProvider(nullptr) {}
    175 
    176 NS_IMETHODIMP
    177 CoreLocationLocationProvider::Startup() {
    178  if (!mCLObjects) {
    179    auto clObjs = MakeUnique<CoreLocationObjects>();
    180 
    181    nsresult rv = clObjs->Init(this);
    182    NS_ENSURE_SUCCESS(rv, rv);
    183 
    184    mCLObjects = clObjs.release();
    185  }
    186 
    187  // Must be stopped before starting or response (success or failure) is not
    188  // guaranteed
    189  [mCLObjects->mLocationManager stopUpdatingLocation];
    190  [mCLObjects->mLocationManager startUpdatingLocation];
    191  return NS_OK;
    192 }
    193 
    194 NS_IMETHODIMP
    195 CoreLocationLocationProvider::Watch(nsIGeolocationUpdate* aCallback) {
    196  if (mCallback) {
    197    return NS_OK;
    198  }
    199 
    200  mCallback = aCallback;
    201  return NS_OK;
    202 }
    203 
    204 NS_IMETHODIMP
    205 CoreLocationLocationProvider::Shutdown() {
    206  NS_ENSURE_STATE(mCLObjects);
    207 
    208  [mCLObjects->mLocationManager stopUpdatingLocation];
    209 
    210  delete mCLObjects;
    211  mCLObjects = nullptr;
    212 
    213  if (mMLSFallbackProvider) {
    214    mMLSFallbackProvider->Shutdown(
    215        MLSFallback::ShutdownReason::ProviderShutdown);
    216    mMLSFallbackProvider = nullptr;
    217  }
    218 
    219  return NS_OK;
    220 }
    221 
    222 NS_IMETHODIMP
    223 CoreLocationLocationProvider::SetHighAccuracy(bool aEnable) {
    224  NS_ENSURE_STATE(mCLObjects);
    225 
    226  mCLObjects->mLocationManager.desiredAccuracy =
    227      aEnable ? kCLLocationAccuracyBest : kDefaultAccuracy;
    228 
    229  return NS_OK;
    230 }
    231 
    232 void CoreLocationLocationProvider::Update(nsIDOMGeoPosition* aSomewhere) {
    233  if (aSomewhere && mCallback) {
    234    mCallback->Update(aSomewhere);
    235  }
    236  mEverUpdated = true;
    237 }
    238 void CoreLocationLocationProvider::NotifyError(uint16_t aErrorCode) {
    239  nsCOMPtr<nsIGeolocationUpdate> callback(mCallback);
    240  callback->NotifyError(aErrorCode);
    241 }
    242 void CoreLocationLocationProvider::CreateMLSFallbackProvider() {
    243  if (mMLSFallbackProvider) {
    244    return;
    245  }
    246 
    247  mMLSFallbackProvider = new MLSFallback(0);
    248  mMLSFallbackProvider->Startup(new MLSUpdate(*this));
    249 }
    250 
    251 void CoreLocationLocationProvider::CancelMLSFallbackProvider() {
    252  if (!mMLSFallbackProvider) {
    253    return;
    254  }
    255 
    256  mMLSFallbackProvider->Shutdown(
    257      MLSFallback::ShutdownReason::ProviderResponded);
    258  mMLSFallbackProvider = nullptr;
    259 }