dom/src/geolocation/nsGeolocation.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 #include "nsXULAppAPI.h"
michael@0 6
michael@0 7 #include "mozilla/dom/ContentChild.h"
michael@0 8 #include "mozilla/dom/TabChild.h"
michael@0 9 #include "mozilla/Telemetry.h"
michael@0 10
michael@0 11 #include "nsISettingsService.h"
michael@0 12
michael@0 13 #include "nsGeolocation.h"
michael@0 14 #include "nsDOMClassInfoID.h"
michael@0 15 #include "nsComponentManagerUtils.h"
michael@0 16 #include "nsServiceManagerUtils.h"
michael@0 17 #include "nsContentUtils.h"
michael@0 18 #include "nsContentPermissionHelper.h"
michael@0 19 #include "nsIDocument.h"
michael@0 20 #include "nsIObserverService.h"
michael@0 21 #include "nsPIDOMWindow.h"
michael@0 22 #include "nsThreadUtils.h"
michael@0 23 #include "mozilla/Services.h"
michael@0 24 #include "mozilla/unused.h"
michael@0 25 #include "mozilla/Preferences.h"
michael@0 26 #include "mozilla/ClearOnShutdown.h"
michael@0 27 #include "PCOMContentPermissionRequestChild.h"
michael@0 28 #include "mozilla/dom/PermissionMessageUtils.h"
michael@0 29
michael@0 30 class nsIPrincipal;
michael@0 31
michael@0 32 #ifdef MOZ_ENABLE_QT5GEOPOSITION
michael@0 33 #include "QTMLocationProvider.h"
michael@0 34 #endif
michael@0 35
michael@0 36 #ifdef MOZ_WIDGET_ANDROID
michael@0 37 #include "AndroidLocationProvider.h"
michael@0 38 #endif
michael@0 39
michael@0 40 #ifdef MOZ_WIDGET_GONK
michael@0 41 #include "GonkGPSGeolocationProvider.h"
michael@0 42 #endif
michael@0 43
michael@0 44 #ifdef MOZ_WIDGET_COCOA
michael@0 45 #include "CoreLocationLocationProvider.h"
michael@0 46 #endif
michael@0 47
michael@0 48 // Some limit to the number of get or watch geolocation requests
michael@0 49 // that a window can make.
michael@0 50 #define MAX_GEO_REQUESTS_PER_WINDOW 1500
michael@0 51
michael@0 52 // The settings key.
michael@0 53 #define GEO_SETINGS_ENABLED "geolocation.enabled"
michael@0 54
michael@0 55 using mozilla::unused; // <snicker>
michael@0 56 using namespace mozilla;
michael@0 57 using namespace mozilla::dom;
michael@0 58
michael@0 59 class nsGeolocationRequest
michael@0 60 : public nsIContentPermissionRequest
michael@0 61 , public nsITimerCallback
michael@0 62 , public nsIGeolocationUpdate
michael@0 63 , public PCOMContentPermissionRequestChild
michael@0 64 {
michael@0 65 public:
michael@0 66 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
michael@0 67 NS_DECL_NSICONTENTPERMISSIONREQUEST
michael@0 68 NS_DECL_NSITIMERCALLBACK
michael@0 69 NS_DECL_NSIGEOLOCATIONUPDATE
michael@0 70
michael@0 71 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsGeolocationRequest, nsIContentPermissionRequest)
michael@0 72
michael@0 73 nsGeolocationRequest(Geolocation* aLocator,
michael@0 74 const GeoPositionCallback& aCallback,
michael@0 75 const GeoPositionErrorCallback& aErrorCallback,
michael@0 76 PositionOptions* aOptions,
michael@0 77 bool aWatchPositionRequest = false,
michael@0 78 int32_t aWatchId = 0);
michael@0 79 void Shutdown();
michael@0 80
michael@0 81 void SendLocation(nsIDOMGeoPosition* location);
michael@0 82 bool WantsHighAccuracy() {return !mShutdown && mOptions && mOptions->mEnableHighAccuracy;}
michael@0 83 void SetTimeoutTimer();
michael@0 84 void StopTimeoutTimer();
michael@0 85 void NotifyErrorAndShutdown(uint16_t);
michael@0 86 nsIPrincipal* GetPrincipal();
michael@0 87
michael@0 88 ~nsGeolocationRequest();
michael@0 89
michael@0 90 virtual bool Recv__delete__(const bool& allow,
michael@0 91 const InfallibleTArray<PermissionChoice>& choices) MOZ_OVERRIDE;
michael@0 92 virtual void IPDLRelease() MOZ_OVERRIDE { Release(); }
michael@0 93
michael@0 94 bool IsWatch() { return mIsWatchPositionRequest; }
michael@0 95 int32_t WatchId() { return mWatchId; }
michael@0 96 private:
michael@0 97 bool mIsWatchPositionRequest;
michael@0 98
michael@0 99 nsCOMPtr<nsITimer> mTimeoutTimer;
michael@0 100 GeoPositionCallback mCallback;
michael@0 101 GeoPositionErrorCallback mErrorCallback;
michael@0 102 nsAutoPtr<PositionOptions> mOptions;
michael@0 103
michael@0 104 nsRefPtr<Geolocation> mLocator;
michael@0 105
michael@0 106 int32_t mWatchId;
michael@0 107 bool mShutdown;
michael@0 108 };
michael@0 109
michael@0 110 static PositionOptions*
michael@0 111 CreatePositionOptionsCopy(const PositionOptions& aOptions)
michael@0 112 {
michael@0 113 nsAutoPtr<PositionOptions> geoOptions(new PositionOptions());
michael@0 114
michael@0 115 geoOptions->mEnableHighAccuracy = aOptions.mEnableHighAccuracy;
michael@0 116 geoOptions->mMaximumAge = aOptions.mMaximumAge;
michael@0 117 geoOptions->mTimeout = aOptions.mTimeout;
michael@0 118
michael@0 119 return geoOptions.forget();
michael@0 120 }
michael@0 121
michael@0 122 class GeolocationSettingsCallback : public nsISettingsServiceCallback
michael@0 123 {
michael@0 124 public:
michael@0 125 NS_DECL_ISUPPORTS
michael@0 126
michael@0 127 GeolocationSettingsCallback() {
michael@0 128 MOZ_COUNT_CTOR(GeolocationSettingsCallback);
michael@0 129 }
michael@0 130
michael@0 131 virtual ~GeolocationSettingsCallback() {
michael@0 132 MOZ_COUNT_DTOR(GeolocationSettingsCallback);
michael@0 133 }
michael@0 134
michael@0 135 NS_IMETHOD Handle(const nsAString& aName, JS::Handle<JS::Value> aResult)
michael@0 136 {
michael@0 137 MOZ_ASSERT(NS_IsMainThread());
michael@0 138
michael@0 139 // The geolocation is enabled by default:
michael@0 140 bool value = true;
michael@0 141 if (aResult.isBoolean()) {
michael@0 142 value = aResult.toBoolean();
michael@0 143 }
michael@0 144
michael@0 145 MozSettingValue(value);
michael@0 146 return NS_OK;
michael@0 147 }
michael@0 148
michael@0 149 NS_IMETHOD HandleError(const nsAString& aName)
michael@0 150 {
michael@0 151 NS_WARNING("Unable to get value for '" GEO_SETINGS_ENABLED "'");
michael@0 152
michael@0 153 // Default it's enabled:
michael@0 154 MozSettingValue(true);
michael@0 155 return NS_OK;
michael@0 156 }
michael@0 157
michael@0 158 void MozSettingValue(const bool aValue)
michael@0 159 {
michael@0 160 nsRefPtr<nsGeolocationService> gs = nsGeolocationService::GetGeolocationService();
michael@0 161 if (gs) {
michael@0 162 gs->HandleMozsettingValue(aValue);
michael@0 163 }
michael@0 164 }
michael@0 165 };
michael@0 166
michael@0 167 NS_IMPL_ISUPPORTS(GeolocationSettingsCallback, nsISettingsServiceCallback)
michael@0 168
michael@0 169 class RequestPromptEvent : public nsRunnable
michael@0 170 {
michael@0 171 public:
michael@0 172 RequestPromptEvent(nsGeolocationRequest* request)
michael@0 173 : mRequest(request)
michael@0 174 {
michael@0 175 }
michael@0 176
michael@0 177 NS_IMETHOD Run() {
michael@0 178 nsCOMPtr<nsIContentPermissionPrompt> prompt = do_CreateInstance(NS_CONTENT_PERMISSION_PROMPT_CONTRACTID);
michael@0 179 if (prompt) {
michael@0 180 prompt->Prompt(mRequest);
michael@0 181 }
michael@0 182 return NS_OK;
michael@0 183 }
michael@0 184
michael@0 185 private:
michael@0 186 nsRefPtr<nsGeolocationRequest> mRequest;
michael@0 187 };
michael@0 188
michael@0 189 class RequestAllowEvent : public nsRunnable
michael@0 190 {
michael@0 191 public:
michael@0 192 RequestAllowEvent(int allow, nsGeolocationRequest* request)
michael@0 193 : mAllow(allow),
michael@0 194 mRequest(request)
michael@0 195 {
michael@0 196 }
michael@0 197
michael@0 198 NS_IMETHOD Run() {
michael@0 199 if (mAllow) {
michael@0 200 mRequest->Allow(JS::UndefinedHandleValue);
michael@0 201 } else {
michael@0 202 mRequest->Cancel();
michael@0 203 }
michael@0 204 return NS_OK;
michael@0 205 }
michael@0 206
michael@0 207 private:
michael@0 208 bool mAllow;
michael@0 209 nsRefPtr<nsGeolocationRequest> mRequest;
michael@0 210 };
michael@0 211
michael@0 212 class RequestSendLocationEvent : public nsRunnable
michael@0 213 {
michael@0 214 public:
michael@0 215 RequestSendLocationEvent(nsIDOMGeoPosition* aPosition,
michael@0 216 nsGeolocationRequest* aRequest)
michael@0 217 : mPosition(aPosition),
michael@0 218 mRequest(aRequest)
michael@0 219 {
michael@0 220 }
michael@0 221
michael@0 222 NS_IMETHOD Run() {
michael@0 223 mRequest->SendLocation(mPosition);
michael@0 224 return NS_OK;
michael@0 225 }
michael@0 226
michael@0 227 private:
michael@0 228 nsCOMPtr<nsIDOMGeoPosition> mPosition;
michael@0 229 nsRefPtr<nsGeolocationRequest> mRequest;
michael@0 230 nsRefPtr<Geolocation> mLocator;
michael@0 231 };
michael@0 232
michael@0 233 ////////////////////////////////////////////////////
michael@0 234 // PositionError
michael@0 235 ////////////////////////////////////////////////////
michael@0 236
michael@0 237 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PositionError)
michael@0 238 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
michael@0 239 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMGeoPositionError)
michael@0 240 NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPositionError)
michael@0 241 NS_INTERFACE_MAP_END
michael@0 242
michael@0 243 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(PositionError, mParent)
michael@0 244 NS_IMPL_CYCLE_COLLECTING_ADDREF(PositionError)
michael@0 245 NS_IMPL_CYCLE_COLLECTING_RELEASE(PositionError)
michael@0 246
michael@0 247 PositionError::PositionError(Geolocation* aParent, int16_t aCode)
michael@0 248 : mCode(aCode)
michael@0 249 , mParent(aParent)
michael@0 250 {
michael@0 251 SetIsDOMBinding();
michael@0 252 }
michael@0 253
michael@0 254 PositionError::~PositionError(){}
michael@0 255
michael@0 256
michael@0 257 NS_IMETHODIMP
michael@0 258 PositionError::GetCode(int16_t *aCode)
michael@0 259 {
michael@0 260 NS_ENSURE_ARG_POINTER(aCode);
michael@0 261 *aCode = Code();
michael@0 262 return NS_OK;
michael@0 263 }
michael@0 264
michael@0 265 NS_IMETHODIMP
michael@0 266 PositionError::GetMessage(nsAString& aMessage)
michael@0 267 {
michael@0 268 switch (mCode)
michael@0 269 {
michael@0 270 case nsIDOMGeoPositionError::PERMISSION_DENIED:
michael@0 271 aMessage = NS_LITERAL_STRING("User denied geolocation prompt");
michael@0 272 break;
michael@0 273 case nsIDOMGeoPositionError::POSITION_UNAVAILABLE:
michael@0 274 aMessage = NS_LITERAL_STRING("Unknown error acquiring position");
michael@0 275 break;
michael@0 276 case nsIDOMGeoPositionError::TIMEOUT:
michael@0 277 aMessage = NS_LITERAL_STRING("Position acquisition timed out");
michael@0 278 break;
michael@0 279 default:
michael@0 280 break;
michael@0 281 }
michael@0 282 return NS_OK;
michael@0 283 }
michael@0 284
michael@0 285 Geolocation*
michael@0 286 PositionError::GetParentObject() const
michael@0 287 {
michael@0 288 return mParent;
michael@0 289 }
michael@0 290
michael@0 291 JSObject*
michael@0 292 PositionError::WrapObject(JSContext* aCx)
michael@0 293 {
michael@0 294 return PositionErrorBinding::Wrap(aCx, this);
michael@0 295 }
michael@0 296
michael@0 297 void
michael@0 298 PositionError::NotifyCallback(const GeoPositionErrorCallback& aCallback)
michael@0 299 {
michael@0 300 nsAutoMicroTask mt;
michael@0 301 if (aCallback.HasWebIDLCallback()) {
michael@0 302 PositionErrorCallback* callback = aCallback.GetWebIDLCallback();
michael@0 303
michael@0 304 if (callback) {
michael@0 305 ErrorResult err;
michael@0 306 callback->Call(*this, err);
michael@0 307 }
michael@0 308 } else {
michael@0 309 nsIDOMGeoPositionErrorCallback* callback = aCallback.GetXPCOMCallback();
michael@0 310 if (callback) {
michael@0 311 callback->HandleEvent(this);
michael@0 312 }
michael@0 313 }
michael@0 314 }
michael@0 315 ////////////////////////////////////////////////////
michael@0 316 // nsGeolocationRequest
michael@0 317 ////////////////////////////////////////////////////
michael@0 318
michael@0 319 nsGeolocationRequest::nsGeolocationRequest(Geolocation* aLocator,
michael@0 320 const GeoPositionCallback& aCallback,
michael@0 321 const GeoPositionErrorCallback& aErrorCallback,
michael@0 322 PositionOptions* aOptions,
michael@0 323 bool aWatchPositionRequest,
michael@0 324 int32_t aWatchId)
michael@0 325 : mIsWatchPositionRequest(aWatchPositionRequest),
michael@0 326 mCallback(aCallback),
michael@0 327 mErrorCallback(aErrorCallback),
michael@0 328 mOptions(aOptions),
michael@0 329 mLocator(aLocator),
michael@0 330 mWatchId(aWatchId),
michael@0 331 mShutdown(false)
michael@0 332 {
michael@0 333 }
michael@0 334
michael@0 335 nsGeolocationRequest::~nsGeolocationRequest()
michael@0 336 {
michael@0 337 }
michael@0 338
michael@0 339 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGeolocationRequest)
michael@0 340 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
michael@0 341 NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
michael@0 342 NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
michael@0 343 NS_INTERFACE_MAP_ENTRY(nsIGeolocationUpdate)
michael@0 344 NS_INTERFACE_MAP_END
michael@0 345
michael@0 346 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGeolocationRequest)
michael@0 347 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGeolocationRequest)
michael@0 348
michael@0 349 NS_IMPL_CYCLE_COLLECTION(nsGeolocationRequest, mCallback, mErrorCallback, mLocator)
michael@0 350
michael@0 351 NS_IMETHODIMP
michael@0 352 nsGeolocationRequest::Notify(nsITimer* aTimer)
michael@0 353 {
michael@0 354 StopTimeoutTimer();
michael@0 355 NotifyErrorAndShutdown(nsIDOMGeoPositionError::TIMEOUT);
michael@0 356 return NS_OK;
michael@0 357 }
michael@0 358
michael@0 359 void
michael@0 360 nsGeolocationRequest::NotifyErrorAndShutdown(uint16_t aErrorCode)
michael@0 361 {
michael@0 362 MOZ_ASSERT(!mShutdown, "timeout after shutdown");
michael@0 363
michael@0 364 if (!mIsWatchPositionRequest) {
michael@0 365 Shutdown();
michael@0 366 mLocator->RemoveRequest(this);
michael@0 367 }
michael@0 368
michael@0 369 NotifyError(aErrorCode);
michael@0 370 }
michael@0 371
michael@0 372 NS_IMETHODIMP
michael@0 373 nsGeolocationRequest::GetPrincipal(nsIPrincipal * *aRequestingPrincipal)
michael@0 374 {
michael@0 375 NS_ENSURE_ARG_POINTER(aRequestingPrincipal);
michael@0 376
michael@0 377 nsCOMPtr<nsIPrincipal> principal = mLocator->GetPrincipal();
michael@0 378 principal.forget(aRequestingPrincipal);
michael@0 379
michael@0 380 return NS_OK;
michael@0 381 }
michael@0 382
michael@0 383 NS_IMETHODIMP
michael@0 384 nsGeolocationRequest::GetTypes(nsIArray** aTypes)
michael@0 385 {
michael@0 386 nsTArray<nsString> emptyOptions;
michael@0 387 return CreatePermissionArray(NS_LITERAL_CSTRING("geolocation"),
michael@0 388 NS_LITERAL_CSTRING("unused"),
michael@0 389 emptyOptions,
michael@0 390 aTypes);
michael@0 391 }
michael@0 392
michael@0 393 NS_IMETHODIMP
michael@0 394 nsGeolocationRequest::GetWindow(nsIDOMWindow * *aRequestingWindow)
michael@0 395 {
michael@0 396 NS_ENSURE_ARG_POINTER(aRequestingWindow);
michael@0 397
michael@0 398 nsCOMPtr<nsIDOMWindow> window = do_QueryReferent(mLocator->GetOwner());
michael@0 399 window.forget(aRequestingWindow);
michael@0 400
michael@0 401 return NS_OK;
michael@0 402 }
michael@0 403
michael@0 404 NS_IMETHODIMP
michael@0 405 nsGeolocationRequest::GetElement(nsIDOMElement * *aRequestingElement)
michael@0 406 {
michael@0 407 NS_ENSURE_ARG_POINTER(aRequestingElement);
michael@0 408 *aRequestingElement = nullptr;
michael@0 409 return NS_OK;
michael@0 410 }
michael@0 411
michael@0 412 NS_IMETHODIMP
michael@0 413 nsGeolocationRequest::Cancel()
michael@0 414 {
michael@0 415 NotifyError(nsIDOMGeoPositionError::PERMISSION_DENIED);
michael@0 416 return NS_OK;
michael@0 417 }
michael@0 418
michael@0 419 NS_IMETHODIMP
michael@0 420 nsGeolocationRequest::Allow(JS::HandleValue aChoices)
michael@0 421 {
michael@0 422 MOZ_ASSERT(aChoices.isUndefined());
michael@0 423
michael@0 424 // Kick off the geo device, if it isn't already running
michael@0 425 nsRefPtr<nsGeolocationService> gs = nsGeolocationService::GetGeolocationService();
michael@0 426 nsresult rv = gs->StartDevice(GetPrincipal());
michael@0 427
michael@0 428 if (NS_FAILED(rv)) {
michael@0 429 // Location provider error
michael@0 430 NotifyError(nsIDOMGeoPositionError::POSITION_UNAVAILABLE);
michael@0 431 return NS_OK;
michael@0 432 }
michael@0 433
michael@0 434 nsCOMPtr<nsIDOMGeoPosition> lastPosition = gs->GetCachedPosition();
michael@0 435 DOMTimeStamp cachedPositionTime;
michael@0 436 if (lastPosition) {
michael@0 437 lastPosition->GetTimestamp(&cachedPositionTime);
michael@0 438 }
michael@0 439
michael@0 440 // check to see if we can use a cached value
michael@0 441 // if the user has specified a maximumAge, return a cached value.
michael@0 442
michael@0 443 uint32_t maximumAge = 0;
michael@0 444 if (mOptions) {
michael@0 445 if (mOptions->mMaximumAge > 0) {
michael@0 446 maximumAge = mOptions->mMaximumAge;
michael@0 447 }
michael@0 448 }
michael@0 449 gs->UpdateAccuracy(WantsHighAccuracy());
michael@0 450
michael@0 451 bool canUseCache = lastPosition && maximumAge > 0 &&
michael@0 452 (PRTime(PR_Now() / PR_USEC_PER_MSEC) - maximumAge <=
michael@0 453 PRTime(cachedPositionTime));
michael@0 454
michael@0 455 if (canUseCache) {
michael@0 456 // okay, we can return a cached position
michael@0 457 // getCurrentPosition requests serviced by the cache
michael@0 458 // will now be owned by the RequestSendLocationEvent
michael@0 459 Update(lastPosition);
michael@0 460 }
michael@0 461
michael@0 462 if (mIsWatchPositionRequest || !canUseCache) {
michael@0 463 // let the locator know we're pending
michael@0 464 // we will now be owned by the locator
michael@0 465 mLocator->NotifyAllowedRequest(this);
michael@0 466 }
michael@0 467
michael@0 468 SetTimeoutTimer();
michael@0 469
michael@0 470 return NS_OK;
michael@0 471 }
michael@0 472
michael@0 473 void
michael@0 474 nsGeolocationRequest::SetTimeoutTimer()
michael@0 475 {
michael@0 476 StopTimeoutTimer();
michael@0 477
michael@0 478 int32_t timeout;
michael@0 479 if (mOptions && (timeout = mOptions->mTimeout) != 0) {
michael@0 480
michael@0 481 if (timeout < 0) {
michael@0 482 timeout = 0;
michael@0 483 } else if (timeout < 10) {
michael@0 484 timeout = 10;
michael@0 485 }
michael@0 486
michael@0 487 mTimeoutTimer = do_CreateInstance("@mozilla.org/timer;1");
michael@0 488 mTimeoutTimer->InitWithCallback(this, timeout, nsITimer::TYPE_ONE_SHOT);
michael@0 489 }
michael@0 490 }
michael@0 491
michael@0 492 void
michael@0 493 nsGeolocationRequest::StopTimeoutTimer()
michael@0 494 {
michael@0 495 if (mTimeoutTimer) {
michael@0 496 mTimeoutTimer->Cancel();
michael@0 497 mTimeoutTimer = nullptr;
michael@0 498 }
michael@0 499 }
michael@0 500
michael@0 501 void
michael@0 502 nsGeolocationRequest::SendLocation(nsIDOMGeoPosition* aPosition)
michael@0 503 {
michael@0 504 if (mShutdown) {
michael@0 505 // Ignore SendLocationEvents issued before we were cleared.
michael@0 506 return;
michael@0 507 }
michael@0 508
michael@0 509 nsRefPtr<Position> wrapped, cachedWrapper = mLocator->GetCachedPosition();
michael@0 510 if (cachedWrapper && aPosition == cachedWrapper->GetWrappedGeoPosition()) {
michael@0 511 wrapped = cachedWrapper;
michael@0 512 } else if (aPosition) {
michael@0 513 nsCOMPtr<nsIDOMGeoPositionCoords> coords;
michael@0 514 aPosition->GetCoords(getter_AddRefs(coords));
michael@0 515 if (coords) {
michael@0 516 wrapped = new Position(ToSupports(mLocator), aPosition);
michael@0 517 }
michael@0 518 }
michael@0 519
michael@0 520 if (!wrapped) {
michael@0 521 NotifyError(nsIDOMGeoPositionError::POSITION_UNAVAILABLE);
michael@0 522 return;
michael@0 523 }
michael@0 524
michael@0 525 mLocator->SetCachedPosition(wrapped);
michael@0 526 if (!mIsWatchPositionRequest) {
michael@0 527 // Cancel timer and position updates in case the position
michael@0 528 // callback spins the event loop
michael@0 529 Shutdown();
michael@0 530 }
michael@0 531
michael@0 532 nsAutoMicroTask mt;
michael@0 533 if (mCallback.HasWebIDLCallback()) {
michael@0 534 ErrorResult err;
michael@0 535 PositionCallback* callback = mCallback.GetWebIDLCallback();
michael@0 536
michael@0 537 MOZ_ASSERT(callback);
michael@0 538 callback->Call(*wrapped, err);
michael@0 539 } else {
michael@0 540 nsIDOMGeoPositionCallback* callback = mCallback.GetXPCOMCallback();
michael@0 541
michael@0 542 MOZ_ASSERT(callback);
michael@0 543 callback->HandleEvent(aPosition);
michael@0 544 }
michael@0 545
michael@0 546 StopTimeoutTimer();
michael@0 547 MOZ_ASSERT(mShutdown || mIsWatchPositionRequest,
michael@0 548 "non-shutdown getCurrentPosition request after callback!");
michael@0 549 }
michael@0 550
michael@0 551 nsIPrincipal*
michael@0 552 nsGeolocationRequest::GetPrincipal()
michael@0 553 {
michael@0 554 if (!mLocator) {
michael@0 555 return nullptr;
michael@0 556 }
michael@0 557 return mLocator->GetPrincipal();
michael@0 558 }
michael@0 559
michael@0 560 NS_IMETHODIMP
michael@0 561 nsGeolocationRequest::Update(nsIDOMGeoPosition* aPosition)
michael@0 562 {
michael@0 563 nsCOMPtr<nsIRunnable> ev = new RequestSendLocationEvent(aPosition, this);
michael@0 564 NS_DispatchToMainThread(ev);
michael@0 565 return NS_OK;
michael@0 566 }
michael@0 567
michael@0 568 NS_IMETHODIMP
michael@0 569 nsGeolocationRequest::LocationUpdatePending()
michael@0 570 {
michael@0 571 if (!mTimeoutTimer) {
michael@0 572 SetTimeoutTimer();
michael@0 573 }
michael@0 574
michael@0 575 return NS_OK;
michael@0 576 }
michael@0 577
michael@0 578 NS_IMETHODIMP
michael@0 579 nsGeolocationRequest::NotifyError(uint16_t aErrorCode)
michael@0 580 {
michael@0 581 MOZ_ASSERT(NS_IsMainThread());
michael@0 582
michael@0 583 nsRefPtr<PositionError> positionError = new PositionError(mLocator, aErrorCode);
michael@0 584 positionError->NotifyCallback(mErrorCallback);
michael@0 585 return NS_OK;
michael@0 586 }
michael@0 587
michael@0 588 void
michael@0 589 nsGeolocationRequest::Shutdown()
michael@0 590 {
michael@0 591 MOZ_ASSERT(!mShutdown, "request shutdown twice");
michael@0 592 mShutdown = true;
michael@0 593
michael@0 594 if (mTimeoutTimer) {
michael@0 595 mTimeoutTimer->Cancel();
michael@0 596 mTimeoutTimer = nullptr;
michael@0 597 }
michael@0 598
michael@0 599 // If there are no other high accuracy requests, the geolocation service will
michael@0 600 // notify the provider to switch to the default accuracy.
michael@0 601 if (mOptions && mOptions->mEnableHighAccuracy) {
michael@0 602 nsRefPtr<nsGeolocationService> gs = nsGeolocationService::GetGeolocationService();
michael@0 603 if (gs) {
michael@0 604 gs->UpdateAccuracy();
michael@0 605 }
michael@0 606 }
michael@0 607 }
michael@0 608
michael@0 609 bool nsGeolocationRequest::Recv__delete__(const bool& allow,
michael@0 610 const InfallibleTArray<PermissionChoice>& choices)
michael@0 611 {
michael@0 612 MOZ_ASSERT(choices.IsEmpty(), "Geolocation doesn't support permission choice");
michael@0 613
michael@0 614 if (allow) {
michael@0 615 (void) Allow(JS::UndefinedHandleValue);
michael@0 616 } else {
michael@0 617 (void) Cancel();
michael@0 618 }
michael@0 619 return true;
michael@0 620 }
michael@0 621 ////////////////////////////////////////////////////
michael@0 622 // nsGeolocationService
michael@0 623 ////////////////////////////////////////////////////
michael@0 624 NS_INTERFACE_MAP_BEGIN(nsGeolocationService)
michael@0 625 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIGeolocationUpdate)
michael@0 626 NS_INTERFACE_MAP_ENTRY(nsIGeolocationUpdate)
michael@0 627 NS_INTERFACE_MAP_ENTRY(nsIObserver)
michael@0 628 NS_INTERFACE_MAP_END
michael@0 629
michael@0 630 NS_IMPL_ADDREF(nsGeolocationService)
michael@0 631 NS_IMPL_RELEASE(nsGeolocationService)
michael@0 632
michael@0 633
michael@0 634 static bool sGeoEnabled = true;
michael@0 635 static bool sGeoInitPending = true;
michael@0 636 static int32_t sProviderTimeout = 6000; // Time, in milliseconds, to wait for the location provider to spin up.
michael@0 637
michael@0 638 nsresult nsGeolocationService::Init()
michael@0 639 {
michael@0 640 Preferences::AddIntVarCache(&sProviderTimeout, "geo.timeout", sProviderTimeout);
michael@0 641 Preferences::AddBoolVarCache(&sGeoEnabled, "geo.enabled", sGeoEnabled);
michael@0 642
michael@0 643 if (!sGeoEnabled) {
michael@0 644 return NS_ERROR_FAILURE;
michael@0 645 }
michael@0 646
michael@0 647 if (XRE_GetProcessType() == GeckoProcessType_Content) {
michael@0 648 sGeoInitPending = false;
michael@0 649 return NS_OK;
michael@0 650 }
michael@0 651
michael@0 652 // check if the geolocation service is enable from settings
michael@0 653 nsCOMPtr<nsISettingsService> settings =
michael@0 654 do_GetService("@mozilla.org/settingsService;1");
michael@0 655
michael@0 656 if (settings) {
michael@0 657 nsCOMPtr<nsISettingsServiceLock> settingsLock;
michael@0 658 nsresult rv = settings->CreateLock(nullptr, getter_AddRefs(settingsLock));
michael@0 659 NS_ENSURE_SUCCESS(rv, rv);
michael@0 660
michael@0 661 nsRefPtr<GeolocationSettingsCallback> callback = new GeolocationSettingsCallback();
michael@0 662 rv = settingsLock->Get(GEO_SETINGS_ENABLED, callback);
michael@0 663 NS_ENSURE_SUCCESS(rv, rv);
michael@0 664 } else {
michael@0 665 // If we cannot obtain the settings service, we continue
michael@0 666 // assuming that the geolocation is enabled:
michael@0 667 sGeoInitPending = false;
michael@0 668 }
michael@0 669
michael@0 670 // geolocation service can be enabled -> now register observer
michael@0 671 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
michael@0 672 if (!obs) {
michael@0 673 return NS_ERROR_FAILURE;
michael@0 674 }
michael@0 675
michael@0 676 obs->AddObserver(this, "quit-application", false);
michael@0 677 obs->AddObserver(this, "mozsettings-changed", false);
michael@0 678
michael@0 679 #ifdef MOZ_ENABLE_QT5GEOPOSITION
michael@0 680 mProvider = new QTMLocationProvider();
michael@0 681 #endif
michael@0 682
michael@0 683 #ifdef MOZ_WIDGET_ANDROID
michael@0 684 mProvider = new AndroidLocationProvider();
michael@0 685 #endif
michael@0 686
michael@0 687 #ifdef MOZ_WIDGET_GONK
michael@0 688 // GonkGPSGeolocationProvider can be started at boot up time for initialization reasons.
michael@0 689 // do_getService gets hold of the already initialized component and starts
michael@0 690 // processing location requests immediately.
michael@0 691 // do_Createinstance will create multiple instances of the provider which is not right.
michael@0 692 // bug 993041
michael@0 693 mProvider = do_GetService(GONK_GPS_GEOLOCATION_PROVIDER_CONTRACTID);
michael@0 694 #endif
michael@0 695
michael@0 696 #ifdef MOZ_WIDGET_COCOA
michael@0 697 if (Preferences::GetBool("geo.provider.use_corelocation", false)) {
michael@0 698 mProvider = new CoreLocationLocationProvider();
michael@0 699 }
michael@0 700 #endif
michael@0 701
michael@0 702 if (Preferences::GetBool("geo.provider.use_mls", false)) {
michael@0 703 mProvider = do_CreateInstance("@mozilla.org/geolocation/mls-provider;1");
michael@0 704 }
michael@0 705
michael@0 706 // Override platform-specific providers with the default (network)
michael@0 707 // provider while testing. Our tests are currently not meant to exercise
michael@0 708 // the provider, and some tests rely on the network provider being used.
michael@0 709 // "geo.provider.testing" is always set for all plain and browser chrome
michael@0 710 // mochitests, and also for xpcshell tests.
michael@0 711 if (!mProvider || Preferences::GetBool("geo.provider.testing", false)) {
michael@0 712 nsCOMPtr<nsIGeolocationProvider> override =
michael@0 713 do_GetService(NS_GEOLOCATION_PROVIDER_CONTRACTID);
michael@0 714
michael@0 715 if (override) {
michael@0 716 mProvider = override;
michael@0 717 }
michael@0 718 }
michael@0 719
michael@0 720 return NS_OK;
michael@0 721 }
michael@0 722
michael@0 723 nsGeolocationService::~nsGeolocationService()
michael@0 724 {
michael@0 725 }
michael@0 726
michael@0 727 void
michael@0 728 nsGeolocationService::HandleMozsettingChanged(const char16_t* aData)
michael@0 729 {
michael@0 730 // The string that we're interested in will be a JSON string that looks like:
michael@0 731 // {"key":"gelocation.enabled","value":true}
michael@0 732
michael@0 733 AutoSafeJSContext cx;
michael@0 734
michael@0 735 nsDependentString dataStr(aData);
michael@0 736 JS::Rooted<JS::Value> val(cx);
michael@0 737 if (!JS_ParseJSON(cx, dataStr.get(), dataStr.Length(), &val) || !val.isObject()) {
michael@0 738 return;
michael@0 739 }
michael@0 740
michael@0 741 JS::Rooted<JSObject*> obj(cx, &val.toObject());
michael@0 742 JS::Rooted<JS::Value> key(cx);
michael@0 743 if (!JS_GetProperty(cx, obj, "key", &key) || !key.isString()) {
michael@0 744 return;
michael@0 745 }
michael@0 746
michael@0 747 bool match;
michael@0 748 if (!JS_StringEqualsAscii(cx, key.toString(), GEO_SETINGS_ENABLED, &match) || !match) {
michael@0 749 return;
michael@0 750 }
michael@0 751
michael@0 752 JS::Rooted<JS::Value> value(cx);
michael@0 753 if (!JS_GetProperty(cx, obj, "value", &value) || !value.isBoolean()) {
michael@0 754 return;
michael@0 755 }
michael@0 756
michael@0 757 HandleMozsettingValue(value.toBoolean());
michael@0 758 }
michael@0 759
michael@0 760 void
michael@0 761 nsGeolocationService::HandleMozsettingValue(const bool aValue)
michael@0 762 {
michael@0 763 if (!aValue) {
michael@0 764 // turn things off
michael@0 765 StopDevice();
michael@0 766 Update(nullptr);
michael@0 767 mLastPosition = nullptr;
michael@0 768 sGeoEnabled = false;
michael@0 769 } else {
michael@0 770 sGeoEnabled = true;
michael@0 771 }
michael@0 772
michael@0 773 if (sGeoInitPending) {
michael@0 774 sGeoInitPending = false;
michael@0 775 for (uint32_t i = 0, length = mGeolocators.Length(); i < length; ++i) {
michael@0 776 mGeolocators[i]->ServiceReady();
michael@0 777 }
michael@0 778 }
michael@0 779 }
michael@0 780
michael@0 781 NS_IMETHODIMP
michael@0 782 nsGeolocationService::Observe(nsISupports* aSubject,
michael@0 783 const char* aTopic,
michael@0 784 const char16_t* aData)
michael@0 785 {
michael@0 786 if (!strcmp("quit-application", aTopic)) {
michael@0 787 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
michael@0 788 if (obs) {
michael@0 789 obs->RemoveObserver(this, "quit-application");
michael@0 790 obs->RemoveObserver(this, "mozsettings-changed");
michael@0 791 }
michael@0 792
michael@0 793 for (uint32_t i = 0; i< mGeolocators.Length(); i++) {
michael@0 794 mGeolocators[i]->Shutdown();
michael@0 795 }
michael@0 796 StopDevice();
michael@0 797
michael@0 798 return NS_OK;
michael@0 799 }
michael@0 800
michael@0 801 if (!strcmp("mozsettings-changed", aTopic)) {
michael@0 802 HandleMozsettingChanged(aData);
michael@0 803 return NS_OK;
michael@0 804 }
michael@0 805
michael@0 806 if (!strcmp("timer-callback", aTopic)) {
michael@0 807 // decide if we can close down the service.
michael@0 808 for (uint32_t i = 0; i< mGeolocators.Length(); i++)
michael@0 809 if (mGeolocators[i]->HasActiveCallbacks()) {
michael@0 810 SetDisconnectTimer();
michael@0 811 return NS_OK;
michael@0 812 }
michael@0 813
michael@0 814 // okay to close up.
michael@0 815 StopDevice();
michael@0 816 Update(nullptr);
michael@0 817 return NS_OK;
michael@0 818 }
michael@0 819
michael@0 820 return NS_ERROR_FAILURE;
michael@0 821 }
michael@0 822
michael@0 823 NS_IMETHODIMP
michael@0 824 nsGeolocationService::Update(nsIDOMGeoPosition *aSomewhere)
michael@0 825 {
michael@0 826 SetCachedPosition(aSomewhere);
michael@0 827
michael@0 828 for (uint32_t i = 0; i< mGeolocators.Length(); i++) {
michael@0 829 mGeolocators[i]->Update(aSomewhere);
michael@0 830 }
michael@0 831 return NS_OK;
michael@0 832 }
michael@0 833
michael@0 834 NS_IMETHODIMP
michael@0 835 nsGeolocationService::LocationUpdatePending()
michael@0 836 {
michael@0 837 for (uint32_t i = 0; i< mGeolocators.Length(); i++) {
michael@0 838 mGeolocators[i]->LocationUpdatePending();
michael@0 839 }
michael@0 840
michael@0 841 return NS_OK;
michael@0 842 }
michael@0 843
michael@0 844 NS_IMETHODIMP
michael@0 845 nsGeolocationService::NotifyError(uint16_t aErrorCode)
michael@0 846 {
michael@0 847 for (uint32_t i = 0; i < mGeolocators.Length(); i++) {
michael@0 848 mGeolocators[i]->NotifyError(aErrorCode);
michael@0 849 }
michael@0 850
michael@0 851 return NS_OK;
michael@0 852 }
michael@0 853
michael@0 854 void
michael@0 855 nsGeolocationService::SetCachedPosition(nsIDOMGeoPosition* aPosition)
michael@0 856 {
michael@0 857 mLastPosition = aPosition;
michael@0 858 }
michael@0 859
michael@0 860 nsIDOMGeoPosition*
michael@0 861 nsGeolocationService::GetCachedPosition()
michael@0 862 {
michael@0 863 return mLastPosition;
michael@0 864 }
michael@0 865
michael@0 866 nsresult
michael@0 867 nsGeolocationService::StartDevice(nsIPrincipal *aPrincipal)
michael@0 868 {
michael@0 869 if (!sGeoEnabled || sGeoInitPending) {
michael@0 870 return NS_ERROR_NOT_AVAILABLE;
michael@0 871 }
michael@0 872
michael@0 873 // we do not want to keep the geolocation devices online
michael@0 874 // indefinitely. Close them down after a reasonable period of
michael@0 875 // inactivivity
michael@0 876 SetDisconnectTimer();
michael@0 877
michael@0 878 if (XRE_GetProcessType() == GeckoProcessType_Content) {
michael@0 879 ContentChild* cpc = ContentChild::GetSingleton();
michael@0 880 cpc->SendAddGeolocationListener(IPC::Principal(aPrincipal),
michael@0 881 HighAccuracyRequested());
michael@0 882 return NS_OK;
michael@0 883 }
michael@0 884
michael@0 885 // Start them up!
michael@0 886 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
michael@0 887 if (!obs) {
michael@0 888 return NS_ERROR_FAILURE;
michael@0 889 }
michael@0 890
michael@0 891 if (!mProvider) {
michael@0 892 return NS_ERROR_FAILURE;
michael@0 893 }
michael@0 894
michael@0 895 nsresult rv;
michael@0 896
michael@0 897 if (NS_FAILED(rv = mProvider->Startup()) ||
michael@0 898 NS_FAILED(rv = mProvider->Watch(this))) {
michael@0 899
michael@0 900 NotifyError(nsIDOMGeoPositionError::POSITION_UNAVAILABLE);
michael@0 901 return rv;
michael@0 902 }
michael@0 903
michael@0 904 obs->NotifyObservers(mProvider,
michael@0 905 "geolocation-device-events",
michael@0 906 MOZ_UTF16("starting"));
michael@0 907
michael@0 908 return NS_OK;
michael@0 909 }
michael@0 910
michael@0 911 void
michael@0 912 nsGeolocationService::SetDisconnectTimer()
michael@0 913 {
michael@0 914 if (!mDisconnectTimer) {
michael@0 915 mDisconnectTimer = do_CreateInstance("@mozilla.org/timer;1");
michael@0 916 } else {
michael@0 917 mDisconnectTimer->Cancel();
michael@0 918 }
michael@0 919
michael@0 920 mDisconnectTimer->Init(this,
michael@0 921 sProviderTimeout,
michael@0 922 nsITimer::TYPE_ONE_SHOT);
michael@0 923 }
michael@0 924
michael@0 925 bool
michael@0 926 nsGeolocationService::HighAccuracyRequested()
michael@0 927 {
michael@0 928 for (uint32_t i = 0; i < mGeolocators.Length(); i++) {
michael@0 929 if (mGeolocators[i]->HighAccuracyRequested()) {
michael@0 930 return true;
michael@0 931 }
michael@0 932 }
michael@0 933 return false;
michael@0 934 }
michael@0 935
michael@0 936 void
michael@0 937 nsGeolocationService::UpdateAccuracy(bool aForceHigh)
michael@0 938 {
michael@0 939 bool highRequired = aForceHigh || HighAccuracyRequested();
michael@0 940
michael@0 941 if (XRE_GetProcessType() == GeckoProcessType_Content) {
michael@0 942 ContentChild* cpc = ContentChild::GetSingleton();
michael@0 943 cpc->SendSetGeolocationHigherAccuracy(highRequired);
michael@0 944 return;
michael@0 945 }
michael@0 946
michael@0 947 if (!mHigherAccuracy && highRequired) {
michael@0 948 mProvider->SetHighAccuracy(true);
michael@0 949 }
michael@0 950
michael@0 951 if (mHigherAccuracy && !highRequired) {
michael@0 952 mProvider->SetHighAccuracy(false);
michael@0 953 }
michael@0 954
michael@0 955 mHigherAccuracy = highRequired;
michael@0 956 }
michael@0 957
michael@0 958 void
michael@0 959 nsGeolocationService::StopDevice()
michael@0 960 {
michael@0 961 if(mDisconnectTimer) {
michael@0 962 mDisconnectTimer->Cancel();
michael@0 963 mDisconnectTimer = nullptr;
michael@0 964 }
michael@0 965
michael@0 966 if (XRE_GetProcessType() == GeckoProcessType_Content) {
michael@0 967 ContentChild* cpc = ContentChild::GetSingleton();
michael@0 968 cpc->SendRemoveGeolocationListener();
michael@0 969 return; // bail early
michael@0 970 }
michael@0 971
michael@0 972 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
michael@0 973 if (!obs) {
michael@0 974 return;
michael@0 975 }
michael@0 976
michael@0 977 if (!mProvider) {
michael@0 978 return;
michael@0 979 }
michael@0 980
michael@0 981 mHigherAccuracy = false;
michael@0 982
michael@0 983 mProvider->Shutdown();
michael@0 984 obs->NotifyObservers(mProvider,
michael@0 985 "geolocation-device-events",
michael@0 986 MOZ_UTF16("shutdown"));
michael@0 987 }
michael@0 988
michael@0 989 StaticRefPtr<nsGeolocationService> nsGeolocationService::sService;
michael@0 990
michael@0 991 already_AddRefed<nsGeolocationService>
michael@0 992 nsGeolocationService::GetGeolocationService()
michael@0 993 {
michael@0 994 nsRefPtr<nsGeolocationService> result;
michael@0 995 if (nsGeolocationService::sService) {
michael@0 996 result = nsGeolocationService::sService;
michael@0 997 return result.forget();
michael@0 998 }
michael@0 999
michael@0 1000 result = new nsGeolocationService();
michael@0 1001 if (NS_FAILED(result->Init())) {
michael@0 1002 return nullptr;
michael@0 1003 }
michael@0 1004 ClearOnShutdown(&nsGeolocationService::sService);
michael@0 1005 nsGeolocationService::sService = result;
michael@0 1006 return result.forget();
michael@0 1007 }
michael@0 1008
michael@0 1009 void
michael@0 1010 nsGeolocationService::AddLocator(Geolocation* aLocator)
michael@0 1011 {
michael@0 1012 mGeolocators.AppendElement(aLocator);
michael@0 1013 }
michael@0 1014
michael@0 1015 void
michael@0 1016 nsGeolocationService::RemoveLocator(Geolocation* aLocator)
michael@0 1017 {
michael@0 1018 mGeolocators.RemoveElement(aLocator);
michael@0 1019 }
michael@0 1020
michael@0 1021 ////////////////////////////////////////////////////
michael@0 1022 // Geolocation
michael@0 1023 ////////////////////////////////////////////////////
michael@0 1024
michael@0 1025 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Geolocation)
michael@0 1026 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
michael@0 1027 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMGeoGeolocation)
michael@0 1028 NS_INTERFACE_MAP_ENTRY(nsIDOMGeoGeolocation)
michael@0 1029 NS_INTERFACE_MAP_ENTRY(nsIGeolocationUpdate)
michael@0 1030 NS_INTERFACE_MAP_END
michael@0 1031
michael@0 1032 NS_IMPL_CYCLE_COLLECTING_ADDREF(Geolocation)
michael@0 1033 NS_IMPL_CYCLE_COLLECTING_RELEASE(Geolocation)
michael@0 1034
michael@0 1035 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_4(Geolocation,
michael@0 1036 mCachedPosition,
michael@0 1037 mPendingCallbacks,
michael@0 1038 mWatchingCallbacks,
michael@0 1039 mPendingRequests)
michael@0 1040
michael@0 1041 Geolocation::Geolocation()
michael@0 1042 : mLastWatchId(0)
michael@0 1043 {
michael@0 1044 SetIsDOMBinding();
michael@0 1045 }
michael@0 1046
michael@0 1047 Geolocation::~Geolocation()
michael@0 1048 {
michael@0 1049 if (mService) {
michael@0 1050 Shutdown();
michael@0 1051 }
michael@0 1052 }
michael@0 1053
michael@0 1054 nsresult
michael@0 1055 Geolocation::Init(nsIDOMWindow* aContentDom)
michael@0 1056 {
michael@0 1057 // Remember the window
michael@0 1058 if (aContentDom) {
michael@0 1059 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aContentDom);
michael@0 1060 if (!window) {
michael@0 1061 return NS_ERROR_FAILURE;
michael@0 1062 }
michael@0 1063
michael@0 1064 mOwner = do_GetWeakReference(window->GetCurrentInnerWindow());
michael@0 1065 if (!mOwner) {
michael@0 1066 return NS_ERROR_FAILURE;
michael@0 1067 }
michael@0 1068
michael@0 1069 // Grab the principal of the document
michael@0 1070 nsCOMPtr<nsIDocument> doc = window->GetDoc();
michael@0 1071 if (!doc) {
michael@0 1072 return NS_ERROR_FAILURE;
michael@0 1073 }
michael@0 1074
michael@0 1075 mPrincipal = doc->NodePrincipal();
michael@0 1076 }
michael@0 1077
michael@0 1078 // If no aContentDom was passed into us, we are being used
michael@0 1079 // by chrome/c++ and have no mOwner, no mPrincipal, and no need
michael@0 1080 // to prompt.
michael@0 1081 mService = nsGeolocationService::GetGeolocationService();
michael@0 1082 if (mService) {
michael@0 1083 mService->AddLocator(this);
michael@0 1084 }
michael@0 1085 return NS_OK;
michael@0 1086 }
michael@0 1087
michael@0 1088 void
michael@0 1089 Geolocation::Shutdown()
michael@0 1090 {
michael@0 1091 // Release all callbacks
michael@0 1092 mPendingCallbacks.Clear();
michael@0 1093 mWatchingCallbacks.Clear();
michael@0 1094
michael@0 1095 if (mService) {
michael@0 1096 mService->RemoveLocator(this);
michael@0 1097 mService->UpdateAccuracy();
michael@0 1098 }
michael@0 1099
michael@0 1100 mService = nullptr;
michael@0 1101 mPrincipal = nullptr;
michael@0 1102 }
michael@0 1103
michael@0 1104 nsIDOMWindow*
michael@0 1105 Geolocation::GetParentObject() const {
michael@0 1106 nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mOwner);
michael@0 1107 return window.get();
michael@0 1108 }
michael@0 1109
michael@0 1110 bool
michael@0 1111 Geolocation::HasActiveCallbacks()
michael@0 1112 {
michael@0 1113 return mPendingCallbacks.Length() || mWatchingCallbacks.Length();
michael@0 1114 }
michael@0 1115
michael@0 1116 bool
michael@0 1117 Geolocation::HighAccuracyRequested()
michael@0 1118 {
michael@0 1119 for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) {
michael@0 1120 if (mWatchingCallbacks[i]->WantsHighAccuracy()) {
michael@0 1121 return true;
michael@0 1122 }
michael@0 1123 }
michael@0 1124
michael@0 1125 for (uint32_t i = 0; i < mPendingCallbacks.Length(); i++) {
michael@0 1126 if (mPendingCallbacks[i]->WantsHighAccuracy()) {
michael@0 1127 return true;
michael@0 1128 }
michael@0 1129 }
michael@0 1130
michael@0 1131 return false;
michael@0 1132 }
michael@0 1133
michael@0 1134 void
michael@0 1135 Geolocation::RemoveRequest(nsGeolocationRequest* aRequest)
michael@0 1136 {
michael@0 1137 bool requestWasKnown =
michael@0 1138 (mPendingCallbacks.RemoveElement(aRequest) !=
michael@0 1139 mWatchingCallbacks.RemoveElement(aRequest));
michael@0 1140
michael@0 1141 unused << requestWasKnown;
michael@0 1142 }
michael@0 1143
michael@0 1144 NS_IMETHODIMP
michael@0 1145 Geolocation::Update(nsIDOMGeoPosition *aSomewhere)
michael@0 1146 {
michael@0 1147 if (!WindowOwnerStillExists()) {
michael@0 1148 Shutdown();
michael@0 1149 return NS_OK;
michael@0 1150 }
michael@0 1151
michael@0 1152 if (aSomewhere) {
michael@0 1153 nsCOMPtr<nsIDOMGeoPositionCoords> coords;
michael@0 1154 aSomewhere->GetCoords(getter_AddRefs(coords));
michael@0 1155 if (coords) {
michael@0 1156 double accuracy = -1;
michael@0 1157 coords->GetAccuracy(&accuracy);
michael@0 1158 mozilla::Telemetry::Accumulate(mozilla::Telemetry::GEOLOCATION_ACCURACY, accuracy);
michael@0 1159 }
michael@0 1160 }
michael@0 1161
michael@0 1162 for (uint32_t i = mPendingCallbacks.Length(); i > 0; i--) {
michael@0 1163 mPendingCallbacks[i-1]->Update(aSomewhere);
michael@0 1164 RemoveRequest(mPendingCallbacks[i-1]);
michael@0 1165 }
michael@0 1166
michael@0 1167 // notify everyone that is watching
michael@0 1168 for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) {
michael@0 1169 mWatchingCallbacks[i]->Update(aSomewhere);
michael@0 1170 }
michael@0 1171
michael@0 1172 return NS_OK;
michael@0 1173 }
michael@0 1174
michael@0 1175 NS_IMETHODIMP
michael@0 1176 Geolocation::LocationUpdatePending()
michael@0 1177 {
michael@0 1178 // this event is only really interesting for watch callbacks
michael@0 1179 for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) {
michael@0 1180 mWatchingCallbacks[i]->LocationUpdatePending();
michael@0 1181 }
michael@0 1182
michael@0 1183 return NS_OK;
michael@0 1184 }
michael@0 1185
michael@0 1186 NS_IMETHODIMP
michael@0 1187 Geolocation::NotifyError(uint16_t aErrorCode)
michael@0 1188 {
michael@0 1189 if (!WindowOwnerStillExists()) {
michael@0 1190 Shutdown();
michael@0 1191 return NS_OK;
michael@0 1192 }
michael@0 1193
michael@0 1194 mozilla::Telemetry::Accumulate(mozilla::Telemetry::GEOLOCATION_ERROR, true);
michael@0 1195
michael@0 1196 for (uint32_t i = mPendingCallbacks.Length(); i > 0; i--) {
michael@0 1197 mPendingCallbacks[i-1]->NotifyErrorAndShutdown(aErrorCode);
michael@0 1198 //NotifyErrorAndShutdown() removes the request from the array
michael@0 1199 }
michael@0 1200
michael@0 1201 // notify everyone that is watching
michael@0 1202 for (uint32_t i = 0; i < mWatchingCallbacks.Length(); i++) {
michael@0 1203 mWatchingCallbacks[i]->NotifyErrorAndShutdown(aErrorCode);
michael@0 1204 }
michael@0 1205
michael@0 1206 return NS_OK;
michael@0 1207 }
michael@0 1208
michael@0 1209 void
michael@0 1210 Geolocation::SetCachedPosition(Position* aPosition)
michael@0 1211 {
michael@0 1212 mCachedPosition = aPosition;
michael@0 1213 }
michael@0 1214
michael@0 1215 Position*
michael@0 1216 Geolocation::GetCachedPosition()
michael@0 1217 {
michael@0 1218 return mCachedPosition;
michael@0 1219 }
michael@0 1220
michael@0 1221 void
michael@0 1222 Geolocation::GetCurrentPosition(PositionCallback& aCallback,
michael@0 1223 PositionErrorCallback* aErrorCallback,
michael@0 1224 const PositionOptions& aOptions,
michael@0 1225 ErrorResult& aRv)
michael@0 1226 {
michael@0 1227 GeoPositionCallback successCallback(&aCallback);
michael@0 1228 GeoPositionErrorCallback errorCallback(aErrorCallback);
michael@0 1229
michael@0 1230 nsresult rv = GetCurrentPosition(successCallback, errorCallback,
michael@0 1231 CreatePositionOptionsCopy(aOptions));
michael@0 1232
michael@0 1233 if (NS_FAILED(rv)) {
michael@0 1234 aRv.Throw(rv);
michael@0 1235 }
michael@0 1236
michael@0 1237 return;
michael@0 1238 }
michael@0 1239
michael@0 1240 NS_IMETHODIMP
michael@0 1241 Geolocation::GetCurrentPosition(nsIDOMGeoPositionCallback* aCallback,
michael@0 1242 nsIDOMGeoPositionErrorCallback* aErrorCallback,
michael@0 1243 PositionOptions* aOptions)
michael@0 1244 {
michael@0 1245 NS_ENSURE_ARG_POINTER(aCallback);
michael@0 1246
michael@0 1247 GeoPositionCallback successCallback(aCallback);
michael@0 1248 GeoPositionErrorCallback errorCallback(aErrorCallback);
michael@0 1249
michael@0 1250 return GetCurrentPosition(successCallback, errorCallback, aOptions);
michael@0 1251 }
michael@0 1252
michael@0 1253 nsresult
michael@0 1254 Geolocation::GetCurrentPosition(GeoPositionCallback& callback,
michael@0 1255 GeoPositionErrorCallback& errorCallback,
michael@0 1256 PositionOptions *options)
michael@0 1257 {
michael@0 1258 if (mPendingCallbacks.Length() > MAX_GEO_REQUESTS_PER_WINDOW) {
michael@0 1259 return NS_ERROR_NOT_AVAILABLE;
michael@0 1260 }
michael@0 1261
michael@0 1262 nsRefPtr<nsGeolocationRequest> request = new nsGeolocationRequest(this,
michael@0 1263 callback,
michael@0 1264 errorCallback,
michael@0 1265 options,
michael@0 1266 false);
michael@0 1267
michael@0 1268 if (!sGeoEnabled) {
michael@0 1269 nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(false, request);
michael@0 1270 NS_DispatchToMainThread(ev);
michael@0 1271 return NS_OK;
michael@0 1272 }
michael@0 1273
michael@0 1274 if (!mOwner && !nsContentUtils::IsCallerChrome()) {
michael@0 1275 return NS_ERROR_FAILURE;
michael@0 1276 }
michael@0 1277
michael@0 1278 if (sGeoInitPending) {
michael@0 1279 mPendingRequests.AppendElement(request);
michael@0 1280 return NS_OK;
michael@0 1281 }
michael@0 1282
michael@0 1283 return GetCurrentPositionReady(request);
michael@0 1284 }
michael@0 1285
michael@0 1286 nsresult
michael@0 1287 Geolocation::GetCurrentPositionReady(nsGeolocationRequest* aRequest)
michael@0 1288 {
michael@0 1289 if (mOwner) {
michael@0 1290 if (!RegisterRequestWithPrompt(aRequest)) {
michael@0 1291 return NS_ERROR_NOT_AVAILABLE;
michael@0 1292 }
michael@0 1293
michael@0 1294 return NS_OK;
michael@0 1295 }
michael@0 1296
michael@0 1297 if (!nsContentUtils::IsCallerChrome()) {
michael@0 1298 return NS_ERROR_FAILURE;
michael@0 1299 }
michael@0 1300
michael@0 1301 nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(true, aRequest);
michael@0 1302 NS_DispatchToMainThread(ev);
michael@0 1303
michael@0 1304 return NS_OK;
michael@0 1305 }
michael@0 1306
michael@0 1307 int32_t
michael@0 1308 Geolocation::WatchPosition(PositionCallback& aCallback,
michael@0 1309 PositionErrorCallback* aErrorCallback,
michael@0 1310 const PositionOptions& aOptions,
michael@0 1311 ErrorResult& aRv)
michael@0 1312 {
michael@0 1313 int32_t ret;
michael@0 1314 GeoPositionCallback successCallback(&aCallback);
michael@0 1315 GeoPositionErrorCallback errorCallback(aErrorCallback);
michael@0 1316
michael@0 1317 nsresult rv = WatchPosition(successCallback, errorCallback,
michael@0 1318 CreatePositionOptionsCopy(aOptions), &ret);
michael@0 1319
michael@0 1320 if (NS_FAILED(rv)) {
michael@0 1321 aRv.Throw(rv);
michael@0 1322 }
michael@0 1323
michael@0 1324 return ret;
michael@0 1325 }
michael@0 1326
michael@0 1327 NS_IMETHODIMP
michael@0 1328 Geolocation::WatchPosition(nsIDOMGeoPositionCallback *aCallback,
michael@0 1329 nsIDOMGeoPositionErrorCallback *aErrorCallback,
michael@0 1330 PositionOptions *aOptions,
michael@0 1331 int32_t* aRv)
michael@0 1332 {
michael@0 1333 NS_ENSURE_ARG_POINTER(aCallback);
michael@0 1334
michael@0 1335 GeoPositionCallback successCallback(aCallback);
michael@0 1336 GeoPositionErrorCallback errorCallback(aErrorCallback);
michael@0 1337
michael@0 1338 return WatchPosition(successCallback, errorCallback, aOptions, aRv);
michael@0 1339 }
michael@0 1340
michael@0 1341 nsresult
michael@0 1342 Geolocation::WatchPosition(GeoPositionCallback& aCallback,
michael@0 1343 GeoPositionErrorCallback& aErrorCallback,
michael@0 1344 PositionOptions* aOptions,
michael@0 1345 int32_t* aRv)
michael@0 1346 {
michael@0 1347 if (mWatchingCallbacks.Length() > MAX_GEO_REQUESTS_PER_WINDOW) {
michael@0 1348 return NS_ERROR_NOT_AVAILABLE;
michael@0 1349 }
michael@0 1350
michael@0 1351 // The watch ID:
michael@0 1352 *aRv = mLastWatchId++;
michael@0 1353
michael@0 1354 nsRefPtr<nsGeolocationRequest> request = new nsGeolocationRequest(this,
michael@0 1355 aCallback,
michael@0 1356 aErrorCallback,
michael@0 1357 aOptions,
michael@0 1358 true,
michael@0 1359 *aRv);
michael@0 1360
michael@0 1361 if (!sGeoEnabled) {
michael@0 1362 nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(false, request);
michael@0 1363 NS_DispatchToMainThread(ev);
michael@0 1364 return NS_OK;
michael@0 1365 }
michael@0 1366
michael@0 1367 if (!mOwner && !nsContentUtils::IsCallerChrome()) {
michael@0 1368 return NS_ERROR_FAILURE;
michael@0 1369 }
michael@0 1370
michael@0 1371 if (sGeoInitPending) {
michael@0 1372 mPendingRequests.AppendElement(request);
michael@0 1373 return NS_OK;
michael@0 1374 }
michael@0 1375
michael@0 1376 return WatchPositionReady(request);
michael@0 1377 }
michael@0 1378
michael@0 1379 nsresult
michael@0 1380 Geolocation::WatchPositionReady(nsGeolocationRequest* aRequest)
michael@0 1381 {
michael@0 1382 if (mOwner) {
michael@0 1383 if (!RegisterRequestWithPrompt(aRequest))
michael@0 1384 return NS_ERROR_NOT_AVAILABLE;
michael@0 1385
michael@0 1386 return NS_OK;
michael@0 1387 }
michael@0 1388
michael@0 1389 if (!nsContentUtils::IsCallerChrome()) {
michael@0 1390 return NS_ERROR_FAILURE;
michael@0 1391 }
michael@0 1392
michael@0 1393 aRequest->Allow(JS::UndefinedHandleValue);
michael@0 1394
michael@0 1395 return NS_OK;
michael@0 1396 }
michael@0 1397
michael@0 1398 NS_IMETHODIMP
michael@0 1399 Geolocation::ClearWatch(int32_t aWatchId)
michael@0 1400 {
michael@0 1401 if (aWatchId < 0) {
michael@0 1402 return NS_OK;
michael@0 1403 }
michael@0 1404
michael@0 1405 for (uint32_t i = 0, length = mWatchingCallbacks.Length(); i < length; ++i) {
michael@0 1406 if (mWatchingCallbacks[i]->WatchId() == aWatchId) {
michael@0 1407 mWatchingCallbacks[i]->Shutdown();
michael@0 1408 RemoveRequest(mWatchingCallbacks[i]);
michael@0 1409 break;
michael@0 1410 }
michael@0 1411 }
michael@0 1412
michael@0 1413 // make sure we also search through the pending requests lists for
michael@0 1414 // watches to clear...
michael@0 1415 for (uint32_t i = 0, length = mPendingRequests.Length(); i < length; ++i) {
michael@0 1416 if (mPendingRequests[i]->IsWatch() &&
michael@0 1417 (mPendingRequests[i]->WatchId() == aWatchId)) {
michael@0 1418 mPendingRequests[i]->Shutdown();
michael@0 1419 mPendingRequests.RemoveElementAt(i);
michael@0 1420 break;
michael@0 1421 }
michael@0 1422 }
michael@0 1423
michael@0 1424 return NS_OK;
michael@0 1425 }
michael@0 1426
michael@0 1427 void
michael@0 1428 Geolocation::ServiceReady()
michael@0 1429 {
michael@0 1430 for (uint32_t length = mPendingRequests.Length(); length > 0; --length) {
michael@0 1431 if (mPendingRequests[0]->IsWatch()) {
michael@0 1432 WatchPositionReady(mPendingRequests[0]);
michael@0 1433 } else {
michael@0 1434 GetCurrentPositionReady(mPendingRequests[0]);
michael@0 1435 }
michael@0 1436
michael@0 1437 mPendingRequests.RemoveElementAt(0);
michael@0 1438 }
michael@0 1439 }
michael@0 1440
michael@0 1441 bool
michael@0 1442 Geolocation::WindowOwnerStillExists()
michael@0 1443 {
michael@0 1444 // an owner was never set when Geolocation
michael@0 1445 // was created, which means that this object
michael@0 1446 // is being used without a window.
michael@0 1447 if (mOwner == nullptr) {
michael@0 1448 return true;
michael@0 1449 }
michael@0 1450
michael@0 1451 nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mOwner);
michael@0 1452
michael@0 1453 if (window) {
michael@0 1454 bool closed = false;
michael@0 1455 window->GetClosed(&closed);
michael@0 1456 if (closed) {
michael@0 1457 return false;
michael@0 1458 }
michael@0 1459
michael@0 1460 nsPIDOMWindow* outer = window->GetOuterWindow();
michael@0 1461 if (!outer || outer->GetCurrentInnerWindow() != window) {
michael@0 1462 return false;
michael@0 1463 }
michael@0 1464 }
michael@0 1465
michael@0 1466 return true;
michael@0 1467 }
michael@0 1468
michael@0 1469 void
michael@0 1470 Geolocation::NotifyAllowedRequest(nsGeolocationRequest* aRequest)
michael@0 1471 {
michael@0 1472 if (aRequest->IsWatch()) {
michael@0 1473 mWatchingCallbacks.AppendElement(aRequest);
michael@0 1474 } else {
michael@0 1475 mPendingCallbacks.AppendElement(aRequest);
michael@0 1476 }
michael@0 1477 }
michael@0 1478
michael@0 1479 bool
michael@0 1480 Geolocation::RegisterRequestWithPrompt(nsGeolocationRequest* request)
michael@0 1481 {
michael@0 1482 if (Preferences::GetBool("geo.prompt.testing", false)) {
michael@0 1483 bool allow = Preferences::GetBool("geo.prompt.testing.allow", false);
michael@0 1484 nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(allow,
michael@0 1485 request);
michael@0 1486 NS_DispatchToMainThread(ev);
michael@0 1487 return true;
michael@0 1488 }
michael@0 1489
michael@0 1490 if (XRE_GetProcessType() == GeckoProcessType_Content) {
michael@0 1491 nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mOwner);
michael@0 1492 if (!window) {
michael@0 1493 return true;
michael@0 1494 }
michael@0 1495
michael@0 1496 // because owner implements nsITabChild, we can assume that it is
michael@0 1497 // the one and only TabChild.
michael@0 1498 TabChild* child = TabChild::GetFrom(window->GetDocShell());
michael@0 1499 if (!child) {
michael@0 1500 return false;
michael@0 1501 }
michael@0 1502
michael@0 1503 nsTArray<PermissionRequest> permArray;
michael@0 1504 nsTArray<nsString> emptyOptions;
michael@0 1505 permArray.AppendElement(PermissionRequest(NS_LITERAL_CSTRING("geolocation"),
michael@0 1506 NS_LITERAL_CSTRING("unused"),
michael@0 1507 emptyOptions));
michael@0 1508
michael@0 1509 // Retain a reference so the object isn't deleted without IPDL's knowledge.
michael@0 1510 // Corresponding release occurs in DeallocPContentPermissionRequest.
michael@0 1511 request->AddRef();
michael@0 1512 child->SendPContentPermissionRequestConstructor(request,
michael@0 1513 permArray,
michael@0 1514 IPC::Principal(mPrincipal));
michael@0 1515
michael@0 1516 request->Sendprompt();
michael@0 1517 return true;
michael@0 1518 }
michael@0 1519
michael@0 1520 nsCOMPtr<nsIRunnable> ev = new RequestPromptEvent(request);
michael@0 1521 NS_DispatchToMainThread(ev);
michael@0 1522 return true;
michael@0 1523 }
michael@0 1524
michael@0 1525 JSObject*
michael@0 1526 Geolocation::WrapObject(JSContext *aCtx)
michael@0 1527 {
michael@0 1528 return GeolocationBinding::Wrap(aCtx, this);
michael@0 1529 }

mercurial