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)
90 ScriptWrappable::init(this);
93 Geolocation::~Geolocation()
95 ASSERT(m_geolocationPermission != PermissionRequested);
98 void Geolocation::trace(Visitor* visitor)
100 visitor->trace(m_oneShots);
101 visitor->trace(m_watchers);
102 visitor->trace(m_pendingForPermissionNotifiers);
103 visitor->trace(m_lastPosition);
104 visitor->trace(m_requestsAwaitingCachedPosition);
107 Document* Geolocation::document() const
109 return toDocument(executionContext());
112 LocalFrame* Geolocation::frame() const
114 return document() ? document()->frame() : 0;
117 void Geolocation::stop()
119 LocalFrame* frame = this->frame();
120 if (frame && m_geolocationPermission == PermissionRequested)
121 GeolocationController::from(frame)->cancelPermissionRequest(this);
123 // The frame may be moving to a new page and we want to get the permissions from the new page's client.
124 m_geolocationPermission = PermissionUnknown;
127 m_pendingForPermissionNotifiers.clear();
130 Geoposition* Geolocation::lastPosition()
132 LocalFrame* frame = this->frame();
136 m_lastPosition = createGeoposition(GeolocationController::from(frame)->lastPosition());
138 return m_lastPosition.get();
141 void Geolocation::getCurrentPosition(PassOwnPtr<PositionCallback> successCallback, PassOwnPtr<PositionErrorCallback> errorCallback, const Dictionary& options)
146 GeoNotifier* notifier = GeoNotifier::create(this, successCallback, errorCallback, PositionOptions::create(options));
147 startRequest(notifier);
149 m_oneShots.add(notifier);
152 int Geolocation::watchPosition(PassOwnPtr<PositionCallback> successCallback, PassOwnPtr<PositionErrorCallback> errorCallback, const Dictionary& options)
157 GeoNotifier* notifier = GeoNotifier::create(this, successCallback, errorCallback, PositionOptions::create(options));
158 startRequest(notifier);
161 // Keep asking for the next id until we're given one that we don't already have.
163 watchID = executionContext()->circularSequentialID();
164 } while (!m_watchers.add(watchID, notifier));
168 void Geolocation::startRequest(GeoNotifier *notifier)
170 // Check whether permissions have already been denied. Note that if this is the case,
171 // the permission state can not change again in the lifetime of this page.
173 notifier->setFatalError(PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage));
174 else if (haveSuitableCachedPosition(notifier->options()))
175 notifier->setUseCachedPosition();
176 else if (!notifier->options()->timeout())
177 notifier->startTimer();
178 else if (!isAllowed()) {
179 // if we don't yet have permission, request for permission before calling startUpdating()
180 m_pendingForPermissionNotifiers.add(notifier);
182 } else if (startUpdating(notifier))
183 notifier->startTimer();
185 notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, failedToStartServiceErrorMessage));
188 void Geolocation::fatalErrorOccurred(GeoNotifier* notifier)
190 // This request has failed fatally. Remove it from our lists.
191 m_oneShots.remove(notifier);
192 m_watchers.remove(notifier);
198 void Geolocation::requestUsesCachedPosition(GeoNotifier* notifier)
200 // This is called asynchronously, so the permissions could have been denied
201 // since we last checked in startRequest.
203 notifier->setFatalError(PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage));
207 m_requestsAwaitingCachedPosition.add(notifier);
209 // If permissions are allowed, make the callback
211 makeCachedPositionCallbacks();
215 // Request permissions, which may be synchronous or asynchronous.
219 void Geolocation::makeCachedPositionCallbacks()
221 // All modifications to m_requestsAwaitingCachedPosition are done
222 // asynchronously, so we don't need to worry about it being modified from
224 GeoNotifierSet::const_iterator end = m_requestsAwaitingCachedPosition.end();
225 for (GeoNotifierSet::const_iterator iter = m_requestsAwaitingCachedPosition.begin(); iter != end; ++iter) {
226 GeoNotifier* notifier = iter->get();
227 notifier->runSuccessCallback(lastPosition());
229 // If this is a one-shot request, stop it. Otherwise, if the watch still
230 // exists, start the service to get updates.
231 if (m_oneShots.contains(notifier))
232 m_oneShots.remove(notifier);
233 else if (m_watchers.contains(notifier)) {
234 if (!notifier->options()->timeout() || startUpdating(notifier))
235 notifier->startTimer();
237 notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, failedToStartServiceErrorMessage));
241 m_requestsAwaitingCachedPosition.clear();
247 void Geolocation::requestTimedOut(GeoNotifier* notifier)
249 // If this is a one-shot request, stop it.
250 m_oneShots.remove(notifier);
256 bool Geolocation::haveSuitableCachedPosition(PositionOptions* options)
258 Geoposition* cachedPosition = lastPosition();
261 if (!options->maximumAge())
263 DOMTimeStamp currentTimeMillis = convertSecondsToDOMTimeStamp(currentTime());
264 return cachedPosition->timestamp() > currentTimeMillis - options->maximumAge();
267 void Geolocation::clearWatch(int watchID)
272 if (GeoNotifier* notifier = m_watchers.find(watchID))
273 m_pendingForPermissionNotifiers.remove(notifier);
274 m_watchers.remove(watchID);
280 void Geolocation::setIsAllowed(bool allowed)
282 // This may be due to either a new position from the service, or a cached position.
283 m_geolocationPermission = allowed ? PermissionAllowed : PermissionDenied;
285 // Permission request was made during the startRequest process
286 if (!m_pendingForPermissionNotifiers.isEmpty()) {
287 handlePendingPermissionNotifiers();
288 m_pendingForPermissionNotifiers.clear();
293 PositionError* error = PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage);
294 error->setIsFatal(true);
296 m_requestsAwaitingCachedPosition.clear();
300 // If the service has a last position, use it to call back for all requests.
301 // If any of the requests are waiting for permission for a cached position,
302 // the position from the service will be at least as fresh.
304 makeSuccessCallbacks();
306 makeCachedPositionCallbacks();
309 void Geolocation::sendError(GeoNotifierVector& notifiers, PositionError* error)
311 GeoNotifierVector::const_iterator end = notifiers.end();
312 for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it)
313 (*it)->runErrorCallback(error);
316 void Geolocation::sendPosition(GeoNotifierVector& notifiers, Geoposition* position)
318 GeoNotifierVector::const_iterator end = notifiers.end();
319 for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it)
320 (*it)->runSuccessCallback(position);
323 void Geolocation::stopTimer(GeoNotifierVector& notifiers)
325 GeoNotifierVector::const_iterator end = notifiers.end();
326 for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it)
330 void Geolocation::stopTimersForOneShots()
332 GeoNotifierVector copy;
333 copyToVector(m_oneShots, copy);
338 void Geolocation::stopTimersForWatchers()
340 GeoNotifierVector copy;
341 m_watchers.getNotifiersVector(copy);
346 void Geolocation::stopTimers()
348 stopTimersForOneShots();
349 stopTimersForWatchers();
352 void Geolocation::cancelRequests(GeoNotifierVector& notifiers)
354 GeoNotifierVector::const_iterator end = notifiers.end();
355 for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it)
356 (*it)->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, framelessDocumentErrorMessage));
359 void Geolocation::cancelAllRequests()
361 GeoNotifierVector copy;
362 copyToVector(m_oneShots, copy);
363 cancelRequests(copy);
364 m_watchers.getNotifiersVector(copy);
365 cancelRequests(copy);
368 void Geolocation::extractNotifiersWithCachedPosition(GeoNotifierVector& notifiers, GeoNotifierVector* cached)
370 GeoNotifierVector nonCached;
371 GeoNotifierVector::iterator end = notifiers.end();
372 for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it) {
373 GeoNotifier* notifier = it->get();
374 if (notifier->useCachedPosition()) {
376 cached->append(notifier);
378 nonCached.append(notifier);
380 notifiers.swap(nonCached);
383 void Geolocation::copyToSet(const GeoNotifierVector& src, GeoNotifierSet& dest)
385 GeoNotifierVector::const_iterator end = src.end();
386 for (GeoNotifierVector::const_iterator it = src.begin(); it != end; ++it) {
387 GeoNotifier* notifier = it->get();
392 void Geolocation::handleError(PositionError* error)
396 GeoNotifierVector oneShotsCopy;
397 copyToVector(m_oneShots, oneShotsCopy);
399 GeoNotifierVector watchersCopy;
400 m_watchers.getNotifiersVector(watchersCopy);
402 // Clear the lists before we make the callbacks, to avoid clearing notifiers
403 // added by calls to Geolocation methods from the callbacks, and to prevent
404 // further callbacks to these notifiers.
405 GeoNotifierVector oneShotsWithCachedPosition;
407 if (error->isFatal())
410 // Don't send non-fatal errors to notifiers due to receive a cached position.
411 extractNotifiersWithCachedPosition(oneShotsCopy, &oneShotsWithCachedPosition);
412 extractNotifiersWithCachedPosition(watchersCopy, 0);
415 sendError(oneShotsCopy, error);
416 sendError(watchersCopy, error);
418 // hasListeners() doesn't distinguish between notifiers due to receive a
419 // cached position and those requiring a fresh position. Perform the check
420 // before restoring the notifiers below.
424 // Maintain a reference to the cached notifiers until their timer fires.
425 copyToSet(oneShotsWithCachedPosition, m_oneShots);
428 void Geolocation::requestPermission()
430 if (m_geolocationPermission != PermissionUnknown)
433 LocalFrame* frame = this->frame();
437 m_geolocationPermission = PermissionRequested;
439 // Ask the embedder: it maintains the geolocation challenge policy itself.
440 GeolocationController::from(frame)->requestPermission(this);
443 void Geolocation::makeSuccessCallbacks()
445 ASSERT(lastPosition());
448 GeoNotifierVector oneShotsCopy;
449 copyToVector(m_oneShots, oneShotsCopy);
451 GeoNotifierVector watchersCopy;
452 m_watchers.getNotifiersVector(watchersCopy);
454 // Clear the lists before we make the callbacks, to avoid clearing notifiers
455 // added by calls to Geolocation methods from the callbacks, and to prevent
456 // further callbacks to these notifiers.
459 // Also clear the set of notifiers waiting for a cached position. All the
460 // oneshots and watchers will receive a position now, and if they happen to
461 // be lingering in that set, avoid this bug: http://crbug.com/311876 .
462 m_requestsAwaitingCachedPosition.clear();
464 sendPosition(oneShotsCopy, lastPosition());
465 sendPosition(watchersCopy, lastPosition());
471 void Geolocation::positionChanged()
475 // Stop all currently running timers.
478 makeSuccessCallbacks();
481 void Geolocation::setError(GeolocationError* error)
483 handleError(createPositionError(error));
486 bool Geolocation::startUpdating(GeoNotifier* notifier)
488 LocalFrame* frame = this->frame();
492 GeolocationController::from(frame)->addObserver(this, notifier->options()->enableHighAccuracy());
496 void Geolocation::stopUpdating()
498 LocalFrame* frame = this->frame();
502 GeolocationController::from(frame)->removeObserver(this);
505 void Geolocation::handlePendingPermissionNotifiers()
507 // While we iterate through the list, we need not worry about list being modified as the permission
508 // is already set to Yes/No and no new listeners will be added to the pending list
509 GeoNotifierSet::const_iterator end = m_pendingForPermissionNotifiers.end();
510 for (GeoNotifierSet::const_iterator iter = m_pendingForPermissionNotifiers.begin(); iter != end; ++iter) {
511 GeoNotifier* notifier = iter->get();
514 // start all pending notification requests as permission granted.
515 // The notifier is always ref'ed by m_oneShots or m_watchers.
516 if (startUpdating(notifier))
517 notifier->startTimer();
519 notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, failedToStartServiceErrorMessage));
521 notifier->setFatalError(PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage));