Home | History | Annotate | Download | only in geolocation
      1 /*
      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
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      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.
     14  *
     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.
     26  */
     27 
     28 #include "config.h"
     29 #include "modules/geolocation/Geolocation.h"
     30 
     31 #include "core/dom/Document.h"
     32 #include "core/page/Frame.h"
     33 #include "core/page/Page.h"
     34 #include "modules/geolocation/Geoposition.h"
     35 #include "wtf/CurrentTime.h"
     36 
     37 #include "modules/geolocation/Coordinates.h"
     38 #include "modules/geolocation/GeolocationController.h"
     39 #include "modules/geolocation/GeolocationError.h"
     40 #include "modules/geolocation/GeolocationPosition.h"
     41 #include "modules/geolocation/PositionError.h"
     42 
     43 namespace WebCore {
     44 
     45 static const char permissionDeniedErrorMessage[] = "User denied Geolocation";
     46 static const char failedToStartServiceErrorMessage[] = "Failed to start Geolocation service";
     47 static const char framelessDocumentErrorMessage[] = "Geolocation cannot be used in frameless documents";
     48 
     49 static PassRefPtr<Geoposition> createGeoposition(GeolocationPosition* position)
     50 {
     51     if (!position)
     52         return 0;
     53 
     54     RefPtr<Coordinates> coordinates = Coordinates::create(position->latitude(), position->longitude(), position->canProvideAltitude(), position->altitude(),
     55                                                           position->accuracy(), position->canProvideAltitudeAccuracy(), position->altitudeAccuracy(),
     56                                                           position->canProvideHeading(), position->heading(), position->canProvideSpeed(), position->speed());
     57     return Geoposition::create(coordinates.release(), convertSecondsToDOMTimeStamp(position->timestamp()));
     58 }
     59 
     60 static PassRefPtr<PositionError> createPositionError(GeolocationError* error)
     61 {
     62     PositionError::ErrorCode code = PositionError::POSITION_UNAVAILABLE;
     63     switch (error->code()) {
     64     case GeolocationError::PermissionDenied:
     65         code = PositionError::PERMISSION_DENIED;
     66         break;
     67     case GeolocationError::PositionUnavailable:
     68         code = PositionError::POSITION_UNAVAILABLE;
     69         break;
     70     }
     71 
     72     return PositionError::create(code, error->message());
     73 }
     74 
     75 Geolocation::GeoNotifier::GeoNotifier(Geolocation* geolocation, PassRefPtr<PositionCallback> successCallback, PassRefPtr<PositionErrorCallback> errorCallback, PassRefPtr<PositionOptions> options)
     76     : m_geolocation(geolocation)
     77     , m_successCallback(successCallback)
     78     , m_errorCallback(errorCallback)
     79     , m_options(options)
     80     , m_timer(this, &Geolocation::GeoNotifier::timerFired)
     81     , m_useCachedPosition(false)
     82 {
     83     ASSERT(m_geolocation);
     84     ASSERT(m_successCallback);
     85     // If no options were supplied from JS, we should have created a default set
     86     // of options in JSGeolocationCustom.cpp.
     87     ASSERT(m_options);
     88 }
     89 
     90 void Geolocation::GeoNotifier::setFatalError(PassRefPtr<PositionError> error)
     91 {
     92     // If a fatal error has already been set, stick with it. This makes sure that
     93     // when permission is denied, this is the error reported, as required by the
     94     // spec.
     95     if (m_fatalError)
     96         return;
     97 
     98     m_fatalError = error;
     99     // An existing timer may not have a zero timeout.
    100     m_timer.stop();
    101     m_timer.startOneShot(0);
    102 }
    103 
    104 void Geolocation::GeoNotifier::setUseCachedPosition()
    105 {
    106     m_useCachedPosition = true;
    107     m_timer.startOneShot(0);
    108 }
    109 
    110 bool Geolocation::GeoNotifier::hasZeroTimeout() const
    111 {
    112     return m_options->hasTimeout() && m_options->timeout() == 0;
    113 }
    114 
    115 void Geolocation::GeoNotifier::runSuccessCallback(Geoposition* position)
    116 {
    117     // If we are here and the Geolocation permission is not approved, something has
    118     // gone horribly wrong.
    119     if (!m_geolocation->isAllowed())
    120         CRASH();
    121 
    122     m_successCallback->handleEvent(position);
    123 }
    124 
    125 void Geolocation::GeoNotifier::runErrorCallback(PositionError* error)
    126 {
    127     if (m_errorCallback)
    128         m_errorCallback->handleEvent(error);
    129 }
    130 
    131 void Geolocation::GeoNotifier::startTimerIfNeeded()
    132 {
    133     if (m_options->hasTimeout())
    134         m_timer.startOneShot(m_options->timeout() / 1000.0);
    135 }
    136 
    137 void Geolocation::GeoNotifier::stopTimer()
    138 {
    139     m_timer.stop();
    140 }
    141 
    142 void Geolocation::GeoNotifier::timerFired(Timer<GeoNotifier>*)
    143 {
    144     m_timer.stop();
    145 
    146     // Protect this GeoNotifier object, since it
    147     // could be deleted by a call to clearWatch in a callback.
    148     RefPtr<GeoNotifier> protect(this);
    149 
    150     // Test for fatal error first. This is required for the case where the Frame is
    151     // disconnected and requests are cancelled.
    152     if (m_fatalError) {
    153         runErrorCallback(m_fatalError.get());
    154         // This will cause this notifier to be deleted.
    155         m_geolocation->fatalErrorOccurred(this);
    156         return;
    157     }
    158 
    159     if (m_useCachedPosition) {
    160         // Clear the cached position flag in case this is a watch request, which
    161         // will continue to run.
    162         m_useCachedPosition = false;
    163         m_geolocation->requestUsesCachedPosition(this);
    164         return;
    165     }
    166 
    167     if (m_errorCallback) {
    168         RefPtr<PositionError> error = PositionError::create(PositionError::TIMEOUT, "Timeout expired");
    169         m_errorCallback->handleEvent(error.get());
    170     }
    171     m_geolocation->requestTimedOut(this);
    172 }
    173 
    174 bool Geolocation::Watchers::add(int id, PassRefPtr<GeoNotifier> prpNotifier)
    175 {
    176     ASSERT(id > 0);
    177     RefPtr<GeoNotifier> notifier = prpNotifier;
    178 
    179     if (!m_idToNotifierMap.add(id, notifier.get()).isNewEntry)
    180         return false;
    181     m_notifierToIdMap.set(notifier.release(), id);
    182     return true;
    183 }
    184 
    185 Geolocation::GeoNotifier* Geolocation::Watchers::find(int id)
    186 {
    187     ASSERT(id > 0);
    188     IdToNotifierMap::iterator iter = m_idToNotifierMap.find(id);
    189     if (iter == m_idToNotifierMap.end())
    190         return 0;
    191     return iter->value.get();
    192 }
    193 
    194 void Geolocation::Watchers::remove(int id)
    195 {
    196     ASSERT(id > 0);
    197     IdToNotifierMap::iterator iter = m_idToNotifierMap.find(id);
    198     if (iter == m_idToNotifierMap.end())
    199         return;
    200     m_notifierToIdMap.remove(iter->value);
    201     m_idToNotifierMap.remove(iter);
    202 }
    203 
    204 void Geolocation::Watchers::remove(GeoNotifier* notifier)
    205 {
    206     NotifierToIdMap::iterator iter = m_notifierToIdMap.find(notifier);
    207     if (iter == m_notifierToIdMap.end())
    208         return;
    209     m_idToNotifierMap.remove(iter->value);
    210     m_notifierToIdMap.remove(iter);
    211 }
    212 
    213 bool Geolocation::Watchers::contains(GeoNotifier* notifier) const
    214 {
    215     return m_notifierToIdMap.contains(notifier);
    216 }
    217 
    218 void Geolocation::Watchers::clear()
    219 {
    220     m_idToNotifierMap.clear();
    221     m_notifierToIdMap.clear();
    222 }
    223 
    224 bool Geolocation::Watchers::isEmpty() const
    225 {
    226     return m_idToNotifierMap.isEmpty();
    227 }
    228 
    229 void Geolocation::Watchers::getNotifiersVector(GeoNotifierVector& copy) const
    230 {
    231     copyValuesToVector(m_idToNotifierMap, copy);
    232 }
    233 
    234 PassRefPtr<Geolocation> Geolocation::create(ScriptExecutionContext* context)
    235 {
    236     RefPtr<Geolocation> geolocation = adoptRef(new Geolocation(context));
    237     geolocation->suspendIfNeeded();
    238     return geolocation.release();
    239 }
    240 
    241 Geolocation::Geolocation(ScriptExecutionContext* context)
    242     : ActiveDOMObject(context)
    243     , m_allowGeolocation(Unknown)
    244 {
    245     ScriptWrappable::init(this);
    246 }
    247 
    248 Geolocation::~Geolocation()
    249 {
    250     ASSERT(m_allowGeolocation != InProgress);
    251 }
    252 
    253 Document* Geolocation::document() const
    254 {
    255     return toDocument(scriptExecutionContext());
    256 }
    257 
    258 Frame* Geolocation::frame() const
    259 {
    260     return document() ? document()->frame() : 0;
    261 }
    262 
    263 Page* Geolocation::page() const
    264 {
    265     return document() ? document()->page() : 0;
    266 }
    267 
    268 void Geolocation::stop()
    269 {
    270     Page* page = this->page();
    271     if (page && m_allowGeolocation == InProgress)
    272         GeolocationController::from(page)->cancelPermissionRequest(this);
    273     // The frame may be moving to a new page and we want to get the permissions from the new page's client.
    274     m_allowGeolocation = Unknown;
    275     cancelAllRequests();
    276     stopUpdating();
    277     m_pendingForPermissionNotifiers.clear();
    278 }
    279 
    280 Geoposition* Geolocation::lastPosition()
    281 {
    282     Page* page = this->page();
    283     if (!page)
    284         return 0;
    285 
    286     m_lastPosition = createGeoposition(GeolocationController::from(page)->lastPosition());
    287 
    288     return m_lastPosition.get();
    289 }
    290 
    291 void Geolocation::getCurrentPosition(PassRefPtr<PositionCallback> successCallback, PassRefPtr<PositionErrorCallback> errorCallback, PassRefPtr<PositionOptions> options)
    292 {
    293     if (!frame())
    294         return;
    295 
    296     RefPtr<GeoNotifier> notifier = GeoNotifier::create(this, successCallback, errorCallback, options);
    297     startRequest(notifier.get());
    298 
    299     m_oneShots.add(notifier);
    300 }
    301 
    302 int Geolocation::watchPosition(PassRefPtr<PositionCallback> successCallback, PassRefPtr<PositionErrorCallback> errorCallback, PassRefPtr<PositionOptions> options)
    303 {
    304     if (!frame())
    305         return 0;
    306 
    307     RefPtr<GeoNotifier> notifier = GeoNotifier::create(this, successCallback, errorCallback, options);
    308     startRequest(notifier.get());
    309 
    310     int watchID;
    311     // Keep asking for the next id until we're given one that we don't already have.
    312     do {
    313         watchID = scriptExecutionContext()->circularSequentialID();
    314     } while (!m_watchers.add(watchID, notifier));
    315     return watchID;
    316 }
    317 
    318 void Geolocation::startRequest(GeoNotifier *notifier)
    319 {
    320     // Check whether permissions have already been denied. Note that if this is the case,
    321     // the permission state can not change again in the lifetime of this page.
    322     if (isDenied())
    323         notifier->setFatalError(PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage));
    324     else if (haveSuitableCachedPosition(notifier->options()))
    325         notifier->setUseCachedPosition();
    326     else if (notifier->hasZeroTimeout())
    327         notifier->startTimerIfNeeded();
    328     else if (!isAllowed()) {
    329         // if we don't yet have permission, request for permission before calling startUpdating()
    330         m_pendingForPermissionNotifiers.add(notifier);
    331         requestPermission();
    332     } else if (startUpdating(notifier))
    333         notifier->startTimerIfNeeded();
    334     else
    335         notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, failedToStartServiceErrorMessage));
    336 }
    337 
    338 void Geolocation::fatalErrorOccurred(Geolocation::GeoNotifier* notifier)
    339 {
    340     // This request has failed fatally. Remove it from our lists.
    341     m_oneShots.remove(notifier);
    342     m_watchers.remove(notifier);
    343 
    344     if (!hasListeners())
    345         stopUpdating();
    346 }
    347 
    348 void Geolocation::requestUsesCachedPosition(GeoNotifier* notifier)
    349 {
    350     // This is called asynchronously, so the permissions could have been denied
    351     // since we last checked in startRequest.
    352     if (isDenied()) {
    353         notifier->setFatalError(PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage));
    354         return;
    355     }
    356 
    357     m_requestsAwaitingCachedPosition.add(notifier);
    358 
    359     // If permissions are allowed, make the callback
    360     if (isAllowed()) {
    361         makeCachedPositionCallbacks();
    362         return;
    363     }
    364 
    365     // Request permissions, which may be synchronous or asynchronous.
    366     requestPermission();
    367 }
    368 
    369 void Geolocation::makeCachedPositionCallbacks()
    370 {
    371     // All modifications to m_requestsAwaitingCachedPosition are done
    372     // asynchronously, so we don't need to worry about it being modified from
    373     // the callbacks.
    374     GeoNotifierSet::const_iterator end = m_requestsAwaitingCachedPosition.end();
    375     for (GeoNotifierSet::const_iterator iter = m_requestsAwaitingCachedPosition.begin(); iter != end; ++iter) {
    376         GeoNotifier* notifier = iter->get();
    377         notifier->runSuccessCallback(lastPosition());
    378 
    379         // If this is a one-shot request, stop it. Otherwise, if the watch still
    380         // exists, start the service to get updates.
    381         if (m_oneShots.contains(notifier))
    382             m_oneShots.remove(notifier);
    383         else if (m_watchers.contains(notifier)) {
    384             if (notifier->hasZeroTimeout() || startUpdating(notifier))
    385                 notifier->startTimerIfNeeded();
    386             else
    387                 notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, failedToStartServiceErrorMessage));
    388         }
    389     }
    390 
    391     m_requestsAwaitingCachedPosition.clear();
    392 
    393     if (!hasListeners())
    394         stopUpdating();
    395 }
    396 
    397 void Geolocation::requestTimedOut(GeoNotifier* notifier)
    398 {
    399     // If this is a one-shot request, stop it.
    400     m_oneShots.remove(notifier);
    401 
    402     if (!hasListeners())
    403         stopUpdating();
    404 }
    405 
    406 bool Geolocation::haveSuitableCachedPosition(PositionOptions* options)
    407 {
    408     Geoposition* cachedPosition = lastPosition();
    409     if (!cachedPosition)
    410         return false;
    411     if (!options->hasMaximumAge())
    412         return true;
    413     if (!options->maximumAge())
    414         return false;
    415     DOMTimeStamp currentTimeMillis = convertSecondsToDOMTimeStamp(currentTime());
    416     return cachedPosition->timestamp() > currentTimeMillis - options->maximumAge();
    417 }
    418 
    419 void Geolocation::clearWatch(int watchID)
    420 {
    421     if (watchID <= 0)
    422         return;
    423 
    424     if (GeoNotifier* notifier = m_watchers.find(watchID))
    425         m_pendingForPermissionNotifiers.remove(notifier);
    426     m_watchers.remove(watchID);
    427 
    428     if (!hasListeners())
    429         stopUpdating();
    430 }
    431 
    432 void Geolocation::setIsAllowed(bool allowed)
    433 {
    434     // Protect the Geolocation object from garbage collection during a callback.
    435     RefPtr<Geolocation> protect(this);
    436 
    437     // This may be due to either a new position from the service, or a cached
    438     // position.
    439     m_allowGeolocation = allowed ? Yes : No;
    440 
    441     // Permission request was made during the startRequest process
    442     if (!m_pendingForPermissionNotifiers.isEmpty()) {
    443         handlePendingPermissionNotifiers();
    444         m_pendingForPermissionNotifiers.clear();
    445         return;
    446     }
    447 
    448     if (!isAllowed()) {
    449         RefPtr<PositionError> error = PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage);
    450         error->setIsFatal(true);
    451         handleError(error.get());
    452         m_requestsAwaitingCachedPosition.clear();
    453         return;
    454     }
    455 
    456     // If the service has a last position, use it to call back for all requests.
    457     // If any of the requests are waiting for permission for a cached position,
    458     // the position from the service will be at least as fresh.
    459     if (lastPosition())
    460         makeSuccessCallbacks();
    461     else
    462         makeCachedPositionCallbacks();
    463 }
    464 
    465 void Geolocation::sendError(GeoNotifierVector& notifiers, PositionError* error)
    466 {
    467      GeoNotifierVector::const_iterator end = notifiers.end();
    468      for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it) {
    469          RefPtr<GeoNotifier> notifier = *it;
    470 
    471          notifier->runErrorCallback(error);
    472      }
    473 }
    474 
    475 void Geolocation::sendPosition(GeoNotifierVector& notifiers, Geoposition* position)
    476 {
    477     GeoNotifierVector::const_iterator end = notifiers.end();
    478     for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it)
    479         (*it)->runSuccessCallback(position);
    480 }
    481 
    482 void Geolocation::stopTimer(GeoNotifierVector& notifiers)
    483 {
    484     GeoNotifierVector::const_iterator end = notifiers.end();
    485     for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it)
    486         (*it)->stopTimer();
    487 }
    488 
    489 void Geolocation::stopTimersForOneShots()
    490 {
    491     GeoNotifierVector copy;
    492     copyToVector(m_oneShots, copy);
    493 
    494     stopTimer(copy);
    495 }
    496 
    497 void Geolocation::stopTimersForWatchers()
    498 {
    499     GeoNotifierVector copy;
    500     m_watchers.getNotifiersVector(copy);
    501 
    502     stopTimer(copy);
    503 }
    504 
    505 void Geolocation::stopTimers()
    506 {
    507     stopTimersForOneShots();
    508     stopTimersForWatchers();
    509 }
    510 
    511 void Geolocation::cancelRequests(GeoNotifierVector& notifiers)
    512 {
    513     GeoNotifierVector::const_iterator end = notifiers.end();
    514     for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it)
    515         (*it)->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, framelessDocumentErrorMessage));
    516 }
    517 
    518 void Geolocation::cancelAllRequests()
    519 {
    520     GeoNotifierVector copy;
    521     copyToVector(m_oneShots, copy);
    522     cancelRequests(copy);
    523     m_watchers.getNotifiersVector(copy);
    524     cancelRequests(copy);
    525 }
    526 
    527 void Geolocation::extractNotifiersWithCachedPosition(GeoNotifierVector& notifiers, GeoNotifierVector* cached)
    528 {
    529     GeoNotifierVector nonCached;
    530     GeoNotifierVector::iterator end = notifiers.end();
    531     for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it) {
    532         GeoNotifier* notifier = it->get();
    533         if (notifier->useCachedPosition()) {
    534             if (cached)
    535                 cached->append(notifier);
    536         } else
    537             nonCached.append(notifier);
    538     }
    539     notifiers.swap(nonCached);
    540 }
    541 
    542 void Geolocation::copyToSet(const GeoNotifierVector& src, GeoNotifierSet& dest)
    543 {
    544      GeoNotifierVector::const_iterator end = src.end();
    545      for (GeoNotifierVector::const_iterator it = src.begin(); it != end; ++it) {
    546          GeoNotifier* notifier = it->get();
    547          dest.add(notifier);
    548      }
    549 }
    550 
    551 void Geolocation::handleError(PositionError* error)
    552 {
    553     ASSERT(error);
    554 
    555     GeoNotifierVector oneShotsCopy;
    556     copyToVector(m_oneShots, oneShotsCopy);
    557 
    558     GeoNotifierVector watchersCopy;
    559     m_watchers.getNotifiersVector(watchersCopy);
    560 
    561     // Clear the lists before we make the callbacks, to avoid clearing notifiers
    562     // added by calls to Geolocation methods from the callbacks, and to prevent
    563     // further callbacks to these notifiers.
    564     GeoNotifierVector oneShotsWithCachedPosition;
    565     m_oneShots.clear();
    566     if (error->isFatal())
    567         m_watchers.clear();
    568     else {
    569         // Don't send non-fatal errors to notifiers due to receive a cached position.
    570         extractNotifiersWithCachedPosition(oneShotsCopy, &oneShotsWithCachedPosition);
    571         extractNotifiersWithCachedPosition(watchersCopy, 0);
    572     }
    573 
    574     sendError(oneShotsCopy, error);
    575     sendError(watchersCopy, error);
    576 
    577     // hasListeners() doesn't distinguish between notifiers due to receive a
    578     // cached position and those requiring a fresh position. Perform the check
    579     // before restoring the notifiers below.
    580     if (!hasListeners())
    581         stopUpdating();
    582 
    583     // Maintain a reference to the cached notifiers until their timer fires.
    584     copyToSet(oneShotsWithCachedPosition, m_oneShots);
    585 }
    586 
    587 void Geolocation::requestPermission()
    588 {
    589     if (m_allowGeolocation > Unknown)
    590         return;
    591 
    592     Page* page = this->page();
    593     if (!page)
    594         return;
    595 
    596     m_allowGeolocation = InProgress;
    597 
    598     // Ask the embedder: it maintains the geolocation challenge policy itself.
    599     GeolocationController::from(page)->requestPermission(this);
    600 }
    601 
    602 void Geolocation::makeSuccessCallbacks()
    603 {
    604     ASSERT(lastPosition());
    605     ASSERT(isAllowed());
    606 
    607     GeoNotifierVector oneShotsCopy;
    608     copyToVector(m_oneShots, oneShotsCopy);
    609 
    610     GeoNotifierVector watchersCopy;
    611     m_watchers.getNotifiersVector(watchersCopy);
    612 
    613     // Clear the lists before we make the callbacks, to avoid clearing notifiers
    614     // added by calls to Geolocation methods from the callbacks, and to prevent
    615     // further callbacks to these notifiers.
    616     m_oneShots.clear();
    617 
    618     sendPosition(oneShotsCopy, lastPosition());
    619     sendPosition(watchersCopy, lastPosition());
    620 
    621     if (!hasListeners())
    622         stopUpdating();
    623 }
    624 
    625 void Geolocation::positionChanged()
    626 {
    627     ASSERT(isAllowed());
    628 
    629     // Stop all currently running timers.
    630     stopTimers();
    631 
    632     makeSuccessCallbacks();
    633 }
    634 
    635 void Geolocation::setError(GeolocationError* error)
    636 {
    637     RefPtr<PositionError> positionError = createPositionError(error);
    638     handleError(positionError.get());
    639 }
    640 
    641 bool Geolocation::startUpdating(GeoNotifier* notifier)
    642 {
    643     Page* page = this->page();
    644     if (!page)
    645         return false;
    646 
    647     GeolocationController::from(page)->addObserver(this, notifier->options()->enableHighAccuracy());
    648     return true;
    649 }
    650 
    651 void Geolocation::stopUpdating()
    652 {
    653     Page* page = this->page();
    654     if (!page)
    655         return;
    656 
    657     GeolocationController::from(page)->removeObserver(this);
    658 }
    659 
    660 void Geolocation::handlePendingPermissionNotifiers()
    661 {
    662     // While we iterate through the list, we need not worry about list being modified as the permission
    663     // is already set to Yes/No and no new listeners will be added to the pending list
    664     GeoNotifierSet::const_iterator end = m_pendingForPermissionNotifiers.end();
    665     for (GeoNotifierSet::const_iterator iter = m_pendingForPermissionNotifiers.begin(); iter != end; ++iter) {
    666         GeoNotifier* notifier = iter->get();
    667 
    668         if (isAllowed()) {
    669             // start all pending notification requests as permission granted.
    670             // The notifier is always ref'ed by m_oneShots or m_watchers.
    671             if (startUpdating(notifier))
    672                 notifier->startTimerIfNeeded();
    673             else
    674                 notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, failedToStartServiceErrorMessage));
    675         } else {
    676             notifier->setFatalError(PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage));
    677         }
    678     }
    679 }
    680 
    681 } // namespace WebCore
    682