summaryrefslogtreecommitdiff
path: root/Source/WebCore/Modules/geolocation/Geolocation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/Modules/geolocation/Geolocation.cpp')
-rw-r--r--Source/WebCore/Modules/geolocation/Geolocation.cpp325
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)));