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 +}