diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/Modules/geolocation/Geolocation.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/Modules/geolocation/Geolocation.cpp')
-rw-r--r-- | Source/WebCore/Modules/geolocation/Geolocation.cpp | 325 |
1 files changed, 115 insertions, 210 deletions
diff --git a/Source/WebCore/Modules/geolocation/Geolocation.cpp b/Source/WebCore/Modules/geolocation/Geolocation.cpp index 15cd1b80e..36109f35f 100644 --- a/Source/WebCore/Modules/geolocation/Geolocation.cpp +++ b/Source/WebCore/Modules/geolocation/Geolocation.cpp @@ -30,37 +30,41 @@ #if ENABLE(GEOLOCATION) +#include "Coordinates.h" #include "Document.h" #include "Frame.h" -#include "Geoposition.h" -#include "Page.h" -#include <wtf/CurrentTime.h> -#include <wtf/Ref.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 "SecurityOrigin.h" +#include <wtf/CurrentTime.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(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())); + auto 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(WTFMove(coordinates), convertSecondsToDOMTimeStamp(position->timestamp())); } -static PassRefPtr<PositionError> createPositionError(GeolocationError* error) +static Ref<PositionError> createPositionError(GeolocationError* error) { PositionError::ErrorCode code = PositionError::POSITION_UNAVAILABLE; switch (error->code()) { @@ -75,117 +79,17 @@ static PassRefPtr<PositionError> createPositionError(GeolocationError* error) 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. - Ref<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); -} - -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); return m_idToNotifierMap.get(id); @@ -225,21 +129,19 @@ void Geolocation::Watchers::getNotifiersVector(GeoNotifierVector& copy) const copyValuesToVector(m_idToNotifierMap, copy); } -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) -#if PLATFORM(IOS) , m_isSuspended(false) , m_hasChangedPosition(false) - , m_resumeTimer(this, &Geolocation::resumeTimerFired) -#endif + , m_resumeTimer(*this, &Geolocation::resumeTimerFired) { } @@ -248,33 +150,24 @@ Geolocation::~Geolocation() ASSERT(m_allowGeolocation != InProgress); } -Document* Geolocation::document() const +SecurityOrigin* Geolocation::securityOrigin() const { - return toDocument(scriptExecutionContext()); -} - -Frame* Geolocation::frame() const -{ - return document() ? document()->frame() : 0; + return scriptExecutionContext()->securityOrigin(); } Page* Geolocation::page() const { - return document() ? document()->page() : 0; + return document() ? document()->page() : nullptr; } -#if PLATFORM(IOS) -bool Geolocation::canSuspend() const +bool Geolocation::canSuspendForDocumentSuspension() const { - return !hasListeners(); + return true; } - + void Geolocation::suspend(ReasonForSuspension reason) { - // Allow pages that no longer have listeners to enter the page cache. - // Have them stop updating and reset geolocation permissions when the page is resumed. - if (reason == ActiveDOMObject::DocumentWillBecomeInactive) { - ASSERT(!hasListeners()); + if (reason == ActiveDOMObject::PageCache) { stop(); m_resetOnResume = true; } @@ -290,14 +183,16 @@ void Geolocation::suspend(ReasonForSuspension reason) void Geolocation::resume() { +#if USE(WEB_THREAD) ASSERT(WebThreadIsLockedOrDisabled()); +#endif ActiveDOMObject::resume(); if (!m_resumeTimer.isActive()) m_resumeTimer.startOneShot(0); } -void Geolocation::resumeTimerFired(Timer<Geolocation>&) +void Geolocation::resumeTimerFired() { m_isSuspended = false; @@ -308,13 +203,12 @@ void Geolocation::resumeTimerFired(Timer<Geolocation>&) // Resume GeoNotifier timeout timers. if (hasListeners()) { - GeoNotifierSet::const_iterator end = m_oneShots.end(); - for (GeoNotifierSet::const_iterator it = m_oneShots.begin(); it != end; ++it) - (*it)->startTimerIfNeeded(); + for (auto& notifier : m_oneShots) + notifier->startTimerIfNeeded(); GeoNotifierVector watcherCopy; m_watchers.getNotifiersVector(watcherCopy); - for (size_t i = 0; i < watcherCopy.size(); ++i) - watcherCopy[i]->startTimerIfNeeded(); + for (auto& watcher : watcherCopy) + watcher->startTimerIfNeeded(); } if ((isAllowed() || isDenied()) && !m_pendingForPermissionNotifiers.isEmpty()) { @@ -369,16 +263,14 @@ void Geolocation::resetAllGeolocationPermission() stopTimers(); // Go over the one shot and re-request permission. - GeoNotifierSet::iterator end = m_oneShots.end(); - for (GeoNotifierSet::iterator it = m_oneShots.begin(); it != end; ++it) - startRequest((*it).get()); + for (auto& notifier : m_oneShots) + startRequest(notifier.get()); // Go over the watchers and re-request permission. GeoNotifierVector watcherCopy; m_watchers.getNotifiersVector(watcherCopy); - for (size_t i = 0; i < watcherCopy.size(); ++i) - startRequest(watcherCopy[i].get()); + for (auto& watcher : watcherCopy) + startRequest(watcher.get()); } -#endif // PLATFORM(IOS) void Geolocation::stop() { @@ -389,13 +281,16 @@ void Geolocation::stop() m_allowGeolocation = Unknown; cancelAllRequests(); stopUpdating(); -#if PLATFORM(IOS) m_hasChangedPosition = false; m_errorWaitingForResume = nullptr; -#endif // PLATFORM(IOS) m_pendingForPermissionNotifiers.clear(); } +const char* Geolocation::activeDOMObjectName() const +{ + return "Geolocation"; +} + Geoposition* Geolocation::lastPosition() { Page* page = this->page(); @@ -407,35 +302,72 @@ Geoposition* Geolocation::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()); +} + +bool Geolocation::shouldBlockGeolocationRequests() +{ + bool isSecure = SecurityOrigin::isSecure(document()->url()); + bool hasMixedContent = document()->foundMixedContent(); + bool isLocalFile = document()->url().isLocalFile(); + if (securityOrigin()->canRequestGeolocation()) { + if (isLocalFile || (isSecure && !hasMixedContent)) + 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()) @@ -454,7 +386,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); @@ -490,15 +422,13 @@ 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) { 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.remove(notifier) && 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))); @@ -520,17 +450,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(); + return cachedPosition->timestamp() > currentTimeMillis - options.maximumAge; } void Geolocation::clearWatch(int watchID) @@ -549,16 +477,14 @@ void Geolocation::clearWatch(int watchID) void Geolocation::setIsAllowed(bool allowed) { // Protect the Geolocation object from garbage collection during a callback. - Ref<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 PLATFORM(IOS) if (m_isSuspended) return; -#endif // Permission request was made during the startRequest process if (!m_pendingForPermissionNotifiers.isEmpty()) { @@ -572,10 +498,8 @@ void Geolocation::setIsAllowed(bool allowed) error->setIsFatal(true); handleError(error.get()); m_requestsAwaitingCachedPosition.clear(); -#if PLATFORM(IOS) m_hasChangedPosition = false; m_errorWaitingForResume = nullptr; -#endif return; } @@ -591,26 +515,20 @@ void Geolocation::setIsAllowed(bool allowed) 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) { - 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() @@ -637,9 +555,8 @@ 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() @@ -654,25 +571,20 @@ 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) @@ -756,24 +668,20 @@ void Geolocation::positionChanged() // Stop all currently running timers. stopTimers(); -#if PLATFORM(IOS) if (m_isSuspended) { m_hasChangedPosition = true; return; } -#endif makeSuccessCallbacks(); } void Geolocation::setError(GeolocationError* error) { -#if PLATFORM(IOS) if (m_isSuspended) { m_errorWaitingForResume = createPositionError(error); return; } -#endif RefPtr<PositionError> positionError = createPositionError(error); handleError(positionError.get()); } @@ -784,7 +692,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; } @@ -801,14 +709,11 @@ 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))); |