2 * Copyright (C) 2008, 2009, 2010, 2011 Apple Inc. All Rights Reserved.
3 * Copyright (C) 2009 Torch Mobile, Inc.
4 * Copyright 2010, The Android Open Source Project
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include "modules/geolocation/Geolocation.h"
31 #include "core/dom/Document.h"
32 #include "modules/geolocation/Coordinates.h"
33 #include "modules/geolocation/GeolocationController.h"
34 #include "modules/geolocation/GeolocationError.h"
35 #include "modules/geolocation/GeolocationPosition.h"
36 #include "wtf/CurrentTime.h"
40 static const char permissionDeniedErrorMessage[] = "User denied Geolocation";
41 static const char failedToStartServiceErrorMessage[] = "Failed to start Geolocation service";
42 static const char framelessDocumentErrorMessage[] = "Geolocation cannot be used in frameless documents";
44 static Geoposition* createGeoposition(GeolocationPosition* position)
49 Coordinates* coordinates = Coordinates::create(
51 position->longitude(),
52 position->canProvideAltitude(),
55 position->canProvideAltitudeAccuracy(),
56 position->altitudeAccuracy(),
57 position->canProvideHeading(),
59 position->canProvideSpeed(),
61 return Geoposition::create(coordinates, convertSecondsToDOMTimeStamp(position->timestamp()));
64 static PositionError* createPositionError(GeolocationError* error)
66 PositionError::ErrorCode code = PositionError::POSITION_UNAVAILABLE;
67 switch (error->code()) {
68 case GeolocationError::PermissionDenied:
69 code = PositionError::PERMISSION_DENIED;
71 case GeolocationError::PositionUnavailable:
72 code = PositionError::POSITION_UNAVAILABLE;
76 return PositionError::create(code, error->message());
79 Geolocation* Geolocation::create(ExecutionContext* context)
81 Geolocation* geolocation = new Geolocation(context);
82 geolocation->suspendIfNeeded();
86 Geolocation::Geolocation(ExecutionContext* context)
87 : ActiveDOMObject(context)
88 , m_geolocationPermission(PermissionUnknown)
92 Geolocation::~Geolocation()
94 ASSERT(m_geolocationPermission != PermissionRequested);
97 void Geolocation::trace(Visitor* visitor)
99 visitor->trace(m_oneShots);
100 visitor->trace(m_watchers);
101 visitor->trace(m_pendingForPermissionNotifiers);
102 visitor->trace(m_lastPosition);
103 visitor->trace(m_requestsAwaitingCachedPosition);
106 Document* Geolocation::document() const
108 return toDocument(executionContext());
111 LocalFrame* Geolocation::frame() const
113 return document() ? document()->frame() : 0;
116 void Geolocation::stop()
118 LocalFrame* frame = this->frame();
119 if (frame && m_geolocationPermission == PermissionRequested)
120 GeolocationController::from(frame)->cancelPermissionRequest(this);
122 // The frame may be moving to a new page and we want to get the permissions from the new page's client.
123 m_geolocationPermission = PermissionUnknown;
126 m_pendingForPermissionNotifiers.clear();
129 Geoposition* Geolocation::lastPosition()
131 LocalFrame* frame = this->frame();
135 m_lastPosition = createGeoposition(GeolocationController::from(frame)->lastPosition());
137 return m_lastPosition.get();
140 void Geolocation::getCurrentPosition(PositionCallback* successCallback, PositionErrorCallback* errorCallback, const Dictionary& options)
145 GeoNotifier* notifier = GeoNotifier::create(this, successCallback, errorCallback, PositionOptions::create(options));
146 startRequest(notifier);
148 m_oneShots.add(notifier);
151 int Geolocation::watchPosition(PositionCallback* successCallback, PositionErrorCallback* errorCallback, const Dictionary& options)
156 GeoNotifier* notifier = GeoNotifier::create(this, successCallback, errorCallback, PositionOptions::create(options));
157 startRequest(notifier);
160 // Keep asking for the next id until we're given one that we don't already have.
162 watchID = executionContext()->circularSequentialID();
163 } while (!m_watchers.add(watchID, notifier));
167 void Geolocation::startRequest(GeoNotifier *notifier)
169 // Check whether permissions have already been denied. Note that if this is the case,
170 // the permission state can not change again in the lifetime of this page.
172 notifier->setFatalError(PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage));
173 else if (haveSuitableCachedPosition(notifier->options()))
174 notifier->setUseCachedPosition();
175 else if (!notifier->options()->timeout())
176 notifier->startTimer();
177 else if (!isAllowed()) {
178 // if we don't yet have permission, request for permission before calling startUpdating()
179 m_pendingForPermissionNotifiers.add(notifier);
181 } else if (startUpdating(notifier))
182 notifier->startTimer();
184 notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, failedToStartServiceErrorMessage));
187 void Geolocation::fatalErrorOccurred(GeoNotifier* notifier)
189 // This request has failed fatally. Remove it from our lists.
190 m_oneShots.remove(notifier);
191 m_watchers.remove(notifier);
197 void Geolocation::requestUsesCachedPosition(GeoNotifier* notifier)
199 // This is called asynchronously, so the permissions could have been denied
200 // since we last checked in startRequest.
202 notifier->setFatalError(PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage));
206 m_requestsAwaitingCachedPosition.add(notifier);
208 // If permissions are allowed, make the callback
210 makeCachedPositionCallbacks();
214 // Request permissions, which may be synchronous or asynchronous.
218 void Geolocation::makeCachedPositionCallbacks()
220 // All modifications to m_requestsAwaitingCachedPosition are done
221 // asynchronously, so we don't need to worry about it being modified from
223 GeoNotifierSet::const_iterator end = m_requestsAwaitingCachedPosition.end();
224 for (GeoNotifierSet::const_iterator iter = m_requestsAwaitingCachedPosition.begin(); iter != end; ++iter) {
225 GeoNotifier* notifier = iter->get();
226 notifier->runSuccessCallback(lastPosition());
228 // If this is a one-shot request, stop it. Otherwise, if the watch still
229 // exists, start the service to get updates.
230 if (m_oneShots.contains(notifier))
231 m_oneShots.remove(notifier);
232 else if (m_watchers.contains(notifier)) {
233 if (!notifier->options()->timeout() || startUpdating(notifier))
234 notifier->startTimer();
236 notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, failedToStartServiceErrorMessage));
240 m_requestsAwaitingCachedPosition.clear();
246 void Geolocation::requestTimedOut(GeoNotifier* notifier)
248 // If this is a one-shot request, stop it.
249 m_oneShots.remove(notifier);
255 bool Geolocation::haveSuitableCachedPosition(PositionOptions* options)
257 Geoposition* cachedPosition = lastPosition();
260 if (!options->maximumAge())
262 DOMTimeStamp currentTimeMillis = convertSecondsToDOMTimeStamp(currentTime());
263 return cachedPosition->timestamp() > currentTimeMillis - options->maximumAge();
266 void Geolocation::clearWatch(int watchID)
271 if (GeoNotifier* notifier = m_watchers.find(watchID))
272 m_pendingForPermissionNotifiers.remove(notifier);
273 m_watchers.remove(watchID);
279 void Geolocation::setIsAllowed(bool allowed)
281 // This may be due to either a new position from the service, or a cached position.
282 m_geolocationPermission = allowed ? PermissionAllowed : PermissionDenied;
284 // Permission request was made during the startRequest process
285 if (!m_pendingForPermissionNotifiers.isEmpty()) {
286 handlePendingPermissionNotifiers();
287 m_pendingForPermissionNotifiers.clear();
292 PositionError* error = PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage);
293 error->setIsFatal(true);
295 m_requestsAwaitingCachedPosition.clear();
299 // If the service has a last position, use it to call back for all requests.
300 // If any of the requests are waiting for permission for a cached position,
301 // the position from the service will be at least as fresh.
303 makeSuccessCallbacks();
305 makeCachedPositionCallbacks();
308 void Geolocation::sendError(GeoNotifierVector& notifiers, PositionError* error)
310 GeoNotifierVector::const_iterator end = notifiers.end();
311 for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it)
312 (*it)->runErrorCallback(error);
315 void Geolocation::sendPosition(GeoNotifierVector& notifiers, Geoposition* position)
317 GeoNotifierVector::const_iterator end = notifiers.end();
318 for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it)
319 (*it)->runSuccessCallback(position);
322 void Geolocation::stopTimer(GeoNotifierVector& notifiers)
324 GeoNotifierVector::const_iterator end = notifiers.end();
325 for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it)
329 void Geolocation::stopTimersForOneShots()
331 GeoNotifierVector copy;
332 copyToVector(m_oneShots, copy);
337 void Geolocation::stopTimersForWatchers()
339 GeoNotifierVector copy;
340 m_watchers.getNotifiersVector(copy);
345 void Geolocation::stopTimers()
347 stopTimersForOneShots();
348 stopTimersForWatchers();
351 void Geolocation::cancelRequests(GeoNotifierVector& notifiers)
353 GeoNotifierVector::const_iterator end = notifiers.end();
354 for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it)
355 (*it)->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, framelessDocumentErrorMessage));
358 void Geolocation::cancelAllRequests()
360 GeoNotifierVector copy;
361 copyToVector(m_oneShots, copy);
362 cancelRequests(copy);
363 m_watchers.getNotifiersVector(copy);
364 cancelRequests(copy);
367 void Geolocation::extractNotifiersWithCachedPosition(GeoNotifierVector& notifiers, GeoNotifierVector* cached)
369 GeoNotifierVector nonCached;
370 GeoNotifierVector::iterator end = notifiers.end();
371 for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it) {
372 GeoNotifier* notifier = it->get();
373 if (notifier->useCachedPosition()) {
375 cached->append(notifier);
377 nonCached.append(notifier);
379 notifiers.swap(nonCached);
382 void Geolocation::copyToSet(const GeoNotifierVector& src, GeoNotifierSet& dest)
384 GeoNotifierVector::const_iterator end = src.end();
385 for (GeoNotifierVector::const_iterator it = src.begin(); it != end; ++it) {
386 GeoNotifier* notifier = it->get();
391 void Geolocation::handleError(PositionError* error)
395 GeoNotifierVector oneShotsCopy;
396 copyToVector(m_oneShots, oneShotsCopy);
398 GeoNotifierVector watchersCopy;
399 m_watchers.getNotifiersVector(watchersCopy);
401 // Clear the lists before we make the callbacks, to avoid clearing notifiers
402 // added by calls to Geolocation methods from the callbacks, and to prevent
403 // further callbacks to these notifiers.
404 GeoNotifierVector oneShotsWithCachedPosition;
406 if (error->isFatal())
409 // Don't send non-fatal errors to notifiers due to receive a cached position.
410 extractNotifiersWithCachedPosition(oneShotsCopy, &oneShotsWithCachedPosition);
411 extractNotifiersWithCachedPosition(watchersCopy, 0);
414 sendError(oneShotsCopy, error);
415 sendError(watchersCopy, error);
417 // hasListeners() doesn't distinguish between notifiers due to receive a
418 // cached position and those requiring a fresh position. Perform the check
419 // before restoring the notifiers below.
423 // Maintain a reference to the cached notifiers until their timer fires.
424 copyToSet(oneShotsWithCachedPosition, m_oneShots);
427 void Geolocation::requestPermission()
429 if (m_geolocationPermission != PermissionUnknown)
432 LocalFrame* frame = this->frame();
436 m_geolocationPermission = PermissionRequested;
438 // Ask the embedder: it maintains the geolocation challenge policy itself.
439 GeolocationController::from(frame)->requestPermission(this);
442 void Geolocation::makeSuccessCallbacks()
444 ASSERT(lastPosition());
447 GeoNotifierVector oneShotsCopy;
448 copyToVector(m_oneShots, oneShotsCopy);
450 GeoNotifierVector watchersCopy;
451 m_watchers.getNotifiersVector(watchersCopy);
453 // Clear the lists before we make the callbacks, to avoid clearing notifiers
454 // added by calls to Geolocation methods from the callbacks, and to prevent
455 // further callbacks to these notifiers.
458 // Also clear the set of notifiers waiting for a cached position. All the
459 // oneshots and watchers will receive a position now, and if they happen to
460 // be lingering in that set, avoid this bug: http://crbug.com/311876 .
461 m_requestsAwaitingCachedPosition.clear();
463 sendPosition(oneShotsCopy, lastPosition());
464 sendPosition(watchersCopy, lastPosition());
470 void Geolocation::positionChanged()
474 // Stop all currently running timers.
477 makeSuccessCallbacks();
480 void Geolocation::setError(GeolocationError* error)
482 handleError(createPositionError(error));
485 bool Geolocation::startUpdating(GeoNotifier* notifier)
487 LocalFrame* frame = this->frame();
491 GeolocationController::from(frame)->addObserver(this, notifier->options()->enableHighAccuracy());
495 void Geolocation::stopUpdating()
497 LocalFrame* frame = this->frame();
501 GeolocationController::from(frame)->removeObserver(this);
504 void Geolocation::handlePendingPermissionNotifiers()
506 // While we iterate through the list, we need not worry about list being modified as the permission
507 // is already set to Yes/No and no new listeners will be added to the pending list
508 GeoNotifierSet::const_iterator end = m_pendingForPermissionNotifiers.end();
509 for (GeoNotifierSet::const_iterator iter = m_pendingForPermissionNotifiers.begin(); iter != end; ++iter) {
510 GeoNotifier* notifier = iter->get();
513 // start all pending notification requests as permission granted.
514 // The notifier is always ref'ed by m_oneShots or m_watchers.
515 if (startUpdating(notifier))
516 notifier->startTimer();
518 notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, failedToStartServiceErrorMessage));
520 notifier->setFatalError(PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage));