tor-browser

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

GeoclueLocationProvider.cpp (39906B)


      1 /*
      2 * This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
      5 *
      6 * Author: Maciej S. Szmigiero <mail@maciej.szmigiero.name>
      7 */
      8 
      9 #include "GeoclueLocationProvider.h"
     10 
     11 #include <gio/gio.h>
     12 #include <glib.h>
     13 
     14 #include "MLSFallback.h"
     15 #include "mozilla/FloatingPoint.h"
     16 #include "mozilla/GRefPtr.h"
     17 #include "mozilla/GUniquePtr.h"
     18 #include "mozilla/Logging.h"
     19 #include "mozilla/ScopeExit.h"
     20 #include "mozilla/StaticPrefs_geo.h"
     21 #include "mozilla/WeakPtr.h"
     22 #include "mozilla/XREAppData.h"
     23 #include "mozilla/dom/GeolocationPosition.h"
     24 #include "mozilla/dom/GeolocationPositionErrorBinding.h"
     25 #include "mozilla/glean/DomGeolocationMetrics.h"
     26 #include "nsAppRunner.h"
     27 #include "nsAppShell.h"
     28 #include "nsCOMPtr.h"
     29 #include "nsIDOMGeoPosition.h"
     30 #include "nsINamed.h"
     31 #include "nsITimer.h"
     32 #include "nsStringFwd.h"
     33 #include "prtime.h"
     34 
     35 namespace mozilla::dom {
     36 
     37 static LazyLogModule gGCLocationLog("GeoclueLocation");
     38 
     39 #define GCL_LOG(level, ...) \
     40  MOZ_LOG(gGCLocationLog, mozilla::LogLevel::level, (__VA_ARGS__))
     41 
     42 static const char* const kGeoclueBusName = "org.freedesktop.GeoClue2";
     43 static const char* const kGCManagerPath = "/org/freedesktop/GeoClue2/Manager";
     44 static const char* const kGCManagerInterface =
     45    "org.freedesktop.GeoClue2.Manager";
     46 static const char* const kGCClientInterface = "org.freedesktop.GeoClue2.Client";
     47 static const char* const kGCLocationInterface =
     48    "org.freedesktop.GeoClue2.Location";
     49 static const char* const kDBPropertySetMethod =
     50    "org.freedesktop.DBus.Properties.Set";
     51 
     52 /*
     53 * Minimum altitude reported as valid (in meters),
     54 * https://en.wikipedia.org/wiki/List_of_places_on_land_with_elevations_below_sea_level
     55 * says that lowest land in the world is at -430 m, so let's use -500 m here.
     56 */
     57 static const double kGCMinAlt = -500;
     58 
     59 /*
     60 * Matches "enum GClueAccuracyLevel" values, see:
     61 * https://www.freedesktop.org/software/geoclue/docs/geoclue-gclue-enums.html#GClueAccuracyLevel
     62 */
     63 enum class GCAccuracyLevel {
     64  None = 0,
     65  Country = 1,
     66  City = 4,
     67  Neighborhood = 5,
     68  Street = 6,
     69  Exact = 8,
     70 };
     71 
     72 /*
     73 * Whether to reuse D-Bus proxies between uses of this provider.
     74 * Usually a good thing, can be disabled for debug purposes.
     75 */
     76 static const bool kGCReuseDBusProxy = true;
     77 
     78 class GCLocProviderPriv final : public nsIGeolocationProvider,
     79                                public SupportsWeakPtr {
     80 public:
     81  NS_DECL_ISUPPORTS
     82  NS_DECL_NSIGEOLOCATIONPROVIDER
     83 
     84  GCLocProviderPriv();
     85 
     86 private:
     87  enum class Accuracy { Unset, Low, High };
     88  // States:
     89  //   Uninit: The default / initial state, with no client proxy yet.
     90  //   Initing: Takes care of establishing the client connection (GetClient /
     91  //            ConnectClient / SetDesktopID).
     92  //   SettingAccuracy: Does SetAccuracy operation, knows it should just go idle
     93  //                    after finishing it.
     94  //   SettingAccuracyForStart: Does SetAccuracy operation, knows it then needs
     95  //                            to do a Start operation after finishing it.
     96  //   Idle: Fully initialized, but not running state (quiescent).
     97  //   Starting: Starts the client by calling the Start D-Bus method.
     98  //   Started: Normal running state.
     99  //   Stopping: Stops the client by calling the Stop D-Bus method, knows it
    100  //             should just go idle after finishing it.
    101  //   StoppingForRestart: Stops the client by calling the Stop D-Bus method as
    102  //                       a part of a Stop -> Start sequence (with possibly
    103  //                       an accuracy update between these method calls).
    104  //
    105  // Valid state transitions are:
    106  //  (any state) -> Uninit: Transition when a D-Bus call failed or
    107  //                         provided invalid data.
    108  //
    109  //   Watch() startup path:
    110  //   Uninit -> Initing: Transition after getting the very first Watch()
    111  //   request
    112  //                      or any such request while not having the client proxy.
    113  //   Initing -> SettingAccuracyForStart: Transition after getting a successful
    114  //                                       SetDesktopID response.
    115  //   SettingAccuracyForStart -> Starting: Transition after getting a
    116  //   successful
    117  //                                        SetAccuracy response.
    118  //   Idle -> Starting: Transition after getting a Watch() request while in
    119  //   fully
    120  //                     initialized, but not running state.
    121  //   SettingAccuracy -> SettingAccuracyForStart: Transition after getting a
    122  //   Watch()
    123  //                                               request in the middle of
    124  //                                               setting accuracy during idle
    125  //                                               status.
    126  //   Stopping -> StoppingForRestart: Transition after getting a Watch()
    127  //   request
    128  //                                   in the middle of doing a Stop D-Bus call
    129  //                                   for idle status.
    130  //   StoppingForRestart -> Starting: Transition after getting a successful
    131  //                                   Stop response as a part of a Stop ->
    132  //                                   Start sequence while the previously set
    133  //                                   accuracy is still correct.
    134  //   StoppingForRestart -> SettingAccuracyForStart: Transition after getting
    135  //                                                  a successful Stop response
    136  //                                                  as a part of a Stop ->
    137  //                                                  Start sequence but the set
    138  //                                                  accuracy needs updating.
    139  //   Starting -> Started: Transition after getting a successful Start
    140  //   response.
    141  //
    142  //   Shutdown() path:
    143  //   (any state) -> Uninit: Transition when not reusing the client proxy for
    144  //                          any reason.
    145  //   Started -> Stopping: Transition from normal running state when reusing
    146  //                        the client proxy.
    147  //   SettingAccuracyForStart -> SettingAccuracy: Transition when doing
    148  //                                               a shutdown in the middle of
    149  //                                               setting accuracy for a start
    150  //                                               when reusing the client
    151  //                                               proxy.
    152  //   SettingAccuracy -> Idle: Transition after getting a successful
    153  //   SetAccuracy
    154  //                            response.
    155  //   StoppingForRestart -> Stopping: Transition when doing shutdown
    156  //                                   in the middle of a Stop -> Start sequence
    157  //                                   when reusing the client proxy.
    158  //   Stopping -> Idle: Transition after getting a successful Stop response.
    159  //
    160  //   SetHighAccuracy() path:
    161  //   Started -> StoppingForRestart: Transition when accuracy needs updating
    162  //                                  on a running client.
    163  //   (the rest of the flow in StoppingForRestart state is the same as when
    164  //    being in this state in the Watch() startup path)
    165  enum class ClientState {
    166    Uninit,
    167    Initing,
    168    SettingAccuracy,
    169    SettingAccuracyForStart,
    170    Idle,
    171    Starting,
    172    Started,
    173    Stopping,
    174    StoppingForRestart
    175  };
    176 
    177  ~GCLocProviderPriv();
    178 
    179  static bool AlwaysHighAccuracy();
    180 
    181  void SetState(ClientState aNewState, const char* aNewStateStr);
    182 
    183  void Update(nsIDOMGeoPosition* aPosition);
    184  MOZ_CAN_RUN_SCRIPT void NotifyError(int aError);
    185  MOZ_CAN_RUN_SCRIPT void DBusProxyError(const GError* aGError,
    186                                         bool aResetManager = false);
    187 
    188  MOZ_CAN_RUN_SCRIPT static void GetClientResponse(GDBusProxy* aProxy,
    189                                                   GAsyncResult* aResult,
    190                                                   gpointer aUserData);
    191  void ConnectClient(const gchar* aClientPath);
    192  MOZ_CAN_RUN_SCRIPT static void ConnectClientResponse(GObject* aObject,
    193                                                       GAsyncResult* aResult,
    194                                                       gpointer aUserData);
    195  void SetDesktopID();
    196  MOZ_CAN_RUN_SCRIPT static void SetDesktopIDResponse(GDBusProxy* aProxy,
    197                                                      GAsyncResult* aResult,
    198                                                      gpointer aUserData);
    199  void SetAccuracy();
    200  MOZ_CAN_RUN_SCRIPT static void SetAccuracyResponse(GDBusProxy* aProxy,
    201                                                     GAsyncResult* aResult,
    202                                                     gpointer aUserData);
    203  void StartClient();
    204  MOZ_CAN_RUN_SCRIPT static void StartClientResponse(GDBusProxy* aProxy,
    205                                                     GAsyncResult* aResult,
    206                                                     gpointer aUserData);
    207  void StopClient(bool aForRestart);
    208  MOZ_CAN_RUN_SCRIPT static void StopClientResponse(GDBusProxy* aProxy,
    209                                                    GAsyncResult* aResult,
    210                                                    gpointer aUserData);
    211  void StopClientNoWait();
    212  void MaybeRestartForAccuracy();
    213 
    214  MOZ_CAN_RUN_SCRIPT static void GCManagerOwnerNotify(GObject* aObject,
    215                                                      GParamSpec* aPSpec,
    216                                                      gpointer aUserData);
    217  static void GCClientSignal(GDBusProxy* aProxy, gchar* aSenderName,
    218                             gchar* aSignalName, GVariant* aParameters,
    219                             gpointer aUserData);
    220  void ConnectLocation(const gchar* aLocationPath);
    221  static bool GetLocationProperty(GDBusProxy* aProxyLocation,
    222                                  const gchar* aName, double* aOut);
    223  static void ConnectLocationResponse(GObject* aObject, GAsyncResult* aResult,
    224                                      gpointer aUserData);
    225 
    226  void StartLastPositionTimer();
    227  void StopPositionTimer();
    228  void UpdateLastPosition();
    229 
    230  void StartMLSFallbackTimerIfNeeded();
    231  void StopMLSFallbackTimer();
    232  void MLSFallbackTimerFired();
    233 
    234  bool InDBusCall();
    235  bool InDBusStoppingCall();
    236  bool InDBusStoppedCall();
    237 
    238  void DeleteManager();
    239  void DoShutdown(bool aDeleteClient, bool aDeleteManager);
    240  void DoShutdownClearCallback(bool aDestroying);
    241 
    242  nsresult FallbackToMLS(MLSFallback::FallbackReason aReason);
    243  void StopMLSFallback();
    244 
    245  void WatchStart();
    246 
    247  Accuracy mAccuracyWanted = Accuracy::Unset;
    248  Accuracy mAccuracySet = Accuracy::Unset;
    249  RefPtr<GDBusProxy> mProxyManager;
    250  RefPtr<GDBusProxy> mProxyClient;
    251  RefPtr<GCancellable> mCancellable;
    252  nsCOMPtr<nsIGeolocationUpdate> mCallback;
    253  ClientState mClientState = ClientState::Uninit;
    254  RefPtr<nsIDOMGeoPosition> mLastPosition;
    255  RefPtr<nsITimer> mLastPositionTimer;
    256  RefPtr<nsITimer> mMLSFallbackTimer;
    257  RefPtr<MLSFallback> mMLSFallback;
    258 };
    259 
    260 class GCLocWeakCallback final : public nsITimerCallback, public nsINamed {
    261  using Method = void (GCLocProviderPriv::*)();
    262 
    263 public:
    264  NS_DECL_ISUPPORTS
    265  NS_DECL_NSITIMERCALLBACK
    266 
    267  explicit GCLocWeakCallback(GCLocProviderPriv* aParent, const char* aName,
    268                             Method aMethod)
    269      : mParent(aParent), mName(aName), mMethod(aMethod) {}
    270 
    271  NS_IMETHOD GetName(nsACString& aName) override {
    272    aName = mName;
    273    return NS_OK;
    274  }
    275 
    276 private:
    277  ~GCLocWeakCallback() = default;
    278  WeakPtr<GCLocProviderPriv> mParent;
    279  const char* mName = nullptr;
    280  Method mMethod = nullptr;
    281 };
    282 
    283 NS_IMPL_ISUPPORTS(GCLocWeakCallback, nsITimerCallback, nsINamed)
    284 
    285 NS_IMETHODIMP
    286 GCLocWeakCallback::Notify(nsITimer* aTimer) {
    287  if (RefPtr parent = mParent.get()) {
    288    (parent->*mMethod)();
    289  }
    290  return NS_OK;
    291 }
    292 
    293 //
    294 // GCLocProviderPriv
    295 //
    296 
    297 #define GCLP_SETSTATE(this, state) this->SetState(ClientState::state, #state)
    298 
    299 GCLocProviderPriv::GCLocProviderPriv() {
    300  if (AlwaysHighAccuracy()) {
    301    mAccuracyWanted = Accuracy::High;
    302  } else {
    303    mAccuracyWanted = Accuracy::Low;
    304  }
    305 }
    306 
    307 GCLocProviderPriv::~GCLocProviderPriv() { DoShutdownClearCallback(true); }
    308 
    309 bool GCLocProviderPriv::AlwaysHighAccuracy() {
    310  return StaticPrefs::geo_provider_geoclue_always_high_accuracy();
    311 }
    312 
    313 void GCLocProviderPriv::SetState(ClientState aNewState,
    314                                 const char* aNewStateStr) {
    315  if (mClientState == aNewState) {
    316    return;
    317  }
    318 
    319  GCL_LOG(Debug, "changing state to %s", aNewStateStr);
    320  mClientState = aNewState;
    321 }
    322 
    323 void GCLocProviderPriv::Update(nsIDOMGeoPosition* aPosition) {
    324  if (!mCallback) {
    325    return;
    326  }
    327 
    328  mCallback->Update(aPosition);
    329 }
    330 
    331 void GCLocProviderPriv::UpdateLastPosition() {
    332  MOZ_DIAGNOSTIC_ASSERT(mLastPosition, "No last position to update");
    333  if (mMLSFallbackTimer) {
    334    // We are not going to wait for MLS fallback anymore
    335    glean::geolocation::fallback
    336        .EnumGet(glean::geolocation::FallbackLabel::eNone)
    337        .Add();
    338  }
    339  StopPositionTimer();
    340  StopMLSFallbackTimer();
    341  Update(mLastPosition);
    342 }
    343 
    344 nsresult GCLocProviderPriv::FallbackToMLS(MLSFallback::FallbackReason aReason) {
    345  GCL_LOG(Debug, "trying to fall back to MLS");
    346  StopMLSFallback();
    347 
    348  RefPtr fallback = new MLSFallback(0);
    349  MOZ_TRY(fallback->Startup(mCallback, aReason));
    350 
    351  GCL_LOG(Debug, "Started up MLS fallback");
    352  mMLSFallback = std::move(fallback);
    353  return NS_OK;
    354 }
    355 
    356 void GCLocProviderPriv::StopMLSFallback() {
    357  if (!mMLSFallback) {
    358    return;
    359  }
    360  GCL_LOG(Debug, "Clearing MLS fallback");
    361  if (mMLSFallback) {
    362    mMLSFallback->Shutdown(MLSFallback::ShutdownReason::ProviderShutdown);
    363    mMLSFallback = nullptr;
    364  }
    365 }
    366 
    367 void GCLocProviderPriv::NotifyError(int aError) {
    368  if (!mCallback) {
    369    return;
    370  }
    371 
    372  // We errored out, try to fall back to MLS.
    373  if (NS_SUCCEEDED(FallbackToMLS(MLSFallback::FallbackReason::Error))) {
    374    return;
    375  }
    376 
    377  nsCOMPtr callback = mCallback;
    378  callback->NotifyError(aError);
    379 }
    380 
    381 void GCLocProviderPriv::DBusProxyError(const GError* aGError,
    382                                       bool aResetManager) {
    383  // that G_DBUS_ERROR below is actually a function call, not a constant
    384  GQuark gdbusDomain = G_DBUS_ERROR;
    385  int error = GeolocationPositionError_Binding::POSITION_UNAVAILABLE;
    386  if (aGError) {
    387    if (g_error_matches(aGError, gdbusDomain, G_DBUS_ERROR_TIMEOUT) ||
    388        g_error_matches(aGError, gdbusDomain, G_DBUS_ERROR_TIMED_OUT)) {
    389      error = GeolocationPositionError_Binding::TIMEOUT;
    390    } else if (g_error_matches(aGError, gdbusDomain,
    391                               G_DBUS_ERROR_LIMITS_EXCEEDED) ||
    392               g_error_matches(aGError, gdbusDomain,
    393                               G_DBUS_ERROR_ACCESS_DENIED) ||
    394               g_error_matches(aGError, gdbusDomain,
    395                               G_DBUS_ERROR_AUTH_FAILED)) {
    396      error = GeolocationPositionError_Binding::PERMISSION_DENIED;
    397    }
    398  }
    399 
    400  DoShutdown(true, aResetManager);
    401  NotifyError(error);
    402 }
    403 
    404 void GCLocProviderPriv::GetClientResponse(GDBusProxy* aProxy,
    405                                          GAsyncResult* aResult,
    406                                          gpointer aUserData) {
    407  GUniquePtr<GError> error;
    408  RefPtr<GVariant> variant = dont_AddRef(
    409      g_dbus_proxy_call_finish(aProxy, aResult, getter_Transfers(error)));
    410  if (!variant) {
    411    GCL_LOG(Error, "Failed to get client: %s\n", error->message);
    412    // if cancelled |self| might no longer be there
    413    if (!g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
    414      RefPtr self = static_cast<GCLocProviderPriv*>(aUserData);
    415      self->DBusProxyError(error.get(), true);
    416    }
    417    return;
    418  }
    419 
    420  RefPtr self = static_cast<GCLocProviderPriv*>(aUserData);
    421  MOZ_DIAGNOSTIC_ASSERT(self->mClientState == ClientState::Initing,
    422                        "Client in a wrong state");
    423 
    424  auto signalError = MakeScopeExit([&]() MOZ_CAN_RUN_SCRIPT_BOUNDARY {
    425    self->DBusProxyError(nullptr, true);
    426  });
    427 
    428  if (!g_variant_is_of_type(variant, G_VARIANT_TYPE_TUPLE)) {
    429    GCL_LOG(Error, "Unexpected get client call return type: %s\n",
    430            g_variant_get_type_string(variant));
    431    return;
    432  }
    433 
    434  if (g_variant_n_children(variant) < 1) {
    435    GCL_LOG(Error,
    436            "Not enough params in get client call return: %" G_GSIZE_FORMAT
    437            "\n",
    438            g_variant_n_children(variant));
    439    return;
    440  }
    441 
    442  variant = dont_AddRef(g_variant_get_child_value(variant, 0));
    443  if (!g_variant_is_of_type(variant, G_VARIANT_TYPE_OBJECT_PATH)) {
    444    GCL_LOG(Error, "Unexpected get client call return type inside tuple: %s\n",
    445            g_variant_get_type_string(variant));
    446    return;
    447  }
    448 
    449  const gchar* clientPath = g_variant_get_string(variant, nullptr);
    450  GCL_LOG(Debug, "Client path: %s\n", clientPath);
    451 
    452  signalError.release();
    453  self->ConnectClient(clientPath);
    454 }
    455 
    456 void GCLocProviderPriv::ConnectClient(const gchar* aClientPath) {
    457  MOZ_DIAGNOSTIC_ASSERT(mClientState == ClientState::Initing,
    458                        "Client in a wrong state");
    459  MOZ_ASSERT(mCancellable, "Watch() wasn't successfully called");
    460  nsAppShell::DBusConnectionCheck();
    461  g_dbus_proxy_new_for_bus(
    462      G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, nullptr, kGeoclueBusName,
    463      aClientPath, kGCClientInterface, mCancellable,
    464      reinterpret_cast<GAsyncReadyCallback>(ConnectClientResponse), this);
    465 }
    466 
    467 void GCLocProviderPriv::ConnectClientResponse(GObject* aObject,
    468                                              GAsyncResult* aResult,
    469                                              gpointer aUserData) {
    470  nsAppShell::DBusConnectionCheck();
    471  GUniquePtr<GError> error;
    472  RefPtr<GDBusProxy> proxyClient =
    473      dont_AddRef(g_dbus_proxy_new_finish(aResult, getter_Transfers(error)));
    474  if (!proxyClient) {
    475    // if cancelled |self| might no longer be there
    476    if (!g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
    477      GCL_LOG(Error, "Failed to connect to client: %s\n", error->message);
    478      RefPtr self = static_cast<GCLocProviderPriv*>(aUserData);
    479      self->DBusProxyError(error.get());
    480    }
    481    return;
    482  }
    483  RefPtr self = static_cast<GCLocProviderPriv*>(aUserData);
    484  self->mProxyClient = std::move(proxyClient);
    485 
    486  MOZ_DIAGNOSTIC_ASSERT(self->mClientState == ClientState::Initing,
    487                        "Client in a wrong state");
    488 
    489  GCL_LOG(Info, "Client interface connected\n");
    490 
    491  g_signal_connect(self->mProxyClient, "g-signal", G_CALLBACK(GCClientSignal),
    492                   self);
    493  self->SetDesktopID();
    494 }
    495 
    496 void GCLocProviderPriv::SetDesktopID() {
    497  MOZ_DIAGNOSTIC_ASSERT(mClientState == ClientState::Initing,
    498                        "Client in a wrong state");
    499  MOZ_DIAGNOSTIC_ASSERT(mProxyClient && mCancellable,
    500                        "Watch() wasn't successfully called");
    501 
    502  nsAutoCString appName;
    503  gAppData->GetDBusAppName(appName);
    504  nsAppShell::DBusConnectionCheck();
    505  g_dbus_proxy_call(mProxyClient, kDBPropertySetMethod,
    506                    g_variant_new("(ssv)", kGCClientInterface, "DesktopId",
    507                                  g_variant_new_string(appName.get())),
    508                    G_DBUS_CALL_FLAGS_NONE, -1, mCancellable,
    509                    reinterpret_cast<GAsyncReadyCallback>(SetDesktopIDResponse),
    510                    this);
    511 }
    512 
    513 void GCLocProviderPriv::SetDesktopIDResponse(GDBusProxy* aProxy,
    514                                             GAsyncResult* aResult,
    515                                             gpointer aUserData) {
    516  GUniquePtr<GError> error;
    517  nsAppShell::DBusConnectionCheck();
    518  RefPtr<GVariant> variant = dont_AddRef(
    519      g_dbus_proxy_call_finish(aProxy, aResult, getter_Transfers(error)));
    520  if (!variant) {
    521    // if cancelled |self| might no longer be there
    522    if (!g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
    523      GCL_LOG(Error, "Failed to set DesktopId: %s\n", error->message);
    524      RefPtr self = static_cast<GCLocProviderPriv*>(aUserData);
    525      self->DBusProxyError(error.get());
    526    }
    527    return;
    528  }
    529 
    530  RefPtr self = static_cast<GCLocProviderPriv*>(aUserData);
    531  MOZ_DIAGNOSTIC_ASSERT(self->mClientState == ClientState::Initing,
    532                        "Client in a wrong state");
    533 
    534  GCLP_SETSTATE(self, Idle);
    535  self->SetAccuracy();
    536 }
    537 
    538 void GCLocProviderPriv::SetAccuracy() {
    539  MOZ_DIAGNOSTIC_ASSERT(mClientState == ClientState::Idle,
    540                        "Client in a wrong state");
    541  MOZ_DIAGNOSTIC_ASSERT(mProxyClient && mCancellable,
    542                        "Watch() wasn't successfully called");
    543  MOZ_ASSERT(mAccuracyWanted != Accuracy::Unset, "Invalid accuracy");
    544 
    545  guint32 accuracy;
    546  if (mAccuracyWanted == Accuracy::High) {
    547    accuracy = (guint32)GCAccuracyLevel::Exact;
    548  } else {
    549    accuracy = (guint32)GCAccuracyLevel::City;
    550  }
    551 
    552  mAccuracySet = mAccuracyWanted;
    553  GCLP_SETSTATE(this, SettingAccuracyForStart);
    554  nsAppShell::DBusConnectionCheck();
    555  g_dbus_proxy_call(
    556      mProxyClient, kDBPropertySetMethod,
    557      g_variant_new("(ssv)", kGCClientInterface, "RequestedAccuracyLevel",
    558                    g_variant_new_uint32(accuracy)),
    559      G_DBUS_CALL_FLAGS_NONE, -1, mCancellable,
    560      reinterpret_cast<GAsyncReadyCallback>(SetAccuracyResponse), this);
    561 }
    562 
    563 void GCLocProviderPriv::SetAccuracyResponse(GDBusProxy* aProxy,
    564                                            GAsyncResult* aResult,
    565                                            gpointer aUserData) {
    566  nsAppShell::DBusConnectionCheck();
    567  GUniquePtr<GError> error;
    568  RefPtr<GVariant> variant = dont_AddRef(
    569      g_dbus_proxy_call_finish(aProxy, aResult, getter_Transfers(error)));
    570  if (!variant) {
    571    // if cancelled |self| might no longer be there
    572    if (!g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
    573      GCL_LOG(Error, "Failed to set requested accuracy level: %s\n",
    574              error->message);
    575      RefPtr self = static_cast<GCLocProviderPriv*>(aUserData);
    576      self->DBusProxyError(error.get());
    577    }
    578    return;
    579  }
    580 
    581  RefPtr self = static_cast<GCLocProviderPriv*>(aUserData);
    582  MOZ_DIAGNOSTIC_ASSERT(
    583      self->mClientState == ClientState::SettingAccuracyForStart ||
    584          self->mClientState == ClientState::SettingAccuracy,
    585      "Client in a wrong state");
    586  bool wantStart = self->mClientState == ClientState::SettingAccuracyForStart;
    587  GCLP_SETSTATE(self, Idle);
    588 
    589  if (wantStart) {
    590    self->StartClient();
    591  }
    592 }
    593 
    594 void GCLocProviderPriv::StartClient() {
    595  MOZ_DIAGNOSTIC_ASSERT(mClientState == ClientState::Idle,
    596                        "Client in a wrong state");
    597  MOZ_DIAGNOSTIC_ASSERT(mProxyClient && mCancellable,
    598                        "Watch() wasn't successfully called");
    599  GCLP_SETSTATE(this, Starting);
    600  nsAppShell::DBusConnectionCheck();
    601  g_dbus_proxy_call(
    602      mProxyClient, "Start", nullptr, G_DBUS_CALL_FLAGS_NONE, -1, mCancellable,
    603      reinterpret_cast<GAsyncReadyCallback>(StartClientResponse), this);
    604 }
    605 
    606 void GCLocProviderPriv::StartClientResponse(GDBusProxy* aProxy,
    607                                            GAsyncResult* aResult,
    608                                            gpointer aUserData) {
    609  GUniquePtr<GError> error;
    610  nsAppShell::DBusConnectionCheck();
    611  RefPtr<GVariant> variant = dont_AddRef(
    612      g_dbus_proxy_call_finish(aProxy, aResult, getter_Transfers(error)));
    613  if (!variant) {
    614    // if cancelled |self| might no longer be there
    615    if (!g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
    616      GCL_LOG(Error, "Failed to start client: %s\n", error->message);
    617      RefPtr self = static_cast<GCLocProviderPriv*>(aUserData);
    618      /*
    619       * A workaround for
    620       * https://gitlab.freedesktop.org/geoclue/geoclue/-/issues/143 We need to
    621       * get a new client instance once the agent finally connects to the
    622       * Geoclue service, otherwise every Start request on the old client
    623       * interface will be denied. We need to reconnect to the Manager interface
    624       * to achieve this since otherwise GetClient call will simply return the
    625       * old client instance.
    626       */
    627      bool resetManager = g_error_matches(error.get(), G_DBUS_ERROR,
    628                                          G_DBUS_ERROR_ACCESS_DENIED);
    629      self->DBusProxyError(error.get(), resetManager);
    630    }
    631    return;
    632  }
    633 
    634  RefPtr self = static_cast<GCLocProviderPriv*>(aUserData);
    635  MOZ_DIAGNOSTIC_ASSERT(self->mClientState == ClientState::Starting,
    636                        "Client in a wrong state");
    637  GCLP_SETSTATE(self, Started);
    638  // If we're started, and we don't get any location update in a reasonable
    639  // amount of time, we fallback to MLS.
    640  self->StartMLSFallbackTimerIfNeeded();
    641  self->MaybeRestartForAccuracy();
    642 }
    643 
    644 void GCLocProviderPriv::StopClient(bool aForRestart) {
    645  MOZ_DIAGNOSTIC_ASSERT(mClientState == ClientState::Started,
    646                        "Client in a wrong state");
    647  MOZ_DIAGNOSTIC_ASSERT(mProxyClient && mCancellable,
    648                        "Watch() wasn't successfully called");
    649 
    650  if (aForRestart) {
    651    GCLP_SETSTATE(this, StoppingForRestart);
    652  } else {
    653    GCLP_SETSTATE(this, Stopping);
    654  }
    655 
    656  nsAppShell::DBusConnectionCheck();
    657  g_dbus_proxy_call(
    658      mProxyClient, "Stop", nullptr, G_DBUS_CALL_FLAGS_NONE, -1, mCancellable,
    659      reinterpret_cast<GAsyncReadyCallback>(StopClientResponse), this);
    660 }
    661 
    662 void GCLocProviderPriv::StopClientResponse(GDBusProxy* aProxy,
    663                                           GAsyncResult* aResult,
    664                                           gpointer aUserData) {
    665  nsAppShell::DBusConnectionCheck();
    666  GUniquePtr<GError> error;
    667  RefPtr<GVariant> variant = dont_AddRef(
    668      g_dbus_proxy_call_finish(aProxy, aResult, getter_Transfers(error)));
    669  if (!variant) {
    670    // if cancelled |self| might no longer be there
    671    if (!g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
    672      GCL_LOG(Error, "Failed to stop client: %s\n", error->message);
    673      RefPtr self = static_cast<GCLocProviderPriv*>(aUserData);
    674      self->DBusProxyError(error.get());
    675    }
    676    return;
    677  }
    678 
    679  RefPtr self = static_cast<GCLocProviderPriv*>(aUserData);
    680  MOZ_DIAGNOSTIC_ASSERT(self->InDBusStoppingCall(), "Client in a wrong state");
    681  bool wantRestart = self->mClientState == ClientState::StoppingForRestart;
    682  GCLP_SETSTATE(self, Idle);
    683 
    684  if (!wantRestart) {
    685    return;
    686  }
    687 
    688  if (self->mAccuracyWanted != self->mAccuracySet) {
    689    self->SetAccuracy();
    690  } else {
    691    self->StartClient();
    692  }
    693 }
    694 
    695 void GCLocProviderPriv::StopClientNoWait() {
    696  MOZ_DIAGNOSTIC_ASSERT(mProxyClient, "Watch() wasn't successfully called");
    697  nsAppShell::DBusConnectionCheck();
    698  g_dbus_proxy_call(mProxyClient, "Stop", nullptr, G_DBUS_CALL_FLAGS_NONE, -1,
    699                    nullptr, nullptr, nullptr);
    700 }
    701 
    702 void GCLocProviderPriv::MaybeRestartForAccuracy() {
    703  if (mAccuracyWanted == mAccuracySet) {
    704    return;
    705  }
    706 
    707  if (mClientState != ClientState::Started) {
    708    return;
    709  }
    710 
    711  // Setting a new accuracy requires restarting the client
    712  StopClient(true);
    713 }
    714 
    715 void GCLocProviderPriv::GCManagerOwnerNotify(GObject* aObject,
    716                                             GParamSpec* aPSpec,
    717                                             gpointer aUserData) {
    718  RefPtr self = static_cast<GCLocProviderPriv*>(aUserData);
    719  GUniquePtr<gchar> managerOwner(
    720      g_dbus_proxy_get_name_owner(self->mProxyManager));
    721  if (!managerOwner) {
    722    GCL_LOG(Info, "The Manager interface has lost its owner\n");
    723    self->DBusProxyError(nullptr, true);
    724  }
    725 }
    726 
    727 void GCLocProviderPriv::GCClientSignal(GDBusProxy* aProxy, gchar* aSenderName,
    728                                       gchar* aSignalName,
    729                                       GVariant* aParameters,
    730                                       gpointer aUserData) {
    731  GCL_LOG(Info, "%s: %s (%s)\n", __PRETTY_FUNCTION__, aSignalName,
    732          GUniquePtr<gchar>(g_variant_print(aParameters, TRUE)).get());
    733 
    734  if (g_strcmp0(aSignalName, "LocationUpdated")) {
    735    return;
    736  }
    737 
    738  if (!g_variant_is_of_type(aParameters, G_VARIANT_TYPE_TUPLE)) {
    739    GCL_LOG(Error, "Unexpected location updated signal params type: %s\n",
    740            g_variant_get_type_string(aParameters));
    741    return;
    742  }
    743 
    744  if (g_variant_n_children(aParameters) < 2) {
    745    GCL_LOG(Error,
    746            "Not enough params in location updated signal: %" G_GSIZE_FORMAT
    747            "\n",
    748            g_variant_n_children(aParameters));
    749    return;
    750  }
    751 
    752  RefPtr<GVariant> variant =
    753      dont_AddRef(g_variant_get_child_value(aParameters, 1));
    754  if (!g_variant_is_of_type(variant, G_VARIANT_TYPE_OBJECT_PATH)) {
    755    GCL_LOG(Error,
    756            "Unexpected location updated signal new location path type: %s\n",
    757            g_variant_get_type_string(variant));
    758    return;
    759  }
    760 
    761  RefPtr self = static_cast<GCLocProviderPriv*>(aUserData);
    762  const gchar* locationPath = g_variant_get_string(variant, nullptr);
    763  GCL_LOG(Verbose, "New location path: %s\n", locationPath);
    764  self->ConnectLocation(locationPath);
    765 }
    766 
    767 void GCLocProviderPriv::ConnectLocation(const gchar* aLocationPath) {
    768  MOZ_ASSERT(mCancellable, "Startup() wasn't successfully called");
    769  nsAppShell::DBusConnectionCheck();
    770  g_dbus_proxy_new_for_bus(
    771      G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, nullptr, kGeoclueBusName,
    772      aLocationPath, kGCLocationInterface, mCancellable,
    773      reinterpret_cast<GAsyncReadyCallback>(ConnectLocationResponse), this);
    774 }
    775 
    776 bool GCLocProviderPriv::GetLocationProperty(GDBusProxy* aProxyLocation,
    777                                            const gchar* aName, double* aOut) {
    778  RefPtr<GVariant> property =
    779      dont_AddRef(g_dbus_proxy_get_cached_property(aProxyLocation, aName));
    780  if (!g_variant_is_of_type(property, G_VARIANT_TYPE_DOUBLE)) {
    781    GCL_LOG(Error, "Unexpected location property %s type: %s\n", aName,
    782            g_variant_get_type_string(property));
    783    return false;
    784  }
    785 
    786  *aOut = g_variant_get_double(property);
    787  return true;
    788 }
    789 
    790 void GCLocProviderPriv::ConnectLocationResponse(GObject* aObject,
    791                                                GAsyncResult* aResult,
    792                                                gpointer aUserData) {
    793  GUniquePtr<GError> error;
    794  RefPtr<GDBusProxy> proxyLocation =
    795      dont_AddRef(g_dbus_proxy_new_finish(aResult, getter_Transfers(error)));
    796  if (!proxyLocation) {
    797    if (!g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
    798      GCL_LOG(Warning, "Failed to connect to location: %s\n", error->message);
    799    }
    800    return;
    801  }
    802 
    803  RefPtr self = static_cast<GCLocProviderPriv*>(aUserData);
    804  /*
    805   * nsGeoPositionCoords will convert NaNs to null for optional properties of
    806   * the JavaScript Coordinates object.
    807   */
    808  double lat = UnspecifiedNaN<double>();
    809  double lon = UnspecifiedNaN<double>();
    810  double alt = UnspecifiedNaN<double>();
    811  double hError = UnspecifiedNaN<double>();
    812  const double vError = UnspecifiedNaN<double>();
    813  double heading = UnspecifiedNaN<double>();
    814  double speed = UnspecifiedNaN<double>();
    815  struct {
    816    const gchar* name;
    817    double* out;
    818  } props[] = {
    819      {"Latitude", &lat},    {"Longitude", &lon},   {"Altitude", &alt},
    820      {"Accuracy", &hError}, {"Heading", &heading}, {"Speed", &speed},
    821  };
    822 
    823  for (auto& prop : props) {
    824    if (!GetLocationProperty(proxyLocation, prop.name, prop.out)) {
    825      return;
    826    }
    827  }
    828 
    829  if (alt < kGCMinAlt) {
    830    alt = UnspecifiedNaN<double>();
    831  }
    832  if (speed < 0) {
    833    speed = UnspecifiedNaN<double>();
    834  }
    835  if (heading < 0 || std::isnan(speed) || speed == 0) {
    836    heading = UnspecifiedNaN<double>();
    837  }
    838 
    839  GCL_LOG(Info, "New location: %f %f +-%fm @ %gm; hdg %f spd %fm/s\n", lat, lon,
    840          hError, alt, heading, speed);
    841 
    842  self->mLastPosition =
    843      new nsGeoPosition(lat, lon, alt, hError, vError, heading, speed,
    844                        PR_Now() / PR_USEC_PER_MSEC);
    845  self->UpdateLastPosition();
    846 }
    847 
    848 void GCLocProviderPriv::StartLastPositionTimer() {
    849  MOZ_DIAGNOSTIC_ASSERT(mLastPosition, "no last position to report");
    850 
    851  StopPositionTimer();
    852 
    853  RefPtr timerCallback = new GCLocWeakCallback(
    854      this, "UpdateLastPosition", &GCLocProviderPriv::UpdateLastPosition);
    855  NS_NewTimerWithCallback(getter_AddRefs(mLastPositionTimer), timerCallback,
    856                          1000, nsITimer::TYPE_ONE_SHOT);
    857 }
    858 
    859 void GCLocProviderPriv::StopPositionTimer() {
    860  if (!mLastPositionTimer) {
    861    return;
    862  }
    863 
    864  mLastPositionTimer->Cancel();
    865  mLastPositionTimer = nullptr;
    866 }
    867 
    868 void GCLocProviderPriv::StartMLSFallbackTimerIfNeeded() {
    869  StopMLSFallbackTimer();
    870  if (mLastPosition) {
    871    // If we already have a location we're good.
    872    return;
    873  }
    874 
    875  uint32_t delay = StaticPrefs::geo_provider_geoclue_mls_fallback_timeout_ms();
    876  if (!delay) {
    877    return;
    878  }
    879 
    880  RefPtr timerCallback = new GCLocWeakCallback(
    881      this, "MLSFallbackTimerFired", &GCLocProviderPriv::MLSFallbackTimerFired);
    882  NS_NewTimerWithCallback(getter_AddRefs(mMLSFallbackTimer), timerCallback,
    883                          delay, nsITimer::TYPE_ONE_SHOT);
    884 }
    885 
    886 void GCLocProviderPriv::StopMLSFallbackTimer() {
    887  if (!mMLSFallbackTimer) {
    888    return;
    889  }
    890  mMLSFallbackTimer->Cancel();
    891  mMLSFallbackTimer = nullptr;
    892 }
    893 
    894 void GCLocProviderPriv::MLSFallbackTimerFired() {
    895  mMLSFallbackTimer = nullptr;
    896 
    897  if (mMLSFallback || mLastPosition || mClientState != ClientState::Started) {
    898    return;
    899  }
    900 
    901  GCL_LOG(Info,
    902          "Didn't get a location in a reasonable amount of time, trying to "
    903          "fall back to MLS");
    904  FallbackToMLS(MLSFallback::FallbackReason::Timeout);
    905 }
    906 
    907 // Did we made some D-Bus call and are still waiting for its response?
    908 bool GCLocProviderPriv::InDBusCall() {
    909  return mClientState == ClientState::Initing ||
    910         mClientState == ClientState::SettingAccuracy ||
    911         mClientState == ClientState::SettingAccuracyForStart ||
    912         mClientState == ClientState::Starting ||
    913         mClientState == ClientState::Stopping ||
    914         mClientState == ClientState::StoppingForRestart;
    915 }
    916 
    917 bool GCLocProviderPriv::InDBusStoppingCall() {
    918  return mClientState == ClientState::Stopping ||
    919         mClientState == ClientState::StoppingForRestart;
    920 }
    921 
    922 /*
    923 * Did we made some D-Bus call while stopped and
    924 * are still waiting for its response?
    925 */
    926 bool GCLocProviderPriv::InDBusStoppedCall() {
    927  return mClientState == ClientState::SettingAccuracy ||
    928         mClientState == ClientState::SettingAccuracyForStart;
    929 }
    930 
    931 void GCLocProviderPriv::DeleteManager() {
    932  if (!mProxyManager) {
    933    return;
    934  }
    935 
    936  g_signal_handlers_disconnect_matched(mProxyManager, G_SIGNAL_MATCH_DATA, 0, 0,
    937                                       nullptr, nullptr, this);
    938  mProxyManager = nullptr;
    939 }
    940 
    941 void GCLocProviderPriv::DoShutdown(bool aDeleteClient, bool aDeleteManager) {
    942  MOZ_DIAGNOSTIC_ASSERT(
    943      !aDeleteManager || aDeleteClient,
    944      "deleting manager proxy requires deleting client one, too");
    945  nsAppShell::DBusConnectionCheck();
    946 
    947  // Invalidate the cached last position
    948  StopPositionTimer();
    949  StopMLSFallbackTimer();
    950  mLastPosition = nullptr;
    951 
    952  /*
    953   * Do we need to delete the D-Bus proxy (or proxies)?
    954   * Either because that's what our caller wanted, or because we are set to
    955   * never reuse them, or because we are in a middle of some D-Bus call while
    956   * having the service running (and so not being able to issue an immediate
    957   * Stop call).
    958   */
    959  if (aDeleteClient || !kGCReuseDBusProxy ||
    960      (InDBusCall() && !InDBusStoppingCall() && !InDBusStoppedCall())) {
    961    if (mClientState == ClientState::Started) {
    962      StopClientNoWait();
    963      GCLP_SETSTATE(this, Idle);
    964    }
    965    if (mProxyClient) {
    966      g_signal_handlers_disconnect_matched(mProxyClient, G_SIGNAL_MATCH_DATA, 0,
    967                                           0, nullptr, nullptr, this);
    968    }
    969    if (mCancellable) {
    970      g_cancellable_cancel(mCancellable);
    971      mCancellable = nullptr;
    972    }
    973    mProxyClient = nullptr;
    974 
    975    if (aDeleteManager || !kGCReuseDBusProxy) {
    976      DeleteManager();
    977    }
    978 
    979    GCLP_SETSTATE(this, Uninit);
    980  } else if (mClientState == ClientState::Started) {
    981    StopClient(false);
    982  } else if (mClientState == ClientState::SettingAccuracyForStart) {
    983    GCLP_SETSTATE(this, SettingAccuracy);
    984  } else if (mClientState == ClientState::StoppingForRestart) {
    985    GCLP_SETSTATE(this, Stopping);
    986  }
    987 }
    988 
    989 void GCLocProviderPriv::DoShutdownClearCallback(bool aDestroying) {
    990  mCallback = nullptr;
    991  StopMLSFallback();
    992  DoShutdown(aDestroying, aDestroying);
    993 }
    994 
    995 NS_IMPL_ISUPPORTS(GCLocProviderPriv, nsIGeolocationProvider)
    996 
    997 // nsIGeolocationProvider
    998 //
    999 
   1000 /*
   1001 * The Startup() method should only succeed if Geoclue is available on D-Bus
   1002 * so it can be used for determining whether to continue with this geolocation
   1003 * provider in Geolocation.cpp
   1004 */
   1005 NS_IMETHODIMP
   1006 GCLocProviderPriv::Startup() {
   1007  if (mProxyManager) {
   1008    return NS_OK;
   1009  }
   1010 
   1011  MOZ_DIAGNOSTIC_ASSERT(mClientState == ClientState::Uninit,
   1012                        "Client in a initialized state but no manager");
   1013 
   1014  GUniquePtr<GError> error;
   1015  nsAppShell::DBusConnectionCheck();
   1016  mProxyManager = dont_AddRef(g_dbus_proxy_new_for_bus_sync(
   1017      G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, nullptr, kGeoclueBusName,
   1018      kGCManagerPath, kGCManagerInterface, nullptr, getter_Transfers(error)));
   1019  if (!mProxyManager) {
   1020    GCL_LOG(Info, "Cannot connect to the Manager interface: %s\n",
   1021            error->message);
   1022    return NS_ERROR_FAILURE;
   1023  }
   1024 
   1025  g_signal_connect(mProxyManager, "notify::g-name-owner",
   1026                   G_CALLBACK(GCManagerOwnerNotify), this);
   1027 
   1028  GUniquePtr<gchar> managerOwner(g_dbus_proxy_get_name_owner(mProxyManager));
   1029  if (!managerOwner) {
   1030    GCL_LOG(Info, "The Manager interface has no owner\n");
   1031    DeleteManager();
   1032    return NS_ERROR_FAILURE;
   1033  }
   1034 
   1035  GCL_LOG(Info, "Manager interface connected successfully\n");
   1036 
   1037  return NS_OK;
   1038 }
   1039 
   1040 void GCLocProviderPriv::WatchStart() {
   1041  if (mClientState == ClientState::Idle) {
   1042    StartClient();
   1043  } else if (mClientState == ClientState::Started) {
   1044    if (mLastPosition && !mLastPositionTimer) {
   1045      GCL_LOG(Verbose,
   1046              "Will report the existing position if new one doesn't come up\n");
   1047      StartLastPositionTimer();
   1048    }
   1049  } else if (mClientState == ClientState::SettingAccuracy) {
   1050    GCLP_SETSTATE(this, SettingAccuracyForStart);
   1051  } else if (mClientState == ClientState::Stopping) {
   1052    GCLP_SETSTATE(this, StoppingForRestart);
   1053  }
   1054 }
   1055 
   1056 NS_IMETHODIMP
   1057 GCLocProviderPriv::Watch(nsIGeolocationUpdate* aCallback) {
   1058  mCallback = aCallback;
   1059 
   1060  if (!mCancellable) {
   1061    mCancellable = dont_AddRef(g_cancellable_new());
   1062  }
   1063 
   1064  if (mClientState != ClientState::Uninit) {
   1065    WatchStart();
   1066    return NS_OK;
   1067  }
   1068 
   1069  if (!mProxyManager) {
   1070    GCL_LOG(Debug, "watch request falling back to MLS");
   1071    return FallbackToMLS(MLSFallback::FallbackReason::Error);
   1072  }
   1073 
   1074  StopMLSFallback();
   1075 
   1076  GCLP_SETSTATE(this, Initing);
   1077  nsAppShell::DBusConnectionCheck();
   1078  g_dbus_proxy_call(mProxyManager, "GetClient", nullptr, G_DBUS_CALL_FLAGS_NONE,
   1079                    -1, mCancellable,
   1080                    reinterpret_cast<GAsyncReadyCallback>(GetClientResponse),
   1081                    this);
   1082 
   1083  return NS_OK;
   1084 }
   1085 
   1086 NS_IMETHODIMP
   1087 GCLocProviderPriv::Shutdown() {
   1088  DoShutdownClearCallback(false);
   1089  return NS_OK;
   1090 }
   1091 
   1092 NS_IMETHODIMP
   1093 GCLocProviderPriv::SetHighAccuracy(bool aHigh) {
   1094  GCL_LOG(Verbose, "Want %s accuracy\n", aHigh ? "high" : "low");
   1095  if (!aHigh && AlwaysHighAccuracy()) {
   1096    GCL_LOG(Verbose, "Forcing high accuracy due to pref\n");
   1097    aHigh = true;
   1098  }
   1099 
   1100  mAccuracyWanted = aHigh ? Accuracy::High : Accuracy::Low;
   1101  MaybeRestartForAccuracy();
   1102 
   1103  return NS_OK;
   1104 }
   1105 
   1106 GeoclueLocationProvider::GeoclueLocationProvider() {
   1107  mPriv = new GCLocProviderPriv;
   1108 }
   1109 
   1110 // nsISupports
   1111 //
   1112 
   1113 NS_IMPL_ISUPPORTS(GeoclueLocationProvider, nsIGeolocationProvider)
   1114 
   1115 // nsIGeolocationProvider
   1116 //
   1117 
   1118 NS_IMETHODIMP
   1119 GeoclueLocationProvider::Startup() { return mPriv->Startup(); }
   1120 
   1121 NS_IMETHODIMP
   1122 GeoclueLocationProvider::Watch(nsIGeolocationUpdate* aCallback) {
   1123  return mPriv->Watch(aCallback);
   1124 }
   1125 
   1126 NS_IMETHODIMP
   1127 GeoclueLocationProvider::Shutdown() { return mPriv->Shutdown(); }
   1128 
   1129 NS_IMETHODIMP
   1130 GeoclueLocationProvider::SetHighAccuracy(bool aHigh) {
   1131  return mPriv->SetHighAccuracy(aHigh);
   1132 }
   1133 
   1134 }  // namespace mozilla::dom