dom/src/geolocation/nsGeolocation.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/src/geolocation/nsGeolocation.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1529 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +#include "nsXULAppAPI.h"
     1.9 +
    1.10 +#include "mozilla/dom/ContentChild.h"
    1.11 +#include "mozilla/dom/TabChild.h"
    1.12 +#include "mozilla/Telemetry.h"
    1.13 +
    1.14 +#include "nsISettingsService.h"
    1.15 +
    1.16 +#include "nsGeolocation.h"
    1.17 +#include "nsDOMClassInfoID.h"
    1.18 +#include "nsComponentManagerUtils.h"
    1.19 +#include "nsServiceManagerUtils.h"
    1.20 +#include "nsContentUtils.h"
    1.21 +#include "nsContentPermissionHelper.h"
    1.22 +#include "nsIDocument.h"
    1.23 +#include "nsIObserverService.h"
    1.24 +#include "nsPIDOMWindow.h"
    1.25 +#include "nsThreadUtils.h"
    1.26 +#include "mozilla/Services.h"
    1.27 +#include "mozilla/unused.h"
    1.28 +#include "mozilla/Preferences.h"
    1.29 +#include "mozilla/ClearOnShutdown.h"
    1.30 +#include "PCOMContentPermissionRequestChild.h"
    1.31 +#include "mozilla/dom/PermissionMessageUtils.h"
    1.32 +
    1.33 +class nsIPrincipal;
    1.34 +
    1.35 +#ifdef MOZ_ENABLE_QT5GEOPOSITION
    1.36 +#include "QTMLocationProvider.h"
    1.37 +#endif
    1.38 +
    1.39 +#ifdef MOZ_WIDGET_ANDROID
    1.40 +#include "AndroidLocationProvider.h"
    1.41 +#endif
    1.42 +
    1.43 +#ifdef MOZ_WIDGET_GONK
    1.44 +#include "GonkGPSGeolocationProvider.h"
    1.45 +#endif
    1.46 +
    1.47 +#ifdef MOZ_WIDGET_COCOA
    1.48 +#include "CoreLocationLocationProvider.h"
    1.49 +#endif
    1.50 +
    1.51 +// Some limit to the number of get or watch geolocation requests
    1.52 +// that a window can make.
    1.53 +#define MAX_GEO_REQUESTS_PER_WINDOW  1500
    1.54 +
    1.55 +// The settings key.
    1.56 +#define GEO_SETINGS_ENABLED          "geolocation.enabled"
    1.57 +
    1.58 +using mozilla::unused;          // <snicker>
    1.59 +using namespace mozilla;
    1.60 +using namespace mozilla::dom;
    1.61 +
    1.62 +class nsGeolocationRequest
    1.63 + : public nsIContentPermissionRequest
    1.64 + , public nsITimerCallback
    1.65 + , public nsIGeolocationUpdate
    1.66 + , public PCOMContentPermissionRequestChild
    1.67 +{
    1.68 + public:
    1.69 +  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
    1.70 +  NS_DECL_NSICONTENTPERMISSIONREQUEST
    1.71 +  NS_DECL_NSITIMERCALLBACK
    1.72 +  NS_DECL_NSIGEOLOCATIONUPDATE
    1.73 +
    1.74 +  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsGeolocationRequest, nsIContentPermissionRequest)
    1.75 +
    1.76 +  nsGeolocationRequest(Geolocation* aLocator,
    1.77 +                       const GeoPositionCallback& aCallback,
    1.78 +                       const GeoPositionErrorCallback& aErrorCallback,
    1.79 +                       PositionOptions* aOptions,
    1.80 +                       bool aWatchPositionRequest = false,
    1.81 +                       int32_t aWatchId = 0);
    1.82 +  void Shutdown();
    1.83 +
    1.84 +  void SendLocation(nsIDOMGeoPosition* location);
    1.85 +  bool WantsHighAccuracy() {return !mShutdown && mOptions && mOptions->mEnableHighAccuracy;}
    1.86 +  void SetTimeoutTimer();
    1.87 +  void StopTimeoutTimer();
    1.88 +  void NotifyErrorAndShutdown(uint16_t);
    1.89 +  nsIPrincipal* GetPrincipal();
    1.90 +
    1.91 +  ~nsGeolocationRequest();
    1.92 +
    1.93 +  virtual bool Recv__delete__(const bool& allow,
    1.94 +                              const InfallibleTArray<PermissionChoice>& choices) MOZ_OVERRIDE;
    1.95 +  virtual void IPDLRelease() MOZ_OVERRIDE { Release(); }
    1.96 +
    1.97 +  bool IsWatch() { return mIsWatchPositionRequest; }
    1.98 +  int32_t WatchId() { return mWatchId; }
    1.99 + private:
   1.100 +  bool mIsWatchPositionRequest;
   1.101 +
   1.102 +  nsCOMPtr<nsITimer> mTimeoutTimer;
   1.103 +  GeoPositionCallback mCallback;
   1.104 +  GeoPositionErrorCallback mErrorCallback;
   1.105 +  nsAutoPtr<PositionOptions> mOptions;
   1.106 +
   1.107 +  nsRefPtr<Geolocation> mLocator;
   1.108 +
   1.109 +  int32_t mWatchId;
   1.110 +  bool mShutdown;
   1.111 +};
   1.112 +
   1.113 +static PositionOptions*
   1.114 +CreatePositionOptionsCopy(const PositionOptions& aOptions)
   1.115 +{
   1.116 +  nsAutoPtr<PositionOptions> geoOptions(new PositionOptions());
   1.117 +
   1.118 +  geoOptions->mEnableHighAccuracy = aOptions.mEnableHighAccuracy;
   1.119 +  geoOptions->mMaximumAge = aOptions.mMaximumAge;
   1.120 +  geoOptions->mTimeout = aOptions.mTimeout;
   1.121 +
   1.122 +  return geoOptions.forget();
   1.123 +}
   1.124 +
   1.125 +class GeolocationSettingsCallback : public nsISettingsServiceCallback
   1.126 +{
   1.127 +public:
   1.128 +  NS_DECL_ISUPPORTS
   1.129 +
   1.130 +  GeolocationSettingsCallback() {
   1.131 +    MOZ_COUNT_CTOR(GeolocationSettingsCallback);
   1.132 +  }
   1.133 +
   1.134 +  virtual ~GeolocationSettingsCallback() {
   1.135 +    MOZ_COUNT_DTOR(GeolocationSettingsCallback);
   1.136 +  }
   1.137 +
   1.138 +  NS_IMETHOD Handle(const nsAString& aName, JS::Handle<JS::Value> aResult)
   1.139 +  {
   1.140 +    MOZ_ASSERT(NS_IsMainThread());
   1.141 +
   1.142 +    // The geolocation is enabled by default:
   1.143 +    bool value = true;
   1.144 +    if (aResult.isBoolean()) {
   1.145 +      value = aResult.toBoolean();
   1.146 +    }
   1.147 +
   1.148 +    MozSettingValue(value);
   1.149 +    return NS_OK;
   1.150 +  }
   1.151 +
   1.152 +  NS_IMETHOD HandleError(const nsAString& aName)
   1.153 +  {
   1.154 +    NS_WARNING("Unable to get value for '" GEO_SETINGS_ENABLED "'");
   1.155 +
   1.156 +    // Default it's enabled:
   1.157 +    MozSettingValue(true);
   1.158 +    return NS_OK;
   1.159 +  }
   1.160 +
   1.161 +  void MozSettingValue(const bool aValue)
   1.162 +  {
   1.163 +    nsRefPtr<nsGeolocationService> gs = nsGeolocationService::GetGeolocationService();
   1.164 +    if (gs) {
   1.165 +      gs->HandleMozsettingValue(aValue);
   1.166 +    }
   1.167 +  }
   1.168 +};
   1.169 +
   1.170 +NS_IMPL_ISUPPORTS(GeolocationSettingsCallback, nsISettingsServiceCallback)
   1.171 +
   1.172 +class RequestPromptEvent : public nsRunnable
   1.173 +{
   1.174 +public:
   1.175 +  RequestPromptEvent(nsGeolocationRequest* request)
   1.176 +    : mRequest(request)
   1.177 +  {
   1.178 +  }
   1.179 +
   1.180 +  NS_IMETHOD Run() {
   1.181 +    nsCOMPtr<nsIContentPermissionPrompt> prompt = do_CreateInstance(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
   1.182 +    if (prompt) {
   1.183 +      prompt->Prompt(mRequest);
   1.184 +    }
   1.185 +    return NS_OK;
   1.186 +  }
   1.187 +
   1.188 +private:
   1.189 +  nsRefPtr<nsGeolocationRequest> mRequest;
   1.190 +};
   1.191 +
   1.192 +class RequestAllowEvent : public nsRunnable
   1.193 +{
   1.194 +public:
   1.195 +  RequestAllowEvent(int allow, nsGeolocationRequest* request)
   1.196 +    : mAllow(allow),
   1.197 +      mRequest(request)
   1.198 +  {
   1.199 +  }
   1.200 +
   1.201 +  NS_IMETHOD Run() {
   1.202 +    if (mAllow) {
   1.203 +      mRequest->Allow(JS::UndefinedHandleValue);
   1.204 +    } else {
   1.205 +      mRequest->Cancel();
   1.206 +    }
   1.207 +    return NS_OK;
   1.208 +  }
   1.209 +
   1.210 +private:
   1.211 +  bool mAllow;
   1.212 +  nsRefPtr<nsGeolocationRequest> mRequest;
   1.213 +};
   1.214 +
   1.215 +class RequestSendLocationEvent : public nsRunnable
   1.216 +{
   1.217 +public:
   1.218 +  RequestSendLocationEvent(nsIDOMGeoPosition* aPosition,
   1.219 +                           nsGeolocationRequest* aRequest)
   1.220 +    : mPosition(aPosition),
   1.221 +      mRequest(aRequest)
   1.222 +  {
   1.223 +  }
   1.224 +
   1.225 +  NS_IMETHOD Run() {
   1.226 +    mRequest->SendLocation(mPosition);
   1.227 +    return NS_OK;
   1.228 +  }
   1.229 +
   1.230 +private:
   1.231 +  nsCOMPtr<nsIDOMGeoPosition> mPosition;
   1.232 +  nsRefPtr<nsGeolocationRequest> mRequest;
   1.233 +  nsRefPtr<Geolocation> mLocator;
   1.234 +};
   1.235 +
   1.236 +////////////////////////////////////////////////////
   1.237 +// PositionError
   1.238 +////////////////////////////////////////////////////
   1.239 +
   1.240 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PositionError)
   1.241 +  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   1.242 +  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMGeoPositionError)
   1.243 +  NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPositionError)
   1.244 +NS_INTERFACE_MAP_END
   1.245 +
   1.246 +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(PositionError, mParent)
   1.247 +NS_IMPL_CYCLE_COLLECTING_ADDREF(PositionError)
   1.248 +NS_IMPL_CYCLE_COLLECTING_RELEASE(PositionError)
   1.249 +
   1.250 +PositionError::PositionError(Geolocation* aParent, int16_t aCode)
   1.251 +  : mCode(aCode)
   1.252 +  , mParent(aParent)
   1.253 +{
   1.254 +  SetIsDOMBinding();
   1.255 +}
   1.256 +
   1.257 +PositionError::~PositionError(){}
   1.258 +
   1.259 +
   1.260 +NS_IMETHODIMP
   1.261 +PositionError::GetCode(int16_t *aCode)
   1.262 +{
   1.263 +  NS_ENSURE_ARG_POINTER(aCode);
   1.264 +  *aCode = Code();
   1.265 +  return NS_OK;
   1.266 +}
   1.267 +
   1.268 +NS_IMETHODIMP
   1.269 +PositionError::GetMessage(nsAString& aMessage)
   1.270 +{
   1.271 +  switch (mCode)
   1.272 +  {
   1.273 +    case nsIDOMGeoPositionError::PERMISSION_DENIED:
   1.274 +      aMessage = NS_LITERAL_STRING("User denied geolocation prompt");
   1.275 +      break;
   1.276 +    case nsIDOMGeoPositionError::POSITION_UNAVAILABLE:
   1.277 +      aMessage = NS_LITERAL_STRING("Unknown error acquiring position");
   1.278 +      break;
   1.279 +    case nsIDOMGeoPositionError::TIMEOUT:
   1.280 +      aMessage = NS_LITERAL_STRING("Position acquisition timed out");
   1.281 +      break;
   1.282 +    default:
   1.283 +      break;
   1.284 +  }
   1.285 +  return NS_OK;
   1.286 +}
   1.287 +
   1.288 +Geolocation*
   1.289 +PositionError::GetParentObject() const
   1.290 +{
   1.291 +  return mParent;
   1.292 +}
   1.293 +
   1.294 +JSObject*
   1.295 +PositionError::WrapObject(JSContext* aCx)
   1.296 +{
   1.297 +  return PositionErrorBinding::Wrap(aCx, this);
   1.298 +}
   1.299 +
   1.300 +void
   1.301 +PositionError::NotifyCallback(const GeoPositionErrorCallback& aCallback)
   1.302 +{
   1.303 +  nsAutoMicroTask mt;
   1.304 +  if (aCallback.HasWebIDLCallback()) {
   1.305 +    PositionErrorCallback* callback = aCallback.GetWebIDLCallback();
   1.306 +
   1.307 +    if (callback) {
   1.308 +      ErrorResult err;
   1.309 +      callback->Call(*this, err);
   1.310 +    }
   1.311 +  } else {
   1.312 +    nsIDOMGeoPositionErrorCallback* callback = aCallback.GetXPCOMCallback();
   1.313 +    if (callback) {
   1.314 +      callback->HandleEvent(this);
   1.315 +    }
   1.316 +  }
   1.317 +}
   1.318 +////////////////////////////////////////////////////
   1.319 +// nsGeolocationRequest
   1.320 +////////////////////////////////////////////////////
   1.321 +
   1.322 +nsGeolocationRequest::nsGeolocationRequest(Geolocation* aLocator,
   1.323 +                                           const GeoPositionCallback& aCallback,
   1.324 +                                           const GeoPositionErrorCallback& aErrorCallback,
   1.325 +                                           PositionOptions* aOptions,
   1.326 +                                           bool aWatchPositionRequest,
   1.327 +                                           int32_t aWatchId)
   1.328 +  : mIsWatchPositionRequest(aWatchPositionRequest),
   1.329 +    mCallback(aCallback),
   1.330 +    mErrorCallback(aErrorCallback),
   1.331 +    mOptions(aOptions),
   1.332 +    mLocator(aLocator),
   1.333 +    mWatchId(aWatchId),
   1.334 +    mShutdown(false)
   1.335 +{
   1.336 +}
   1.337 +
   1.338 +nsGeolocationRequest::~nsGeolocationRequest()
   1.339 +{
   1.340 +}
   1.341 +
   1.342 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGeolocationRequest)
   1.343 +  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
   1.344 +  NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
   1.345 +  NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
   1.346 +  NS_INTERFACE_MAP_ENTRY(nsIGeolocationUpdate)
   1.347 +NS_INTERFACE_MAP_END
   1.348 +
   1.349 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGeolocationRequest)
   1.350 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGeolocationRequest)
   1.351 +
   1.352 +NS_IMPL_CYCLE_COLLECTION(nsGeolocationRequest, mCallback, mErrorCallback, mLocator)
   1.353 +
   1.354 +NS_IMETHODIMP
   1.355 +nsGeolocationRequest::Notify(nsITimer* aTimer)
   1.356 +{
   1.357 +  StopTimeoutTimer();
   1.358 +  NotifyErrorAndShutdown(nsIDOMGeoPositionError::TIMEOUT);
   1.359 +  return NS_OK;
   1.360 +}
   1.361 +
   1.362 +void
   1.363 +nsGeolocationRequest::NotifyErrorAndShutdown(uint16_t aErrorCode)
   1.364 +{
   1.365 +  MOZ_ASSERT(!mShutdown, "timeout after shutdown");
   1.366 +
   1.367 +  if (!mIsWatchPositionRequest) {
   1.368 +    Shutdown();
   1.369 +    mLocator->RemoveRequest(this);
   1.370 +  }
   1.371 +
   1.372 +  NotifyError(aErrorCode);
   1.373 +}
   1.374 +
   1.375 +NS_IMETHODIMP
   1.376 +nsGeolocationRequest::GetPrincipal(nsIPrincipal * *aRequestingPrincipal)
   1.377 +{
   1.378 +  NS_ENSURE_ARG_POINTER(aRequestingPrincipal);
   1.379 +
   1.380 +  nsCOMPtr<nsIPrincipal> principal = mLocator->GetPrincipal();
   1.381 +  principal.forget(aRequestingPrincipal);
   1.382 +
   1.383 +  return NS_OK;
   1.384 +}
   1.385 +
   1.386 +NS_IMETHODIMP
   1.387 +nsGeolocationRequest::GetTypes(nsIArray** aTypes)
   1.388 +{
   1.389 +  nsTArray<nsString> emptyOptions;
   1.390 +  return CreatePermissionArray(NS_LITERAL_CSTRING("geolocation"),
   1.391 +                               NS_LITERAL_CSTRING("unused"),
   1.392 +                               emptyOptions,
   1.393 +                               aTypes);
   1.394 +}
   1.395 +
   1.396 +NS_IMETHODIMP
   1.397 +nsGeolocationRequest::GetWindow(nsIDOMWindow * *aRequestingWindow)
   1.398 +{
   1.399 +  NS_ENSURE_ARG_POINTER(aRequestingWindow);
   1.400 +
   1.401 +  nsCOMPtr<nsIDOMWindow> window = do_QueryReferent(mLocator->GetOwner());
   1.402 +  window.forget(aRequestingWindow);
   1.403 +
   1.404 +  return NS_OK;
   1.405 +}
   1.406 +
   1.407 +NS_IMETHODIMP
   1.408 +nsGeolocationRequest::GetElement(nsIDOMElement * *aRequestingElement)
   1.409 +{
   1.410 +  NS_ENSURE_ARG_POINTER(aRequestingElement);
   1.411 +  *aRequestingElement = nullptr;
   1.412 +  return NS_OK;
   1.413 +}
   1.414 +
   1.415 +NS_IMETHODIMP
   1.416 +nsGeolocationRequest::Cancel()
   1.417 +{
   1.418 +  NotifyError(nsIDOMGeoPositionError::PERMISSION_DENIED);
   1.419 +  return NS_OK;
   1.420 +}
   1.421 +
   1.422 +NS_IMETHODIMP
   1.423 +nsGeolocationRequest::Allow(JS::HandleValue aChoices)
   1.424 +{
   1.425 +  MOZ_ASSERT(aChoices.isUndefined());
   1.426 +
   1.427 +  // Kick off the geo device, if it isn't already running
   1.428 +  nsRefPtr<nsGeolocationService> gs = nsGeolocationService::GetGeolocationService();
   1.429 +  nsresult rv = gs->StartDevice(GetPrincipal());
   1.430 +
   1.431 +  if (NS_FAILED(rv)) {
   1.432 +    // Location provider error
   1.433 +    NotifyError(nsIDOMGeoPositionError::POSITION_UNAVAILABLE);
   1.434 +    return NS_OK;
   1.435 +  }
   1.436 +
   1.437 +  nsCOMPtr<nsIDOMGeoPosition> lastPosition = gs->GetCachedPosition();
   1.438 +  DOMTimeStamp cachedPositionTime;
   1.439 +  if (lastPosition) {
   1.440 +    lastPosition->GetTimestamp(&cachedPositionTime);
   1.441 +  }
   1.442 +
   1.443 +  // check to see if we can use a cached value
   1.444 +  // if the user has specified a maximumAge, return a cached value.
   1.445 +
   1.446 +  uint32_t maximumAge = 0;
   1.447 +  if (mOptions) {
   1.448 +    if (mOptions->mMaximumAge > 0) {
   1.449 +      maximumAge = mOptions->mMaximumAge;
   1.450 +    }
   1.451 +  }
   1.452 +  gs->UpdateAccuracy(WantsHighAccuracy());
   1.453 +
   1.454 +  bool canUseCache = lastPosition && maximumAge > 0 &&
   1.455 +    (PRTime(PR_Now() / PR_USEC_PER_MSEC) - maximumAge <=
   1.456 +    PRTime(cachedPositionTime));
   1.457 +
   1.458 +  if (canUseCache) {
   1.459 +    // okay, we can return a cached position
   1.460 +    // getCurrentPosition requests serviced by the cache
   1.461 +    // will now be owned by the RequestSendLocationEvent
   1.462 +    Update(lastPosition);
   1.463 +  }
   1.464 +
   1.465 +  if (mIsWatchPositionRequest || !canUseCache) {
   1.466 +    // let the locator know we're pending
   1.467 +    // we will now be owned by the locator
   1.468 +    mLocator->NotifyAllowedRequest(this);
   1.469 +  }
   1.470 +
   1.471 +  SetTimeoutTimer();
   1.472 +
   1.473 +  return NS_OK;
   1.474 +}
   1.475 +
   1.476 +void
   1.477 +nsGeolocationRequest::SetTimeoutTimer()
   1.478 +{
   1.479 +  StopTimeoutTimer();
   1.480 +
   1.481 +  int32_t timeout;
   1.482 +  if (mOptions && (timeout = mOptions->mTimeout) != 0) {
   1.483 +
   1.484 +    if (timeout < 0) {
   1.485 +      timeout = 0;
   1.486 +    } else if (timeout < 10) {
   1.487 +      timeout = 10;
   1.488 +    }
   1.489 +
   1.490 +    mTimeoutTimer = do_CreateInstance("@mozilla.org/timer;1");
   1.491 +    mTimeoutTimer->InitWithCallback(this, timeout, nsITimer::TYPE_ONE_SHOT);
   1.492 +  }
   1.493 +}
   1.494 +
   1.495 +void
   1.496 +nsGeolocationRequest::StopTimeoutTimer()
   1.497 +{
   1.498 +  if (mTimeoutTimer) {
   1.499 +    mTimeoutTimer->Cancel();
   1.500 +    mTimeoutTimer = nullptr;
   1.501 +  }
   1.502 +}
   1.503 +
   1.504 +void
   1.505 +nsGeolocationRequest::SendLocation(nsIDOMGeoPosition* aPosition)
   1.506 +{
   1.507 +  if (mShutdown) {
   1.508 +    // Ignore SendLocationEvents issued before we were cleared.
   1.509 +    return;
   1.510 +  }
   1.511 +
   1.512 +  nsRefPtr<Position> wrapped, cachedWrapper = mLocator->GetCachedPosition();
   1.513 +  if (cachedWrapper && aPosition == cachedWrapper->GetWrappedGeoPosition()) {
   1.514 +    wrapped = cachedWrapper;
   1.515 +  } else if (aPosition) {
   1.516 +    nsCOMPtr<nsIDOMGeoPositionCoords> coords;
   1.517 +    aPosition->GetCoords(getter_AddRefs(coords));
   1.518 +    if (coords) {
   1.519 +      wrapped = new Position(ToSupports(mLocator), aPosition);
   1.520 +    }
   1.521 +  }
   1.522 +
   1.523 +  if (!wrapped) {
   1.524 +    NotifyError(nsIDOMGeoPositionError::POSITION_UNAVAILABLE);
   1.525 +    return;
   1.526 +  }
   1.527 +
   1.528 +  mLocator->SetCachedPosition(wrapped);
   1.529 +  if (!mIsWatchPositionRequest) {
   1.530 +    // Cancel timer and position updates in case the position
   1.531 +    // callback spins the event loop
   1.532 +    Shutdown();
   1.533 +  }
   1.534 +
   1.535 +  nsAutoMicroTask mt;
   1.536 +  if (mCallback.HasWebIDLCallback()) {
   1.537 +    ErrorResult err;
   1.538 +    PositionCallback* callback = mCallback.GetWebIDLCallback();
   1.539 +
   1.540 +    MOZ_ASSERT(callback);
   1.541 +    callback->Call(*wrapped, err);
   1.542 +  } else {
   1.543 +    nsIDOMGeoPositionCallback* callback = mCallback.GetXPCOMCallback();
   1.544 +
   1.545 +    MOZ_ASSERT(callback);
   1.546 +    callback->HandleEvent(aPosition);
   1.547 +  }
   1.548 +
   1.549 +  StopTimeoutTimer();
   1.550 +  MOZ_ASSERT(mShutdown || mIsWatchPositionRequest,
   1.551 +             "non-shutdown getCurrentPosition request after callback!");
   1.552 +}
   1.553 +
   1.554 +nsIPrincipal*
   1.555 +nsGeolocationRequest::GetPrincipal()
   1.556 +{
   1.557 +  if (!mLocator) {
   1.558 +    return nullptr;
   1.559 +  }
   1.560 +  return mLocator->GetPrincipal();
   1.561 +}
   1.562 +
   1.563 +NS_IMETHODIMP
   1.564 +nsGeolocationRequest::Update(nsIDOMGeoPosition* aPosition)
   1.565 +{
   1.566 +  nsCOMPtr<nsIRunnable> ev = new RequestSendLocationEvent(aPosition, this);
   1.567 +  NS_DispatchToMainThread(ev);
   1.568 +  return NS_OK;
   1.569 +}
   1.570 +
   1.571 +NS_IMETHODIMP
   1.572 +nsGeolocationRequest::LocationUpdatePending()
   1.573 +{
   1.574 +  if (!mTimeoutTimer) {
   1.575 +    SetTimeoutTimer();
   1.576 +  }
   1.577 +
   1.578 +  return NS_OK;
   1.579 +}
   1.580 +
   1.581 +NS_IMETHODIMP
   1.582 +nsGeolocationRequest::NotifyError(uint16_t aErrorCode)
   1.583 +{
   1.584 +  MOZ_ASSERT(NS_IsMainThread());
   1.585 +
   1.586 +  nsRefPtr<PositionError> positionError = new PositionError(mLocator, aErrorCode);
   1.587 +  positionError->NotifyCallback(mErrorCallback);
   1.588 +  return NS_OK;
   1.589 +}
   1.590 +
   1.591 +void
   1.592 +nsGeolocationRequest::Shutdown()
   1.593 +{
   1.594 +  MOZ_ASSERT(!mShutdown, "request shutdown twice");
   1.595 +  mShutdown = true;
   1.596 +
   1.597 +  if (mTimeoutTimer) {
   1.598 +    mTimeoutTimer->Cancel();
   1.599 +    mTimeoutTimer = nullptr;
   1.600 +  }
   1.601 +
   1.602 +  // If there are no other high accuracy requests, the geolocation service will
   1.603 +  // notify the provider to switch to the default accuracy.
   1.604 +  if (mOptions && mOptions->mEnableHighAccuracy) {
   1.605 +    nsRefPtr<nsGeolocationService> gs = nsGeolocationService::GetGeolocationService();
   1.606 +    if (gs) {
   1.607 +      gs->UpdateAccuracy();
   1.608 +    }
   1.609 +  }
   1.610 +}
   1.611 +
   1.612 +bool nsGeolocationRequest::Recv__delete__(const bool& allow,
   1.613 +                                          const InfallibleTArray<PermissionChoice>& choices)
   1.614 +{
   1.615 +  MOZ_ASSERT(choices.IsEmpty(), "Geolocation doesn't support permission choice");
   1.616 +
   1.617 +  if (allow) {
   1.618 +    (void) Allow(JS::UndefinedHandleValue);
   1.619 +  } else {
   1.620 +    (void) Cancel();
   1.621 +  }
   1.622 +  return true;
   1.623 +}
   1.624 +////////////////////////////////////////////////////
   1.625 +// nsGeolocationService
   1.626 +////////////////////////////////////////////////////
   1.627 +NS_INTERFACE_MAP_BEGIN(nsGeolocationService)
   1.628 +  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIGeolocationUpdate)
   1.629 +  NS_INTERFACE_MAP_ENTRY(nsIGeolocationUpdate)
   1.630 +  NS_INTERFACE_MAP_ENTRY(nsIObserver)
   1.631 +NS_INTERFACE_MAP_END
   1.632 +
   1.633 +NS_IMPL_ADDREF(nsGeolocationService)
   1.634 +NS_IMPL_RELEASE(nsGeolocationService)
   1.635 +
   1.636 +
   1.637 +static bool sGeoEnabled = true;
   1.638 +static bool sGeoInitPending = true;
   1.639 +static int32_t sProviderTimeout = 6000; // Time, in milliseconds, to wait for the location provider to spin up.
   1.640 +
   1.641 +nsresult nsGeolocationService::Init()
   1.642 +{
   1.643 +  Preferences::AddIntVarCache(&sProviderTimeout, "geo.timeout", sProviderTimeout);
   1.644 +  Preferences::AddBoolVarCache(&sGeoEnabled, "geo.enabled", sGeoEnabled);
   1.645 +
   1.646 +  if (!sGeoEnabled) {
   1.647 +    return NS_ERROR_FAILURE;
   1.648 +  }
   1.649 +
   1.650 +  if (XRE_GetProcessType() == GeckoProcessType_Content) {
   1.651 +    sGeoInitPending = false;
   1.652 +    return NS_OK;
   1.653 +  }
   1.654 +
   1.655 +  // check if the geolocation service is enable from settings
   1.656 +  nsCOMPtr<nsISettingsService> settings =
   1.657 +    do_GetService("@mozilla.org/settingsService;1");
   1.658 +
   1.659 +  if (settings) {
   1.660 +    nsCOMPtr<nsISettingsServiceLock> settingsLock;
   1.661 +    nsresult rv = settings->CreateLock(nullptr, getter_AddRefs(settingsLock));
   1.662 +    NS_ENSURE_SUCCESS(rv, rv);
   1.663 +
   1.664 +    nsRefPtr<GeolocationSettingsCallback> callback = new GeolocationSettingsCallback();
   1.665 +    rv = settingsLock->Get(GEO_SETINGS_ENABLED, callback);
   1.666 +    NS_ENSURE_SUCCESS(rv, rv);
   1.667 +  } else {
   1.668 +    // If we cannot obtain the settings service, we continue
   1.669 +    // assuming that the geolocation is enabled:
   1.670 +    sGeoInitPending = false;
   1.671 +  }
   1.672 +
   1.673 +  // geolocation service can be enabled -> now register observer
   1.674 +  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   1.675 +  if (!obs) {
   1.676 +    return NS_ERROR_FAILURE;
   1.677 +  }
   1.678 +
   1.679 +  obs->AddObserver(this, "quit-application", false);
   1.680 +  obs->AddObserver(this, "mozsettings-changed", false);
   1.681 +
   1.682 +#ifdef MOZ_ENABLE_QT5GEOPOSITION
   1.683 +  mProvider = new QTMLocationProvider();
   1.684 +#endif
   1.685 +
   1.686 +#ifdef MOZ_WIDGET_ANDROID
   1.687 +  mProvider = new AndroidLocationProvider();
   1.688 +#endif
   1.689 +
   1.690 +#ifdef MOZ_WIDGET_GONK
   1.691 +  // GonkGPSGeolocationProvider can be started at boot up time for initialization reasons.
   1.692 +  // do_getService gets hold of the already initialized component and starts
   1.693 +  // processing location requests immediately.
   1.694 +  // do_Createinstance will create multiple instances of the provider which is not right.
   1.695 +  // bug 993041
   1.696 +  mProvider = do_GetService(GONK_GPS_GEOLOCATION_PROVIDER_CONTRACTID);
   1.697 +#endif
   1.698 +
   1.699 +#ifdef MOZ_WIDGET_COCOA
   1.700 +  if (Preferences::GetBool("geo.provider.use_corelocation", false)) {
   1.701 +    mProvider = new CoreLocationLocationProvider();
   1.702 +  }
   1.703 +#endif
   1.704 +
   1.705 +  if (Preferences::GetBool("geo.provider.use_mls", false)) {
   1.706 +    mProvider = do_CreateInstance("@mozilla.org/geolocation/mls-provider;1");
   1.707 +  }
   1.708 +
   1.709 +  // Override platform-specific providers with the default (network)
   1.710 +  // provider while testing. Our tests are currently not meant to exercise
   1.711 +  // the provider, and some tests rely on the network provider being used.
   1.712 +  // "geo.provider.testing" is always set for all plain and browser chrome
   1.713 +  // mochitests, and also for xpcshell tests.
   1.714 +  if (!mProvider || Preferences::GetBool("geo.provider.testing", false)) {
   1.715 +    nsCOMPtr<nsIGeolocationProvider> override =
   1.716 +      do_GetService(NS_GEOLOCATION_PROVIDER_CONTRACTID);
   1.717 +
   1.718 +    if (override) {
   1.719 +      mProvider = override;
   1.720 +    }
   1.721 +  }
   1.722 +
   1.723 +  return NS_OK;
   1.724 +}
   1.725 +
   1.726 +nsGeolocationService::~nsGeolocationService()
   1.727 +{
   1.728 +}
   1.729 +
   1.730 +void
   1.731 +nsGeolocationService::HandleMozsettingChanged(const char16_t* aData)
   1.732 +{
   1.733 +    // The string that we're interested in will be a JSON string that looks like:
   1.734 +    //  {"key":"gelocation.enabled","value":true}
   1.735 +
   1.736 +    AutoSafeJSContext cx;
   1.737 +
   1.738 +    nsDependentString dataStr(aData);
   1.739 +    JS::Rooted<JS::Value> val(cx);
   1.740 +    if (!JS_ParseJSON(cx, dataStr.get(), dataStr.Length(), &val) || !val.isObject()) {
   1.741 +      return;
   1.742 +    }
   1.743 +
   1.744 +    JS::Rooted<JSObject*> obj(cx, &val.toObject());
   1.745 +    JS::Rooted<JS::Value> key(cx);
   1.746 +    if (!JS_GetProperty(cx, obj, "key", &key) || !key.isString()) {
   1.747 +      return;
   1.748 +    }
   1.749 +
   1.750 +    bool match;
   1.751 +    if (!JS_StringEqualsAscii(cx, key.toString(), GEO_SETINGS_ENABLED, &match) || !match) {
   1.752 +      return;
   1.753 +    }
   1.754 +
   1.755 +    JS::Rooted<JS::Value> value(cx);
   1.756 +    if (!JS_GetProperty(cx, obj, "value", &value) || !value.isBoolean()) {
   1.757 +      return;
   1.758 +    }
   1.759 +
   1.760 +    HandleMozsettingValue(value.toBoolean());
   1.761 +}
   1.762 +
   1.763 +void
   1.764 +nsGeolocationService::HandleMozsettingValue(const bool aValue)
   1.765 +{
   1.766 +    if (!aValue) {
   1.767 +      // turn things off
   1.768 +      StopDevice();
   1.769 +      Update(nullptr);
   1.770 +      mLastPosition = nullptr;
   1.771 +      sGeoEnabled = false;
   1.772 +    } else {
   1.773 +      sGeoEnabled = true;
   1.774 +    }
   1.775 +
   1.776 +    if (sGeoInitPending) {
   1.777 +      sGeoInitPending = false;
   1.778 +      for (uint32_t i = 0, length = mGeolocators.Length(); i < length; ++i) {
   1.779 +        mGeolocators[i]->ServiceReady();
   1.780 +      }
   1.781 +    }
   1.782 +}
   1.783 +
   1.784 +NS_IMETHODIMP
   1.785 +nsGeolocationService::Observe(nsISupports* aSubject,
   1.786 +                              const char* aTopic,
   1.787 +                              const char16_t* aData)
   1.788 +{
   1.789 +  if (!strcmp("quit-application", aTopic)) {
   1.790 +    nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   1.791 +    if (obs) {
   1.792 +      obs->RemoveObserver(this, "quit-application");
   1.793 +      obs->RemoveObserver(this, "mozsettings-changed");
   1.794 +    }
   1.795 +
   1.796 +    for (uint32_t i = 0; i< mGeolocators.Length(); i++) {
   1.797 +      mGeolocators[i]->Shutdown();
   1.798 +    }
   1.799 +    StopDevice();
   1.800 +
   1.801 +    return NS_OK;
   1.802 +  }
   1.803 +
   1.804 +  if (!strcmp("mozsettings-changed", aTopic)) {
   1.805 +    HandleMozsettingChanged(aData);
   1.806 +    return NS_OK;
   1.807 +  }
   1.808 +
   1.809 +  if (!strcmp("timer-callback", aTopic)) {
   1.810 +    // decide if we can close down the service.
   1.811 +    for (uint32_t i = 0; i< mGeolocators.Length(); i++)
   1.812 +      if (mGeolocators[i]->HasActiveCallbacks()) {
   1.813 +        SetDisconnectTimer();
   1.814 +        return NS_OK;
   1.815 +      }
   1.816 +
   1.817 +    // okay to close up.
   1.818 +    StopDevice();
   1.819 +    Update(nullptr);
   1.820 +    return NS_OK;
   1.821 +  }
   1.822 +
   1.823 +  return NS_ERROR_FAILURE;
   1.824 +}
   1.825 +
   1.826 +NS_IMETHODIMP
   1.827 +nsGeolocationService::Update(nsIDOMGeoPosition *aSomewhere)
   1.828 +{
   1.829 +  SetCachedPosition(aSomewhere);
   1.830 +
   1.831 +  for (uint32_t i = 0; i< mGeolocators.Length(); i++) {
   1.832 +    mGeolocators[i]->Update(aSomewhere);
   1.833 +  }
   1.834 +  return NS_OK;
   1.835 +}
   1.836 +
   1.837 +NS_IMETHODIMP
   1.838 +nsGeolocationService::LocationUpdatePending()
   1.839 +{
   1.840 +  for (uint32_t i = 0; i< mGeolocators.Length(); i++) {
   1.841 +    mGeolocators[i]->LocationUpdatePending();
   1.842 +  }
   1.843 +
   1.844 +  return NS_OK;
   1.845 +}
   1.846 +
   1.847 +NS_IMETHODIMP
   1.848 +nsGeolocationService::NotifyError(uint16_t aErrorCode)
   1.849 +{
   1.850 +  for (uint32_t i = 0; i < mGeolocators.Length(); i++) {
   1.851 +    mGeolocators[i]->NotifyError(aErrorCode);
   1.852 +  }
   1.853 +
   1.854 +  return NS_OK;
   1.855 +}
   1.856 +
   1.857 +void
   1.858 +nsGeolocationService::SetCachedPosition(nsIDOMGeoPosition* aPosition)
   1.859 +{
   1.860 +  mLastPosition = aPosition;
   1.861 +}
   1.862 +
   1.863 +nsIDOMGeoPosition*
   1.864 +nsGeolocationService::GetCachedPosition()
   1.865 +{
   1.866 +  return mLastPosition;
   1.867 +}
   1.868 +
   1.869 +nsresult
   1.870 +nsGeolocationService::StartDevice(nsIPrincipal *aPrincipal)
   1.871 +{
   1.872 +  if (!sGeoEnabled || sGeoInitPending) {
   1.873 +    return NS_ERROR_NOT_AVAILABLE;
   1.874 +  }
   1.875 +
   1.876 +  // we do not want to keep the geolocation devices online
   1.877 +  // indefinitely.  Close them down after a reasonable period of
   1.878 +  // inactivivity
   1.879 +  SetDisconnectTimer();
   1.880 +
   1.881 +  if (XRE_GetProcessType() == GeckoProcessType_Content) {
   1.882 +    ContentChild* cpc = ContentChild::GetSingleton();
   1.883 +    cpc->SendAddGeolocationListener(IPC::Principal(aPrincipal),
   1.884 +                                    HighAccuracyRequested());
   1.885 +    return NS_OK;
   1.886 +  }
   1.887 +
   1.888 +  // Start them up!
   1.889 +  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   1.890 +  if (!obs) {
   1.891 +    return NS_ERROR_FAILURE;
   1.892 +  }
   1.893 +
   1.894 +  if (!mProvider) {
   1.895 +    return NS_ERROR_FAILURE;
   1.896 +  }
   1.897 +
   1.898 +  nsresult rv;
   1.899 +
   1.900 +  if (NS_FAILED(rv = mProvider->Startup()) ||
   1.901 +      NS_FAILED(rv = mProvider->Watch(this))) {
   1.902 +
   1.903 +    NotifyError(nsIDOMGeoPositionError::POSITION_UNAVAILABLE);
   1.904 +    return rv;
   1.905 +  }
   1.906 +
   1.907 +  obs->NotifyObservers(mProvider,
   1.908 +                       "geolocation-device-events",
   1.909 +                       MOZ_UTF16("starting"));
   1.910 +
   1.911 +  return NS_OK;
   1.912 +}
   1.913 +
   1.914 +void
   1.915 +nsGeolocationService::SetDisconnectTimer()
   1.916 +{
   1.917 +  if (!mDisconnectTimer) {
   1.918 +    mDisconnectTimer = do_CreateInstance("@mozilla.org/timer;1");
   1.919 +  } else {
   1.920 +    mDisconnectTimer->Cancel();
   1.921 +  }
   1.922 +
   1.923 +  mDisconnectTimer->Init(this,
   1.924 +                         sProviderTimeout,
   1.925 +                         nsITimer::TYPE_ONE_SHOT);
   1.926 +}
   1.927 +
   1.928 +bool
   1.929 +nsGeolocationService::HighAccuracyRequested()
   1.930 +{
   1.931 +  for (uint32_t i = 0; i < mGeolocators.Length(); i++) {
   1.932 +    if (mGeolocators[i]->HighAccuracyRequested()) {
   1.933 +      return true;
   1.934 +    }
   1.935 +  }
   1.936 +  return false;
   1.937 +}
   1.938 +
   1.939 +void
   1.940 +nsGeolocationService::UpdateAccuracy(bool aForceHigh)
   1.941 +{
   1.942 +  bool highRequired = aForceHigh || HighAccuracyRequested();
   1.943 +
   1.944 +  if (XRE_GetProcessType() == GeckoProcessType_Content) {
   1.945 +    ContentChild* cpc = ContentChild::GetSingleton();
   1.946 +    cpc->SendSetGeolocationHigherAccuracy(highRequired);
   1.947 +    return;
   1.948 +  }
   1.949 +
   1.950 +  if (!mHigherAccuracy && highRequired) {
   1.951 +      mProvider->SetHighAccuracy(true);
   1.952 +  }
   1.953 +
   1.954 +  if (mHigherAccuracy && !highRequired) {
   1.955 +      mProvider->SetHighAccuracy(false);
   1.956 +  }
   1.957 +
   1.958 +  mHigherAccuracy = highRequired;
   1.959 +}
   1.960 +
   1.961 +void
   1.962 +nsGeolocationService::StopDevice()
   1.963 +{
   1.964 +  if(mDisconnectTimer) {
   1.965 +    mDisconnectTimer->Cancel();
   1.966 +    mDisconnectTimer = nullptr;
   1.967 +  }
   1.968 +
   1.969 +  if (XRE_GetProcessType() == GeckoProcessType_Content) {
   1.970 +    ContentChild* cpc = ContentChild::GetSingleton();
   1.971 +    cpc->SendRemoveGeolocationListener();
   1.972 +    return; // bail early
   1.973 +  }
   1.974 +
   1.975 +  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   1.976 +  if (!obs) {
   1.977 +    return;
   1.978 +  }
   1.979 +
   1.980 +  if (!mProvider) {
   1.981 +    return;
   1.982 +  }
   1.983 +
   1.984 +  mHigherAccuracy = false;
   1.985 +
   1.986 +  mProvider->Shutdown();
   1.987 +  obs->NotifyObservers(mProvider,
   1.988 +                       "geolocation-device-events",
   1.989 +                       MOZ_UTF16("shutdown"));
   1.990 +}
   1.991 +
   1.992 +StaticRefPtr<nsGeolocationService> nsGeolocationService::sService;
   1.993 +
   1.994 +already_AddRefed<nsGeolocationService>
   1.995 +nsGeolocationService::GetGeolocationService()
   1.996 +{
   1.997 +  nsRefPtr<nsGeolocationService> result;
   1.998 +  if (nsGeolocationService::sService) {
   1.999 +    result = nsGeolocationService::sService;
  1.1000 +    return result.forget();
  1.1001 +  }
  1.1002 +
  1.1003 +  result = new nsGeolocationService();
  1.1004 +  if (NS_FAILED(result->Init())) {
  1.1005 +    return nullptr;
  1.1006 +  }
  1.1007 +  ClearOnShutdown(&nsGeolocationService::sService);
  1.1008 +  nsGeolocationService::sService = result;
  1.1009 +  return result.forget();
  1.1010 +}
  1.1011 +
  1.1012 +void
  1.1013 +nsGeolocationService::AddLocator(Geolocation* aLocator)
  1.1014 +{
  1.1015 +  mGeolocators.AppendElement(aLocator);
  1.1016 +}
  1.1017 +
  1.1018 +void
  1.1019 +nsGeolocationService::RemoveLocator(Geolocation* aLocator)
  1.1020 +{
  1.1021 +  mGeolocators.RemoveElement(aLocator);
  1.1022 +}
  1.1023 +
  1.1024 +////////////////////////////////////////////////////
  1.1025 +// Geolocation
  1.1026 +////////////////////////////////////////////////////
  1.1027 +
  1.1028 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Geolocation)
  1.1029 +  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
  1.1030 +  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMGeoGeolocation)
  1.1031 +  NS_INTERFACE_MAP_ENTRY(nsIDOMGeoGeolocation)
  1.1032 +  NS_INTERFACE_MAP_ENTRY(nsIGeolocationUpdate)
  1.1033 +NS_INTERFACE_MAP_END
  1.1034 +
  1.1035 +NS_IMPL_CYCLE_COLLECTING_ADDREF(Geolocation)
  1.1036 +NS_IMPL_CYCLE_COLLECTING_RELEASE(Geolocation)
  1.1037 +
  1.1038 +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_4(Geolocation,
  1.1039 +                                        mCachedPosition,
  1.1040 +                                        mPendingCallbacks,
  1.1041 +                                        mWatchingCallbacks,
  1.1042 +                                        mPendingRequests)
  1.1043 +
  1.1044 +Geolocation::Geolocation()
  1.1045 +: mLastWatchId(0)
  1.1046 +{
  1.1047 +  SetIsDOMBinding();
  1.1048 +}
  1.1049 +
  1.1050 +Geolocation::~Geolocation()
  1.1051 +{
  1.1052 +  if (mService) {
  1.1053 +    Shutdown();
  1.1054 +  }
  1.1055 +}
  1.1056 +
  1.1057 +nsresult
  1.1058 +Geolocation::Init(nsIDOMWindow* aContentDom)
  1.1059 +{
  1.1060 +  // Remember the window
  1.1061 +  if (aContentDom) {
  1.1062 +    nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aContentDom);
  1.1063 +    if (!window) {
  1.1064 +      return NS_ERROR_FAILURE;
  1.1065 +    }
  1.1066 +
  1.1067 +    mOwner = do_GetWeakReference(window->GetCurrentInnerWindow());
  1.1068 +    if (!mOwner) {
  1.1069 +      return NS_ERROR_FAILURE;
  1.1070 +    }
  1.1071 +
  1.1072 +    // Grab the principal of the document
  1.1073 +    nsCOMPtr<nsIDocument> doc = window->GetDoc();
  1.1074 +    if (!doc) {
  1.1075 +      return NS_ERROR_FAILURE;
  1.1076 +    }
  1.1077 +
  1.1078 +    mPrincipal = doc->NodePrincipal();
  1.1079 +  }
  1.1080 +
  1.1081 +  // If no aContentDom was passed into us, we are being used
  1.1082 +  // by chrome/c++ and have no mOwner, no mPrincipal, and no need
  1.1083 +  // to prompt.
  1.1084 +  mService = nsGeolocationService::GetGeolocationService();
  1.1085 +  if (mService) {
  1.1086 +    mService->AddLocator(this);
  1.1087 +  }
  1.1088 +  return NS_OK;
  1.1089 +}
  1.1090 +
  1.1091 +void
  1.1092 +Geolocation::Shutdown()
  1.1093 +{
  1.1094 +  // Release all callbacks
  1.1095 +  mPendingCallbacks.Clear();
  1.1096 +  mWatchingCallbacks.Clear();
  1.1097 +
  1.1098 +  if (mService) {
  1.1099 +    mService->RemoveLocator(this);
  1.1100 +    mService->UpdateAccuracy();
  1.1101 +  }
  1.1102 +
  1.1103 +  mService = nullptr;
  1.1104 +  mPrincipal = nullptr;
  1.1105 +}
  1.1106 +
  1.1107 +nsIDOMWindow*
  1.1108 +Geolocation::GetParentObject() const {
  1.1109 +  nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mOwner);
  1.1110 +  return window.get();
  1.1111 +}
  1.1112 +
  1.1113 +bool
  1.1114 +Geolocation::HasActiveCallbacks()
  1.1115 +{
  1.1116 +  return mPendingCallbacks.Length() || mWatchingCallbacks.Length();
  1.1117 +}
  1.1118 +
  1.1119 +bool
  1.1120 +Geolocation::HighAccuracyRequested()
  1.1121 +{
  1.1122 +  for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) {
  1.1123 +    if (mWatchingCallbacks[i]->WantsHighAccuracy()) {
  1.1124 +      return true;
  1.1125 +    }
  1.1126 +  }
  1.1127 +
  1.1128 +  for (uint32_t i = 0; i < mPendingCallbacks.Length(); i++) {
  1.1129 +    if (mPendingCallbacks[i]->WantsHighAccuracy()) {
  1.1130 +      return true;
  1.1131 +    }
  1.1132 +  }
  1.1133 +
  1.1134 +  return false;
  1.1135 +}
  1.1136 +
  1.1137 +void
  1.1138 +Geolocation::RemoveRequest(nsGeolocationRequest* aRequest)
  1.1139 +{
  1.1140 +  bool requestWasKnown =
  1.1141 +    (mPendingCallbacks.RemoveElement(aRequest) !=
  1.1142 +     mWatchingCallbacks.RemoveElement(aRequest));
  1.1143 +
  1.1144 +  unused << requestWasKnown;
  1.1145 +}
  1.1146 +
  1.1147 +NS_IMETHODIMP
  1.1148 +Geolocation::Update(nsIDOMGeoPosition *aSomewhere)
  1.1149 +{
  1.1150 +  if (!WindowOwnerStillExists()) {
  1.1151 +    Shutdown();
  1.1152 +    return NS_OK;
  1.1153 +  }
  1.1154 +
  1.1155 +  if (aSomewhere) {
  1.1156 +    nsCOMPtr<nsIDOMGeoPositionCoords> coords;
  1.1157 +    aSomewhere->GetCoords(getter_AddRefs(coords));
  1.1158 +    if (coords) {
  1.1159 +      double accuracy = -1;
  1.1160 +      coords->GetAccuracy(&accuracy);
  1.1161 +      mozilla::Telemetry::Accumulate(mozilla::Telemetry::GEOLOCATION_ACCURACY, accuracy);
  1.1162 +    }
  1.1163 +  }
  1.1164 +
  1.1165 +  for (uint32_t i = mPendingCallbacks.Length(); i > 0; i--) {
  1.1166 +    mPendingCallbacks[i-1]->Update(aSomewhere);
  1.1167 +    RemoveRequest(mPendingCallbacks[i-1]);
  1.1168 +  }
  1.1169 +
  1.1170 +  // notify everyone that is watching
  1.1171 +  for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) {
  1.1172 +    mWatchingCallbacks[i]->Update(aSomewhere);
  1.1173 +  }
  1.1174 +
  1.1175 +  return NS_OK;
  1.1176 +}
  1.1177 +
  1.1178 +NS_IMETHODIMP
  1.1179 +Geolocation::LocationUpdatePending()
  1.1180 +{
  1.1181 +  // this event is only really interesting for watch callbacks
  1.1182 +  for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) {
  1.1183 +    mWatchingCallbacks[i]->LocationUpdatePending();
  1.1184 +  }
  1.1185 +
  1.1186 +  return NS_OK;
  1.1187 +}
  1.1188 +
  1.1189 +NS_IMETHODIMP
  1.1190 +Geolocation::NotifyError(uint16_t aErrorCode)
  1.1191 +{
  1.1192 +  if (!WindowOwnerStillExists()) {
  1.1193 +    Shutdown();
  1.1194 +    return NS_OK;
  1.1195 +  }
  1.1196 +
  1.1197 +  mozilla::Telemetry::Accumulate(mozilla::Telemetry::GEOLOCATION_ERROR, true);
  1.1198 +
  1.1199 +  for (uint32_t i = mPendingCallbacks.Length(); i > 0; i--) {
  1.1200 +    mPendingCallbacks[i-1]->NotifyErrorAndShutdown(aErrorCode);
  1.1201 +    //NotifyErrorAndShutdown() removes the request from the array
  1.1202 +  }
  1.1203 +
  1.1204 +  // notify everyone that is watching
  1.1205 +  for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) {
  1.1206 +    mWatchingCallbacks[i]->NotifyErrorAndShutdown(aErrorCode);
  1.1207 +  }
  1.1208 +
  1.1209 +  return NS_OK;
  1.1210 +}
  1.1211 +
  1.1212 +void
  1.1213 +Geolocation::SetCachedPosition(Position* aPosition)
  1.1214 +{
  1.1215 +  mCachedPosition = aPosition;
  1.1216 +}
  1.1217 +
  1.1218 +Position*
  1.1219 +Geolocation::GetCachedPosition()
  1.1220 +{
  1.1221 +  return mCachedPosition;
  1.1222 +}
  1.1223 +
  1.1224 +void
  1.1225 +Geolocation::GetCurrentPosition(PositionCallback& aCallback,
  1.1226 +                                PositionErrorCallback* aErrorCallback,
  1.1227 +                                const PositionOptions& aOptions,
  1.1228 +                                ErrorResult& aRv)
  1.1229 +{
  1.1230 +  GeoPositionCallback successCallback(&aCallback);
  1.1231 +  GeoPositionErrorCallback errorCallback(aErrorCallback);
  1.1232 +
  1.1233 +  nsresult rv = GetCurrentPosition(successCallback, errorCallback,
  1.1234 +                                   CreatePositionOptionsCopy(aOptions));
  1.1235 +
  1.1236 +  if (NS_FAILED(rv)) {
  1.1237 +    aRv.Throw(rv);
  1.1238 +  }
  1.1239 +
  1.1240 +  return;
  1.1241 +}
  1.1242 +
  1.1243 +NS_IMETHODIMP
  1.1244 +Geolocation::GetCurrentPosition(nsIDOMGeoPositionCallback* aCallback,
  1.1245 +                                nsIDOMGeoPositionErrorCallback* aErrorCallback,
  1.1246 +                                PositionOptions* aOptions)
  1.1247 +{
  1.1248 +  NS_ENSURE_ARG_POINTER(aCallback);
  1.1249 +
  1.1250 +  GeoPositionCallback successCallback(aCallback);
  1.1251 +  GeoPositionErrorCallback errorCallback(aErrorCallback);
  1.1252 +
  1.1253 +  return GetCurrentPosition(successCallback, errorCallback, aOptions);
  1.1254 +}
  1.1255 +
  1.1256 +nsresult
  1.1257 +Geolocation::GetCurrentPosition(GeoPositionCallback& callback,
  1.1258 +                                GeoPositionErrorCallback& errorCallback,
  1.1259 +                                PositionOptions *options)
  1.1260 +{
  1.1261 +  if (mPendingCallbacks.Length() > MAX_GEO_REQUESTS_PER_WINDOW) {
  1.1262 +    return NS_ERROR_NOT_AVAILABLE;
  1.1263 +  }
  1.1264 +
  1.1265 +  nsRefPtr<nsGeolocationRequest> request = new nsGeolocationRequest(this,
  1.1266 +                                                                    callback,
  1.1267 +                                                                    errorCallback,
  1.1268 +                                                                    options,
  1.1269 +                                                                    false);
  1.1270 +
  1.1271 +  if (!sGeoEnabled) {
  1.1272 +    nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(false, request);
  1.1273 +    NS_DispatchToMainThread(ev);
  1.1274 +    return NS_OK;
  1.1275 +  }
  1.1276 +
  1.1277 +  if (!mOwner && !nsContentUtils::IsCallerChrome()) {
  1.1278 +    return NS_ERROR_FAILURE;
  1.1279 +  }
  1.1280 +
  1.1281 +  if (sGeoInitPending) {
  1.1282 +    mPendingRequests.AppendElement(request);
  1.1283 +    return NS_OK;
  1.1284 +  }
  1.1285 +
  1.1286 +  return GetCurrentPositionReady(request);
  1.1287 +}
  1.1288 +
  1.1289 +nsresult
  1.1290 +Geolocation::GetCurrentPositionReady(nsGeolocationRequest* aRequest)
  1.1291 +{
  1.1292 +  if (mOwner) {
  1.1293 +    if (!RegisterRequestWithPrompt(aRequest)) {
  1.1294 +      return NS_ERROR_NOT_AVAILABLE;
  1.1295 +    }
  1.1296 +
  1.1297 +    return NS_OK;
  1.1298 +  }
  1.1299 +
  1.1300 +  if (!nsContentUtils::IsCallerChrome()) {
  1.1301 +    return NS_ERROR_FAILURE;
  1.1302 +  }
  1.1303 +
  1.1304 +  nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(true, aRequest);
  1.1305 +  NS_DispatchToMainThread(ev);
  1.1306 +
  1.1307 +  return NS_OK;
  1.1308 +}
  1.1309 +
  1.1310 +int32_t
  1.1311 +Geolocation::WatchPosition(PositionCallback& aCallback,
  1.1312 +                           PositionErrorCallback* aErrorCallback,
  1.1313 +                           const PositionOptions& aOptions,
  1.1314 +                           ErrorResult& aRv)
  1.1315 +{
  1.1316 +  int32_t ret;
  1.1317 +  GeoPositionCallback successCallback(&aCallback);
  1.1318 +  GeoPositionErrorCallback errorCallback(aErrorCallback);
  1.1319 +
  1.1320 +  nsresult rv = WatchPosition(successCallback, errorCallback,
  1.1321 +                              CreatePositionOptionsCopy(aOptions), &ret);
  1.1322 +
  1.1323 +  if (NS_FAILED(rv)) {
  1.1324 +    aRv.Throw(rv);
  1.1325 +  }
  1.1326 +
  1.1327 +  return ret;
  1.1328 +}
  1.1329 +
  1.1330 +NS_IMETHODIMP
  1.1331 +Geolocation::WatchPosition(nsIDOMGeoPositionCallback *aCallback,
  1.1332 +                           nsIDOMGeoPositionErrorCallback *aErrorCallback,
  1.1333 +                           PositionOptions *aOptions,
  1.1334 +                           int32_t* aRv)
  1.1335 +{
  1.1336 +  NS_ENSURE_ARG_POINTER(aCallback);
  1.1337 +
  1.1338 +  GeoPositionCallback successCallback(aCallback);
  1.1339 +  GeoPositionErrorCallback errorCallback(aErrorCallback);
  1.1340 +
  1.1341 +  return WatchPosition(successCallback, errorCallback, aOptions, aRv);
  1.1342 +}
  1.1343 +
  1.1344 +nsresult
  1.1345 +Geolocation::WatchPosition(GeoPositionCallback& aCallback,
  1.1346 +                           GeoPositionErrorCallback& aErrorCallback,
  1.1347 +                           PositionOptions* aOptions,
  1.1348 +                           int32_t* aRv)
  1.1349 +{
  1.1350 +  if (mWatchingCallbacks.Length() > MAX_GEO_REQUESTS_PER_WINDOW) {
  1.1351 +    return NS_ERROR_NOT_AVAILABLE;
  1.1352 +  }
  1.1353 +
  1.1354 +  // The watch ID:
  1.1355 +  *aRv = mLastWatchId++;
  1.1356 +
  1.1357 +  nsRefPtr<nsGeolocationRequest> request = new nsGeolocationRequest(this,
  1.1358 +                                                                    aCallback,
  1.1359 +                                                                    aErrorCallback,
  1.1360 +                                                                    aOptions,
  1.1361 +                                                                    true,
  1.1362 +                                                                    *aRv);
  1.1363 +
  1.1364 +  if (!sGeoEnabled) {
  1.1365 +    nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(false, request);
  1.1366 +    NS_DispatchToMainThread(ev);
  1.1367 +    return NS_OK;
  1.1368 +  }
  1.1369 +
  1.1370 +  if (!mOwner && !nsContentUtils::IsCallerChrome()) {
  1.1371 +    return NS_ERROR_FAILURE;
  1.1372 +  }
  1.1373 +
  1.1374 +  if (sGeoInitPending) {
  1.1375 +    mPendingRequests.AppendElement(request);
  1.1376 +    return NS_OK;
  1.1377 +  }
  1.1378 +
  1.1379 +  return WatchPositionReady(request);
  1.1380 +}
  1.1381 +
  1.1382 +nsresult
  1.1383 +Geolocation::WatchPositionReady(nsGeolocationRequest* aRequest)
  1.1384 +{
  1.1385 +  if (mOwner) {
  1.1386 +    if (!RegisterRequestWithPrompt(aRequest))
  1.1387 +      return NS_ERROR_NOT_AVAILABLE;
  1.1388 +
  1.1389 +    return NS_OK;
  1.1390 +  }
  1.1391 +
  1.1392 +  if (!nsContentUtils::IsCallerChrome()) {
  1.1393 +    return NS_ERROR_FAILURE;
  1.1394 +  }
  1.1395 +
  1.1396 +  aRequest->Allow(JS::UndefinedHandleValue);
  1.1397 +
  1.1398 +  return NS_OK;
  1.1399 +}
  1.1400 +
  1.1401 +NS_IMETHODIMP
  1.1402 +Geolocation::ClearWatch(int32_t aWatchId)
  1.1403 +{
  1.1404 +  if (aWatchId < 0) {
  1.1405 +    return NS_OK;
  1.1406 +  }
  1.1407 +
  1.1408 +  for (uint32_t i = 0, length = mWatchingCallbacks.Length(); i < length; ++i) {
  1.1409 +    if (mWatchingCallbacks[i]->WatchId() == aWatchId) {
  1.1410 +      mWatchingCallbacks[i]->Shutdown();
  1.1411 +      RemoveRequest(mWatchingCallbacks[i]);
  1.1412 +      break;
  1.1413 +    }
  1.1414 +  }
  1.1415 +
  1.1416 +  // make sure we also search through the pending requests lists for
  1.1417 +  // watches to clear...
  1.1418 +  for (uint32_t i = 0, length = mPendingRequests.Length(); i < length; ++i) {
  1.1419 +    if (mPendingRequests[i]->IsWatch() &&
  1.1420 +        (mPendingRequests[i]->WatchId() == aWatchId)) {
  1.1421 +      mPendingRequests[i]->Shutdown();
  1.1422 +      mPendingRequests.RemoveElementAt(i);
  1.1423 +      break;
  1.1424 +    }
  1.1425 +  }
  1.1426 +
  1.1427 +  return NS_OK;
  1.1428 +}
  1.1429 +
  1.1430 +void
  1.1431 +Geolocation::ServiceReady()
  1.1432 +{
  1.1433 +  for (uint32_t length = mPendingRequests.Length(); length > 0; --length) {
  1.1434 +    if (mPendingRequests[0]->IsWatch()) {
  1.1435 +      WatchPositionReady(mPendingRequests[0]);
  1.1436 +    } else {
  1.1437 +      GetCurrentPositionReady(mPendingRequests[0]);
  1.1438 +    }
  1.1439 +
  1.1440 +    mPendingRequests.RemoveElementAt(0);
  1.1441 +  }
  1.1442 +}
  1.1443 +
  1.1444 +bool
  1.1445 +Geolocation::WindowOwnerStillExists()
  1.1446 +{
  1.1447 +  // an owner was never set when Geolocation
  1.1448 +  // was created, which means that this object
  1.1449 +  // is being used without a window.
  1.1450 +  if (mOwner == nullptr) {
  1.1451 +    return true;
  1.1452 +  }
  1.1453 +
  1.1454 +  nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mOwner);
  1.1455 +
  1.1456 +  if (window) {
  1.1457 +    bool closed = false;
  1.1458 +    window->GetClosed(&closed);
  1.1459 +    if (closed) {
  1.1460 +      return false;
  1.1461 +    }
  1.1462 +
  1.1463 +    nsPIDOMWindow* outer = window->GetOuterWindow();
  1.1464 +    if (!outer || outer->GetCurrentInnerWindow() != window) {
  1.1465 +      return false;
  1.1466 +    }
  1.1467 +  }
  1.1468 +
  1.1469 +  return true;
  1.1470 +}
  1.1471 +
  1.1472 +void
  1.1473 +Geolocation::NotifyAllowedRequest(nsGeolocationRequest* aRequest)
  1.1474 +{
  1.1475 +  if (aRequest->IsWatch()) {
  1.1476 +    mWatchingCallbacks.AppendElement(aRequest);
  1.1477 +  } else {
  1.1478 +    mPendingCallbacks.AppendElement(aRequest);
  1.1479 +  }
  1.1480 +}
  1.1481 +
  1.1482 +bool
  1.1483 +Geolocation::RegisterRequestWithPrompt(nsGeolocationRequest* request)
  1.1484 +{
  1.1485 +  if (Preferences::GetBool("geo.prompt.testing", false)) {
  1.1486 +    bool allow = Preferences::GetBool("geo.prompt.testing.allow", false);
  1.1487 +    nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(allow,
  1.1488 +						     request);
  1.1489 +    NS_DispatchToMainThread(ev);
  1.1490 +    return true;
  1.1491 +  }
  1.1492 +
  1.1493 +  if (XRE_GetProcessType() == GeckoProcessType_Content) {
  1.1494 +    nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mOwner);
  1.1495 +    if (!window) {
  1.1496 +      return true;
  1.1497 +    }
  1.1498 +
  1.1499 +    // because owner implements nsITabChild, we can assume that it is
  1.1500 +    // the one and only TabChild.
  1.1501 +    TabChild* child = TabChild::GetFrom(window->GetDocShell());
  1.1502 +    if (!child) {
  1.1503 +      return false;
  1.1504 +    }
  1.1505 +
  1.1506 +    nsTArray<PermissionRequest> permArray;
  1.1507 +    nsTArray<nsString> emptyOptions;
  1.1508 +    permArray.AppendElement(PermissionRequest(NS_LITERAL_CSTRING("geolocation"),
  1.1509 +                                              NS_LITERAL_CSTRING("unused"),
  1.1510 +                                              emptyOptions));
  1.1511 +
  1.1512 +    // Retain a reference so the object isn't deleted without IPDL's knowledge.
  1.1513 +    // Corresponding release occurs in DeallocPContentPermissionRequest.
  1.1514 +    request->AddRef();
  1.1515 +    child->SendPContentPermissionRequestConstructor(request,
  1.1516 +                                                    permArray,
  1.1517 +                                                    IPC::Principal(mPrincipal));
  1.1518 +
  1.1519 +    request->Sendprompt();
  1.1520 +    return true;
  1.1521 +  }
  1.1522 +
  1.1523 +  nsCOMPtr<nsIRunnable> ev  = new RequestPromptEvent(request);
  1.1524 +  NS_DispatchToMainThread(ev);
  1.1525 +  return true;
  1.1526 +}
  1.1527 +
  1.1528 +JSObject*
  1.1529 +Geolocation::WrapObject(JSContext *aCtx)
  1.1530 +{
  1.1531 +  return GeolocationBinding::Wrap(aCtx, this);
  1.1532 +}

mercurial