[WTF] Move currentCPUTime and sleep(Seconds) to CPUTime.h and Seconds.h respectively
[WebKit-https.git] / Source / WebCore / Modules / geolocation / Geolocation.cpp
index c7a65c7..dcbf703 100644 (file)
 
 #if ENABLE(GEOLOCATION)
 
+#include "Coordinates.h"
 #include "Document.h"
 #include "Frame.h"
-#include "Geoposition.h"
-#include "Page.h"
-#include <wtf/CurrentTime.h>
-
-#include "Coordinates.h"
+#include "GeoNotifier.h"
 #include "GeolocationController.h"
 #include "GeolocationError.h"
 #include "GeolocationPosition.h"
+#include "Geoposition.h"
+#include "Page.h"
 #include "PositionError.h"
+#include "RuntimeApplicationChecks.h"
+#include "SecurityOrigin.h"
+#include <wtf/Ref.h>
+#include <wtf/text/StringBuilder.h>
 
 namespace WebCore {
 
 static const char permissionDeniedErrorMessage[] = "User denied Geolocation";
 static const char failedToStartServiceErrorMessage[] = "Failed to start Geolocation service";
 static const char framelessDocumentErrorMessage[] = "Geolocation cannot be used in frameless documents";
+static const char originCannotRequestGeolocationErrorMessage[] = "Origin does not have permission to use Geolocation service";
 
-static PassRefPtr<Geoposition> createGeoposition(GeolocationPosition* position)
+static RefPtr<Geoposition> createGeoposition(std::optional<GeolocationPosition>&& position)
 {
     if (!position)
-        return 0;
+        return nullptr;
     
-    RefPtr<Coordinates> coordinates = Coordinates::create(position->latitude(), position->longitude(), position->canProvideAltitude(), position->altitude(), 
-                                                          position->accuracy(), position->canProvideAltitudeAccuracy(), position->altitudeAccuracy(),
-                                                          position->canProvideHeading(), position->heading(), position->canProvideSpeed(), position->speed());
-    return Geoposition::create(coordinates.release(), convertSecondsToDOMTimeStamp(position->timestamp()));
+    DOMTimeStamp timestamp = convertSecondsToDOMTimeStamp(position->timestamp);
+    return Geoposition::create(Coordinates::create(WTFMove(position.value())), timestamp);
 }
 
-static PassRefPtr<PositionError> createPositionError(GeolocationError* error)
+static Ref<PositionError> createPositionError(GeolocationError& error)
 {
     PositionError::ErrorCode code = PositionError::POSITION_UNAVAILABLE;
-    switch (error->code()) {
+    switch (error.code()) {
     case GeolocationError::PermissionDenied:
         code = PositionError::PERMISSION_DENIED;
         break;
@@ -71,145 +73,36 @@ static PassRefPtr<PositionError> createPositionError(GeolocationError* error)
         break;
     }
 
-    return PositionError::create(code, error->message());
-}
-
-Geolocation::GeoNotifier::GeoNotifier(Geolocation* geolocation, PassRefPtr<PositionCallback> successCallback, PassRefPtr<PositionErrorCallback> errorCallback, PassRefPtr<PositionOptions> options)
-    : m_geolocation(geolocation)
-    , m_successCallback(successCallback)
-    , m_errorCallback(errorCallback)
-    , m_options(options)
-    , m_timer(this, &Geolocation::GeoNotifier::timerFired)
-    , m_useCachedPosition(false)
-{
-    ASSERT(m_geolocation);
-    ASSERT(m_successCallback);
-    // If no options were supplied from JS, we should have created a default set
-    // of options in JSGeolocationCustom.cpp.
-    ASSERT(m_options);
-}
-
-void Geolocation::GeoNotifier::setFatalError(PassRefPtr<PositionError> error)
-{
-    // If a fatal error has already been set, stick with it. This makes sure that
-    // when permission is denied, this is the error reported, as required by the
-    // spec.
-    if (m_fatalError)
-        return;
-
-    m_fatalError = error;
-    // An existing timer may not have a zero timeout.
-    m_timer.stop();
-    m_timer.startOneShot(0);
-}
-
-void Geolocation::GeoNotifier::setUseCachedPosition()
-{
-    m_useCachedPosition = true;
-    m_timer.startOneShot(0);
-}
-
-bool Geolocation::GeoNotifier::hasZeroTimeout() const
-{
-    return m_options->hasTimeout() && m_options->timeout() == 0;
-}
-
-void Geolocation::GeoNotifier::runSuccessCallback(Geoposition* position)
-{
-    // If we are here and the Geolocation permission is not approved, something has
-    // gone horribly wrong.
-    if (!m_geolocation->isAllowed())
-        CRASH();
-
-    m_successCallback->handleEvent(position);
-}
-
-void Geolocation::GeoNotifier::runErrorCallback(PositionError* error)
-{
-    if (m_errorCallback)
-        m_errorCallback->handleEvent(error);
-}
-
-void Geolocation::GeoNotifier::startTimerIfNeeded()
-{
-    if (m_options->hasTimeout())
-        m_timer.startOneShot(m_options->timeout() / 1000.0);
-}
-
-void Geolocation::GeoNotifier::stopTimer()
-{
-    m_timer.stop();
-}
-
-void Geolocation::GeoNotifier::timerFired(Timer<GeoNotifier>*)
-{
-    m_timer.stop();
-
-    // Protect this GeoNotifier object, since it
-    // could be deleted by a call to clearWatch in a callback.
-    RefPtr<GeoNotifier> protect(this);
-
-    // Test for fatal error first. This is required for the case where the Frame is
-    // disconnected and requests are cancelled.
-    if (m_fatalError) {
-        runErrorCallback(m_fatalError.get());
-        // This will cause this notifier to be deleted.
-        m_geolocation->fatalErrorOccurred(this);
-        return;
-    }
-
-    if (m_useCachedPosition) {
-        // Clear the cached position flag in case this is a watch request, which
-        // will continue to run.
-        m_useCachedPosition = false;
-        m_geolocation->requestUsesCachedPosition(this);
-        return;
-    }
-
-    if (m_errorCallback) {
-        RefPtr<PositionError> error = PositionError::create(PositionError::TIMEOUT, ASCIILiteral("Timeout expired"));
-        m_errorCallback->handleEvent(error.get());
-    }
-    m_geolocation->requestTimedOut(this);
+    return PositionError::create(code, error.message());
 }
 
-bool Geolocation::Watchers::add(int id, PassRefPtr<GeoNotifier> prpNotifier)
+bool Geolocation::Watchers::add(int id, RefPtr<GeoNotifier>&& notifier)
 {
     ASSERT(id > 0);
-    RefPtr<GeoNotifier> notifier = prpNotifier;
 
     if (!m_idToNotifierMap.add(id, notifier.get()).isNewEntry)
         return false;
-    m_notifierToIdMap.set(notifier.release(), id);
+    m_notifierToIdMap.set(WTFMove(notifier), id);
     return true;
 }
 
-Geolocation::GeoNotifier* Geolocation::Watchers::find(int id)
+GeoNotifier* Geolocation::Watchers::find(int id)
 {
     ASSERT(id > 0);
-    IdToNotifierMap::iterator iter = m_idToNotifierMap.find(id);
-    if (iter == m_idToNotifierMap.end())
-        return 0;
-    return iter->value.get();
+    return m_idToNotifierMap.get(id);
 }
 
 void Geolocation::Watchers::remove(int id)
 {
     ASSERT(id > 0);
-    IdToNotifierMap::iterator iter = m_idToNotifierMap.find(id);
-    if (iter == m_idToNotifierMap.end())
-        return;
-    m_notifierToIdMap.remove(iter->value);
-    m_idToNotifierMap.remove(iter);
+    if (auto notifier = m_idToNotifierMap.take(id))
+        m_notifierToIdMap.remove(notifier);
 }
 
 void Geolocation::Watchers::remove(GeoNotifier* notifier)
 {
-    NotifierToIdMap::iterator iter = m_notifierToIdMap.find(notifier);
-    if (iter == m_notifierToIdMap.end())
-        return;
-    m_idToNotifierMap.remove(iter->value);
-    m_notifierToIdMap.remove(iter);
+    if (auto identifier = m_notifierToIdMap.take(notifier))
+        m_idToNotifierMap.remove(identifier);
 }
 
 bool Geolocation::Watchers::contains(GeoNotifier* notifier) const
@@ -230,19 +123,22 @@ bool Geolocation::Watchers::isEmpty() const
 
 void Geolocation::Watchers::getNotifiersVector(GeoNotifierVector& copy) const
 {
-    copyValuesToVector(m_idToNotifierMap, copy);
+    copy = copyToVector(m_idToNotifierMap.values());
 }
 
-PassRefPtr<Geolocation> Geolocation::create(ScriptExecutionContext* context)
+Ref<Geolocation> Geolocation::create(ScriptExecutionContext* context)
 {
-    RefPtr<Geolocation> geolocation = adoptRef(new Geolocation(context));
-    geolocation->suspendIfNeeded();
-    return geolocation.release();
+    auto geolocation = adoptRef(*new Geolocation(context));
+    geolocation.get().suspendIfNeeded();
+    return geolocation;
 }
 
 Geolocation::Geolocation(ScriptExecutionContext* context)
     : ActiveDOMObject(context)
     , m_allowGeolocation(Unknown)
+    , m_isSuspended(false)
+    , m_hasChangedPosition(false)
+    , m_resumeTimer(*this, &Geolocation::resumeTimerFired)
 {
 }
 
@@ -251,73 +147,235 @@ Geolocation::~Geolocation()
     ASSERT(m_allowGeolocation != InProgress);
 }
 
-Document* Geolocation::document() const
+SecurityOrigin* Geolocation::securityOrigin() const
 {
-    return toDocument(scriptExecutionContext());
+    return scriptExecutionContext()->securityOrigin();
 }
 
-Frame* Geolocation::frame() const
+Page* Geolocation::page() const
 {
-    return document() ? document()->frame() : 0;
+    return document() ? document()->page() : nullptr;
 }
 
-Page* Geolocation::page() const
+bool Geolocation::canSuspendForDocumentSuspension() const
+{
+    return true;
+}
+    
+void Geolocation::suspend(ReasonForSuspension reason)
+{
+    if (reason == ActiveDOMObject::PageCache) {
+        stop();
+        m_resetOnResume = true;
+    }
+
+    // Suspend GeoNotifier timeout timers.
+    if (hasListeners())
+        stopTimers();
+
+    m_isSuspended = true;
+    m_resumeTimer.stop();
+    ActiveDOMObject::suspend(reason);
+}
+
+void Geolocation::resume()
 {
-    return document() ? document()->page() : 0;
+#if USE(WEB_THREAD)
+    ASSERT(WebThreadIsLockedOrDisabled());
+#endif
+    ActiveDOMObject::resume();
+
+    if (!m_resumeTimer.isActive())
+        m_resumeTimer.startOneShot(0_s);
+}
+
+void Geolocation::resumeTimerFired()
+{
+    m_isSuspended = false;
+
+    if (m_resetOnResume) {
+        resetAllGeolocationPermission();
+        m_resetOnResume = false;
+    }
+
+    // Resume GeoNotifier timeout timers.
+    if (hasListeners()) {
+        for (auto& notifier : m_oneShots)
+            notifier->startTimerIfNeeded();
+        GeoNotifierVector watcherCopy;
+        m_watchers.getNotifiersVector(watcherCopy);
+        for (auto& watcher : watcherCopy)
+            watcher->startTimerIfNeeded();
+    }
+
+    if ((isAllowed() || isDenied()) && !m_pendingForPermissionNotifiers.isEmpty()) {
+        // The pending permission was granted while the object was suspended.
+        setIsAllowed(isAllowed());
+        ASSERT(!m_hasChangedPosition);
+        ASSERT(!m_errorWaitingForResume);
+        return;
+    }
+
+    if (isDenied() && hasListeners()) {
+        // The permission was revoked while the object was suspended.
+        setIsAllowed(false);
+        return;
+    }
+
+    if (m_hasChangedPosition) {
+        positionChanged();
+        m_hasChangedPosition = false;
+    }
+
+    if (m_errorWaitingForResume) {
+        handleError(*m_errorWaitingForResume);
+        m_errorWaitingForResume = nullptr;
+    }
+}
+
+void Geolocation::resetAllGeolocationPermission()
+{
+    if (m_isSuspended) {
+        m_resetOnResume = true;
+        return;
+    }
+
+    if (m_allowGeolocation == InProgress) {
+        Page* page = this->page();
+        if (page)
+            GeolocationController::from(page)->cancelPermissionRequest(*this);
+
+        // This return is not technically correct as GeolocationController::cancelPermissionRequest() should have cleared the active request.
+        // Neither iOS nor OS X supports cancelPermissionRequest() (https://bugs.webkit.org/show_bug.cgi?id=89524), so we workaround that and let ongoing requests complete. :(
+        return;
+    }
+
+    // 1) Reset our own state.
+    stopUpdating();
+    m_allowGeolocation = Unknown;
+    m_hasChangedPosition = false;
+    m_errorWaitingForResume = nullptr;
+
+    // 2) Request new permission for the active notifiers.
+    stopTimers();
+
+    // Go over the one shot and re-request permission.
+    for (auto& notifier : m_oneShots)
+        startRequest(notifier.get());
+    // Go over the watchers and re-request permission.
+    GeoNotifierVector watcherCopy;
+    m_watchers.getNotifiersVector(watcherCopy);
+    for (auto& watcher : watcherCopy)
+        startRequest(watcher.get());
 }
 
 void Geolocation::stop()
 {
     Page* page = this->page();
     if (page && m_allowGeolocation == InProgress)
-        GeolocationController::from(page)->cancelPermissionRequest(this);
+        GeolocationController::from(page)->cancelPermissionRequest(*this);
     // The frame may be moving to a new page and we want to get the permissions from the new page's client.
     m_allowGeolocation = Unknown;
     cancelAllRequests();
     stopUpdating();
+    m_hasChangedPosition = false;
+    m_errorWaitingForResume = nullptr;
     m_pendingForPermissionNotifiers.clear();
 }
 
+const char* Geolocation::activeDOMObjectName() const
+{
+    return "Geolocation";
+}
+
 Geoposition* Geolocation::lastPosition()
 {
     Page* page = this->page();
     if (!page)
-        return 0;
+        return nullptr;
 
     m_lastPosition = createGeoposition(GeolocationController::from(page)->lastPosition());
 
     return m_lastPosition.get();
 }
 
-void Geolocation::getCurrentPosition(PassRefPtr<PositionCallback> successCallback, PassRefPtr<PositionErrorCallback> errorCallback, PassRefPtr<PositionOptions> options)
+void Geolocation::getCurrentPosition(Ref<PositionCallback>&& successCallback, RefPtr<PositionErrorCallback>&& errorCallback, PositionOptions&& options)
 {
     if (!frame())
         return;
 
-    RefPtr<GeoNotifier> notifier = GeoNotifier::create(this, successCallback, errorCallback, options);
+    RefPtr<GeoNotifier> notifier = GeoNotifier::create(*this, WTFMove(successCallback), WTFMove(errorCallback), WTFMove(options));
     startRequest(notifier.get());
 
     m_oneShots.add(notifier);
 }
 
-int Geolocation::watchPosition(PassRefPtr<PositionCallback> successCallback, PassRefPtr<PositionErrorCallback> errorCallback, PassRefPtr<PositionOptions> options)
+int Geolocation::watchPosition(Ref<PositionCallback>&& successCallback, RefPtr<PositionErrorCallback>&& errorCallback, PositionOptions&& options)
 {
     if (!frame())
         return 0;
 
-    RefPtr<GeoNotifier> notifier = GeoNotifier::create(this, successCallback, errorCallback, options);
+    RefPtr<GeoNotifier> notifier = GeoNotifier::create(*this, WTFMove(successCallback), WTFMove(errorCallback), WTFMove(options));
     startRequest(notifier.get());
 
     int watchID;
     // Keep asking for the next id until we're given one that we don't already have.
     do {
         watchID = m_scriptExecutionContext->circularSequentialID();
-    } while (!m_watchers.add(watchID, notifier));
+    } while (!m_watchers.add(watchID, WTFMove(notifier)));
     return watchID;
 }
 
-void Geolocation::startRequest(GeoNotifier *notifier)
+static void logError(const String& target, const bool isSecure, const bool isMixedContent, Document* document)
 {
+    StringBuilder message;
+    message.append("[blocked] Access to geolocation was blocked over");
+    
+    if (!isSecure)
+        message.append(" insecure connection to ");
+    else if (isMixedContent)
+        message.append(" secure connection with mixed content to ");
+    else
+        return;
+    
+    message.append(target);
+    message.append(".\n");
+    document->addConsoleMessage(MessageSource::Security, MessageLevel::Error, message.toString());
+}
+
+// FIXME: remove this function when rdar://problem/32137821 is fixed.
+static bool isRequestFromIBooks()
+{
+#if PLATFORM(MAC)
+    return MacApplication::isIBooks();
+#elif PLATFORM(IOS)
+    return IOSApplication::isIBooks();
+#endif
+    return false;
+}
+    
+bool Geolocation::shouldBlockGeolocationRequests()
+{
+    bool isSecure = SecurityOrigin::isSecure(document()->url());
+    bool hasMixedContent = !document()->foundMixedContent().isEmpty();
+    bool isLocalOrigin = securityOrigin()->isLocal();
+    if (securityOrigin()->canRequestGeolocation()) {
+        if (isLocalOrigin || (isSecure && !hasMixedContent) || isRequestFromIBooks())
+            return false;
+    }
+    
+    logError(securityOrigin()->toString(), isSecure, hasMixedContent, document());
+    return true;
+}
+
+void Geolocation::startRequest(GeoNotifier* notifier)
+{
+    if (shouldBlockGeolocationRequests()) {
+        notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, ASCIILiteral(originCannotRequestGeolocationErrorMessage)));
+        return;
+    }
+    document()->setGeolocationAccessed();
+
     // Check whether permissions have already been denied. Note that if this is the case,
     // the permission state can not change again in the lifetime of this page.
     if (isDenied())
@@ -336,7 +394,7 @@ void Geolocation::startRequest(GeoNotifier *notifier)
         notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, ASCIILiteral(failedToStartServiceErrorMessage)));
 }
 
-void Geolocation::fatalErrorOccurred(Geolocation::GeoNotifier* notifier)
+void Geolocation::fatalErrorOccurred(GeoNotifier* notifier)
 {
     // This request has failed fatally. Remove it from our lists.
     m_oneShots.remove(notifier);
@@ -372,17 +430,15 @@ void Geolocation::makeCachedPositionCallbacks()
     // All modifications to m_requestsAwaitingCachedPosition are done
     // asynchronously, so we don't need to worry about it being modified from
     // the callbacks.
-    GeoNotifierSet::const_iterator end = m_requestsAwaitingCachedPosition.end();
-    for (GeoNotifierSet::const_iterator iter = m_requestsAwaitingCachedPosition.begin(); iter != end; ++iter) {
-        GeoNotifier* notifier = iter->get();
+    for (auto& notifier : m_requestsAwaitingCachedPosition) {
+        // FIXME: This seems wrong, since makeCachedPositionCallbacks() is called in a branch where
+        // lastPosition() is known to be null in Geolocation::setIsAllowed().
         notifier->runSuccessCallback(lastPosition());
 
         // If this is a one-shot request, stop it. Otherwise, if the watch still
         // exists, start the service to get updates.
-        if (m_oneShots.contains(notifier))
-            m_oneShots.remove(notifier);
-        else if (m_watchers.contains(notifier)) {
-            if (notifier->hasZeroTimeout() || startUpdating(notifier))
+        if (!m_oneShots.remove(notifier.get()) && m_watchers.contains(notifier.get())) {
+            if (notifier->hasZeroTimeout() || startUpdating(notifier.get()))
                 notifier->startTimerIfNeeded();
             else
                 notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, ASCIILiteral(failedToStartServiceErrorMessage)));
@@ -404,17 +460,15 @@ void Geolocation::requestTimedOut(GeoNotifier* notifier)
         stopUpdating();
 }
 
-bool Geolocation::haveSuitableCachedPosition(PositionOptions* options)
+bool Geolocation::haveSuitableCachedPosition(const PositionOptions& options)
 {
     Geoposition* cachedPosition = lastPosition();
     if (!cachedPosition)
         return false;
-    if (!options->hasMaximumAge())
-        return true;
-    if (!options->maximumAge())
+    if (!options.maximumAge)
         return false;
-    DOMTimeStamp currentTimeMillis = convertSecondsToDOMTimeStamp(currentTime());
-    return cachedPosition->timestamp() > currentTimeMillis - options->maximumAge();
+    DOMTimeStamp currentTimeMillis = convertSecondsToDOMTimeStamp(WallTime::now().secondsSinceEpoch());
+    return cachedPosition->timestamp() > currentTimeMillis - options.maximumAge;
 }
 
 void Geolocation::clearWatch(int watchID)
@@ -433,12 +487,15 @@ void Geolocation::clearWatch(int watchID)
 void Geolocation::setIsAllowed(bool allowed)
 {
     // Protect the Geolocation object from garbage collection during a callback.
-    RefPtr<Geolocation> protect(this);
+    Ref<Geolocation> protectedThis(*this);
 
     // This may be due to either a new position from the service, or a cached
     // position.
     m_allowGeolocation = allowed ? Yes : No;
     
+    if (m_isSuspended)
+        return;
+
     // Permission request was made during the startRequest process
     if (!m_pendingForPermissionNotifiers.isEmpty()) {
         handlePendingPermissionNotifiers();
@@ -447,51 +504,45 @@ void Geolocation::setIsAllowed(bool allowed)
     }
 
     if (!isAllowed()) {
-        RefPtr<PositionError> error = PositionError::create(PositionError::PERMISSION_DENIED,  ASCIILiteral(permissionDeniedErrorMessage));
+        auto error = PositionError::create(PositionError::PERMISSION_DENIED,  ASCIILiteral(permissionDeniedErrorMessage));
         error->setIsFatal(true);
-        handleError(error.get());
+        handleError(error);
         m_requestsAwaitingCachedPosition.clear();
+        m_hasChangedPosition = false;
+        m_errorWaitingForResume = nullptr;
         return;
     }
 
     // If the service has a last position, use it to call back for all requests.
     // If any of the requests are waiting for permission for a cached position,
     // the position from the service will be at least as fresh.
-    if (lastPosition())
-        makeSuccessCallbacks();
+    if (RefPtr<Geoposition> position = lastPosition())
+        makeSuccessCallbacks(*position);
     else
         makeCachedPositionCallbacks();
 }
 
-void Geolocation::sendError(GeoNotifierVector& notifiers, PositionError* error)
+void Geolocation::sendError(GeoNotifierVector& notifiers, PositionError& error)
 {
-     GeoNotifierVector::const_iterator end = notifiers.end();
-     for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it) {
-         RefPtr<GeoNotifier> notifier = *it;
-         
-         notifier->runErrorCallback(error);
-     }
+    for (auto& notifier : notifiers)
+        notifier->runErrorCallback(error);
 }
 
-void Geolocation::sendPosition(GeoNotifierVector& notifiers, Geoposition* position)
+void Geolocation::sendPosition(GeoNotifierVector& notifiers, Geoposition& position)
 {
-    GeoNotifierVector::const_iterator end = notifiers.end();
-    for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it)
-        (*it)->runSuccessCallback(position);
+    for (auto& notifier : notifiers)
+        notifier->runSuccessCallback(&position);
 }
 
 void Geolocation::stopTimer(GeoNotifierVector& notifiers)
 {
-    GeoNotifierVector::const_iterator end = notifiers.end();
-    for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it)
-        (*it)->stopTimer();
+    for (auto& notifier : notifiers)
+        notifier->stopTimer();
 }
 
 void Geolocation::stopTimersForOneShots()
 {
-    GeoNotifierVector copy;
-    copyToVector(m_oneShots, copy);
-    
+    auto copy = copyToVector(m_oneShots);
     stopTimer(copy);
 }
 
@@ -511,15 +562,13 @@ void Geolocation::stopTimers()
 
 void Geolocation::cancelRequests(GeoNotifierVector& notifiers)
 {
-    GeoNotifierVector::const_iterator end = notifiers.end();
-    for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it)
-        (*it)->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, ASCIILiteral(framelessDocumentErrorMessage)));
+    for (auto& notifier : notifiers)
+        notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, ASCIILiteral(framelessDocumentErrorMessage)));
 }
 
 void Geolocation::cancelAllRequests()
 {
-    GeoNotifierVector copy;
-    copyToVector(m_oneShots, copy);
+    auto copy = copyToVector(m_oneShots);
     cancelRequests(copy);
     m_watchers.getNotifiersVector(copy);
     cancelRequests(copy);
@@ -528,33 +577,25 @@ void Geolocation::cancelAllRequests()
 void Geolocation::extractNotifiersWithCachedPosition(GeoNotifierVector& notifiers, GeoNotifierVector* cached)
 {
     GeoNotifierVector nonCached;
-    GeoNotifierVector::iterator end = notifiers.end();
-    for (GeoNotifierVector::const_iterator it = notifiers.begin(); it != end; ++it) {
-        GeoNotifier* notifier = it->get();
+    for (auto& notifier : notifiers) {
         if (notifier->useCachedPosition()) {
             if (cached)
-                cached->append(notifier);
+                cached->append(notifier.get());
         } else
-            nonCached.append(notifier);
+            nonCached.append(notifier.get());
     }
     notifiers.swap(nonCached);
 }
 
 void Geolocation::copyToSet(const GeoNotifierVector& src, GeoNotifierSet& dest)
 {
-     GeoNotifierVector::const_iterator end = src.end();
-     for (GeoNotifierVector::const_iterator it = src.begin(); it != end; ++it) {
-         GeoNotifier* notifier = it->get();
-         dest.add(notifier);
-     }
+    for (auto& notifier : src)
+        dest.add(notifier.get());
 }
 
-void Geolocation::handleError(PositionError* error)
+void Geolocation::handleError(PositionError& error)
 {
-    ASSERT(error);
-    
-    GeoNotifierVector oneShotsCopy;
-    copyToVector(m_oneShots, oneShotsCopy);
+    auto oneShotsCopy = copyToVector(m_oneShots);
 
     GeoNotifierVector watchersCopy;
     m_watchers.getNotifiersVector(watchersCopy);
@@ -564,7 +605,7 @@ void Geolocation::handleError(PositionError* error)
     // further callbacks to these notifiers.
     GeoNotifierVector oneShotsWithCachedPosition;
     m_oneShots.clear();
-    if (error->isFatal())
+    if (error.isFatal())
         m_watchers.clear();
     else {
         // Don't send non-fatal errors to notifiers due to receive a cached position.
@@ -597,16 +638,15 @@ void Geolocation::requestPermission()
     m_allowGeolocation = InProgress;
 
     // Ask the embedder: it maintains the geolocation challenge policy itself.
-    GeolocationController::from(page)->requestPermission(this);
+    GeolocationController::from(page)->requestPermission(*this);
 }
 
-void Geolocation::makeSuccessCallbacks()
+void Geolocation::makeSuccessCallbacks(Geoposition& position)
 {
     ASSERT(lastPosition());
     ASSERT(isAllowed());
     
-    GeoNotifierVector oneShotsCopy;
-    copyToVector(m_oneShots, oneShotsCopy);
+    auto oneShotsCopy = copyToVector(m_oneShots);
     
     GeoNotifierVector watchersCopy;
     m_watchers.getNotifiersVector(watchersCopy);
@@ -616,8 +656,8 @@ void Geolocation::makeSuccessCallbacks()
     // further callbacks to these notifiers.
     m_oneShots.clear();
 
-    sendPosition(oneShotsCopy, lastPosition());
-    sendPosition(watchersCopy, lastPosition());
+    sendPosition(oneShotsCopy, position);
+    sendPosition(watchersCopy, position);
 
     if (!hasListeners())
         stopUpdating();
@@ -630,13 +670,26 @@ void Geolocation::positionChanged()
     // Stop all currently running timers.
     stopTimers();
 
-    makeSuccessCallbacks();
+    if (m_isSuspended) {
+        m_hasChangedPosition = true;
+        return;
+    }
+
+    RefPtr<Geoposition> position = lastPosition();
+    ASSERT(position);
+
+    makeSuccessCallbacks(*position);
 }
 
-void Geolocation::setError(GeolocationError* error)
+void Geolocation::setError(GeolocationError& error)
 {
-    RefPtr<PositionError> positionError = createPositionError(error);
-    handleError(positionError.get());
+    if (m_isSuspended) {
+        m_errorWaitingForResume = createPositionError(error);
+        return;
+    }
+
+    auto positionError = createPositionError(error);
+    handleError(positionError);
 }
 
 bool Geolocation::startUpdating(GeoNotifier* notifier)
@@ -645,7 +698,7 @@ bool Geolocation::startUpdating(GeoNotifier* notifier)
     if (!page)
         return false;
 
-    GeolocationController::from(page)->addObserver(this, notifier->options()->enableHighAccuracy());
+    GeolocationController::from(page)->addObserver(*this, notifier->options().enableHighAccuracy);
     return true;
 }
 
@@ -655,21 +708,18 @@ void Geolocation::stopUpdating()
     if (!page)
         return;
 
-    GeolocationController::from(page)->removeObserver(this);
+    GeolocationController::from(page)->removeObserver(*this);
 }
 
 void Geolocation::handlePendingPermissionNotifiers()
 {
     // While we iterate through the list, we need not worry about list being modified as the permission 
     // is already set to Yes/No and no new listeners will be added to the pending list
-    GeoNotifierSet::const_iterator end = m_pendingForPermissionNotifiers.end();
-    for (GeoNotifierSet::const_iterator iter = m_pendingForPermissionNotifiers.begin(); iter != end; ++iter) {
-        GeoNotifier* notifier = iter->get();
-
+    for (auto& notifier : m_pendingForPermissionNotifiers) {
         if (isAllowed()) {
             // start all pending notification requests as permission granted.
             // The notifier is always ref'ed by m_oneShots or m_watchers.
-            if (startUpdating(notifier))
+            if (startUpdating(notifier.get()))
                 notifier->startTimerIfNeeded();
             else
                 notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, ASCIILiteral(failedToStartServiceErrorMessage)));