Index: third_party/WebKit/Source/modules/geolocation/Geolocation.cpp |
diff --git a/third_party/WebKit/Source/modules/geolocation/Geolocation.cpp b/third_party/WebKit/Source/modules/geolocation/Geolocation.cpp |
index efdb860a03b604b053a2dcc6507b1a5393f3035a..6b510b3256682204294aa9209c4fc5d486cd91da 100644 |
--- a/third_party/WebKit/Source/modules/geolocation/Geolocation.cpp |
+++ b/third_party/WebKit/Source/modules/geolocation/Geolocation.cpp |
@@ -32,64 +32,67 @@ |
#include "core/frame/OriginsUsingFeatures.h" |
#include "core/frame/Settings.h" |
#include "modules/geolocation/Coordinates.h" |
-#include "modules/geolocation/GeolocationController.h" |
#include "modules/geolocation/GeolocationError.h" |
-#include "modules/geolocation/GeolocationPosition.h" |
-#include "platform/weborigin/SecurityOrigin.h" |
+#include "platform/mojo/MojoHelper.h" |
+#include "public/platform/ServiceRegistry.h" |
#include "wtf/CurrentTime.h" |
namespace blink { |
+namespace { |
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 Geoposition* createGeoposition(GeolocationPosition* position) |
+static Geoposition* createGeoposition(const mojom::blink::Geoposition& position) |
{ |
- if (!position) |
- return nullptr; |
- |
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, convertSecondsToDOMTimeStamp(position->timestamp())); |
-} |
- |
-static PositionError* createPositionError(GeolocationError* error) |
-{ |
- PositionError::ErrorCode code = PositionError::POSITION_UNAVAILABLE; |
- switch (error->code()) { |
- case GeolocationError::PermissionDenied: |
- code = PositionError::PERMISSION_DENIED; |
+ position.latitude, |
+ position.longitude, |
+ position.altitude > -10000., |
+ position.altitude, |
+ position.accuracy, |
+ position.altitude_accuracy >= 0., |
+ position.altitude_accuracy, |
+ position.heading >= 0. && position.heading <= 360., |
+ position.heading, |
+ position.speed >= 0., |
+ position.speed); |
+ return Geoposition::create(coordinates, convertSecondsToDOMTimeStamp(position.timestamp)); |
+} |
+ |
+static PositionError* createPositionError(mojom::blink::Geoposition::ErrorCode mojomErrorCode, const String& error) |
+{ |
+ PositionError::ErrorCode errorCode = PositionError::POSITION_UNAVAILABLE; |
+ switch (mojomErrorCode) { |
+ case mojom::blink::Geoposition::ErrorCode::PERMISSION_DENIED: |
+ errorCode = PositionError::PERMISSION_DENIED; |
+ break; |
+ case mojom::blink::Geoposition::ErrorCode::POSITION_UNAVAILABLE: |
+ errorCode = PositionError::POSITION_UNAVAILABLE; |
break; |
- case GeolocationError::PositionUnavailable: |
- code = PositionError::POSITION_UNAVAILABLE; |
+ case mojom::blink::Geoposition::ErrorCode::NONE: |
+ case mojom::blink::Geoposition::ErrorCode::TIMEOUT: |
+ NOTREACHED(); |
break; |
} |
- |
- return PositionError::create(code, error->message()); |
+ return PositionError::create(errorCode, error); |
} |
+} // namespace |
+ |
Geolocation* Geolocation::create(ExecutionContext* context) |
{ |
Geolocation* geolocation = new Geolocation(context); |
- geolocation->suspendIfNeeded(); |
return geolocation; |
} |
Geolocation::Geolocation(ExecutionContext* context) |
- : ActiveDOMObject(context) |
+ : ContextLifecycleObserver(context) |
+ , PageLifecycleObserver(document()->page()) |
, m_geolocationPermission(PermissionUnknown) |
{ |
+ ThreadState::current()->registerPreFinalizer(this); |
} |
Geolocation::~Geolocation() |
@@ -103,8 +106,8 @@ DEFINE_TRACE(Geolocation) |
visitor->trace(m_watchers); |
visitor->trace(m_pendingForPermissionNotifiers); |
visitor->trace(m_lastPosition); |
- visitor->trace(m_requestsAwaitingCachedPosition); |
- ActiveDOMObject::trace(visitor); |
+ ContextLifecycleObserver::trace(visitor); |
+ PageLifecycleObserver::trace(visitor); |
} |
Document* Geolocation::document() const |
@@ -117,28 +120,18 @@ LocalFrame* Geolocation::frame() const |
return document() ? document()->frame() : 0; |
} |
-void Geolocation::stop() |
+void Geolocation::contextDestroyed() |
{ |
- LocalFrame* frame = this->frame(); |
- if (frame && m_geolocationPermission == PermissionRequested) |
- GeolocationController::from(frame)->cancelPermissionRequest(this); |
+ if (m_permissionService) |
+ m_permissionService.reset(); |
- // The frame may be moving to a new page and we want to get the permissions from the new page's client. |
- m_geolocationPermission = PermissionUnknown; |
cancelAllRequests(); |
stopUpdating(); |
+ m_geolocationPermission = PermissionDenied; |
m_pendingForPermissionNotifiers.clear(); |
-} |
- |
-Geoposition* Geolocation::lastPosition() |
-{ |
- LocalFrame* frame = this->frame(); |
- if (!frame) |
- return 0; |
- |
- m_lastPosition = createGeoposition(GeolocationController::from(frame)->lastPosition()); |
- |
- return m_lastPosition; |
+ m_lastPosition = nullptr; |
+ ContextLifecycleObserver::clearContext(); |
+ PageLifecycleObserver::clearContext(); |
} |
void Geolocation::recordOriginTypeAccess() const |
@@ -218,10 +211,10 @@ void Geolocation::startRequest(GeoNotifier *notifier) |
// if we don't yet have permission, request for permission before calling startUpdating() |
m_pendingForPermissionNotifiers.add(notifier); |
requestPermission(); |
- } else if (startUpdating(notifier)) |
+ } else { |
+ startUpdating(notifier); |
notifier->startTimer(); |
- else |
- notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, failedToStartServiceErrorMessage)); |
+ } |
} |
void Geolocation::fatalErrorOccurred(GeoNotifier* notifier) |
@@ -236,47 +229,20 @@ void Geolocation::fatalErrorOccurred(GeoNotifier* notifier) |
void Geolocation::requestUsesCachedPosition(GeoNotifier* notifier) |
{ |
- // This is called asynchronously, so the permissions could have been denied |
- // since we last checked in startRequest. |
- if (isDenied()) { |
- notifier->setFatalError(PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage)); |
- return; |
- } |
+ DCHECK(isAllowed()); |
- m_requestsAwaitingCachedPosition.add(notifier); |
+ notifier->runSuccessCallback(m_lastPosition); |
- // If permissions are allowed, make the callback |
- if (isAllowed()) { |
- makeCachedPositionCallbacks(); |
- return; |
- } |
- |
- // Request permissions, which may be synchronous or asynchronous. |
- requestPermission(); |
-} |
- |
-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. |
- for (GeoNotifier* 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.contains(notifier)) |
- m_oneShots.remove(notifier); |
- else if (m_watchers.contains(notifier)) { |
- if (!notifier->options().timeout() || startUpdating(notifier)) |
- notifier->startTimer(); |
- else |
- notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, failedToStartServiceErrorMessage)); |
- } |
+ // 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->options().timeout()) |
+ startUpdating(notifier); |
+ notifier->startTimer(); |
} |
- m_requestsAwaitingCachedPosition.clear(); |
- |
if (!hasListeners()) |
stopUpdating(); |
} |
@@ -292,13 +258,13 @@ void Geolocation::requestTimedOut(GeoNotifier* notifier) |
bool Geolocation::haveSuitableCachedPosition(const PositionOptions& options) |
{ |
- Geoposition* cachedPosition = lastPosition(); |
- if (!cachedPosition) |
+ if (!m_lastPosition) |
return false; |
+ DCHECK(isAllowed()); |
if (!options.maximumAge()) |
return false; |
DOMTimeStamp currentTimeMillis = convertSecondsToDOMTimeStamp(currentTime()); |
- return cachedPosition->timestamp() > currentTimeMillis - options.maximumAge(); |
+ return m_lastPosition->timestamp() > currentTimeMillis - options.maximumAge(); |
} |
void Geolocation::clearWatch(int watchID) |
@@ -314,33 +280,25 @@ void Geolocation::clearWatch(int watchID) |
stopUpdating(); |
} |
-void Geolocation::setIsAllowed(bool allowed) |
+void Geolocation::onGeolocationPermissionUpdated(mojom::blink::PermissionStatus status) |
{ |
// This may be due to either a new position from the service, or a cached position. |
- m_geolocationPermission = allowed ? PermissionAllowed : PermissionDenied; |
+ m_geolocationPermission = status == mojom::blink::PermissionStatus::GRANTED ? PermissionAllowed : PermissionDenied; |
+ m_permissionService.reset(); |
- // Permission request was made during the startRequest process |
- if (!m_pendingForPermissionNotifiers.isEmpty()) { |
- handlePendingPermissionNotifiers(); |
- m_pendingForPermissionNotifiers.clear(); |
- return; |
- } |
- |
- if (!isAllowed()) { |
- PositionError* error = PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage); |
- error->setIsFatal(true); |
- handleError(error); |
- m_requestsAwaitingCachedPosition.clear(); |
- return; |
+ // While we iterate through the list, we need not worry about the list being modified as the permission |
+ // is already set to Yes/No and no new listeners will be added to the pending list. |
+ for (GeoNotifier* 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. |
+ startUpdating(notifier); |
+ notifier->startTimer(); |
+ } else { |
+ notifier->setFatalError(PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage)); |
+ } |
} |
- |
- // 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(); |
- else |
- makeCachedPositionCallbacks(); |
+ m_pendingForPermissionNotifiers.clear(); |
} |
void Geolocation::sendError(GeoNotifierVector& notifiers, PositionError* error) |
@@ -463,14 +421,20 @@ void Geolocation::requestPermission() |
return; |
m_geolocationPermission = PermissionRequested; |
+ frame->serviceRegistry()->connectToRemoteService( |
+ mojo::GetProxy(&m_permissionService)); |
+ m_permissionService.set_connection_error_handler(sameThreadBindForMojo(&Geolocation::onPermissionConnectionError, this)); |
// Ask the embedder: it maintains the geolocation challenge policy itself. |
- GeolocationController::from(frame)->requestPermission(this); |
+ m_permissionService->RequestPermission( |
+ mojom::blink::PermissionName::GEOLOCATION, |
+ getExecutionContext()->getSecurityOrigin()->toString(), |
+ sameThreadBindForMojo(&Geolocation::onGeolocationPermissionUpdated, this)); |
} |
void Geolocation::makeSuccessCallbacks() |
{ |
- ASSERT(lastPosition()); |
+ ASSERT(m_lastPosition); |
ASSERT(isAllowed()); |
GeoNotifierVector oneShotsCopy; |
@@ -484,13 +448,8 @@ void Geolocation::makeSuccessCallbacks() |
// further callbacks to these notifiers. |
m_oneShots.clear(); |
- // Also clear the set of notifiers waiting for a cached position. All the |
- // oneshots and watchers will receive a position now, and if they happen to |
- // be lingering in that set, avoid this bug: http://crbug.com/311876 . |
- m_requestsAwaitingCachedPosition.clear(); |
- |
- sendPosition(oneShotsCopy, lastPosition()); |
- sendPosition(watchersCopy, lastPosition()); |
+ sendPosition(oneShotsCopy, m_lastPosition); |
+ sendPosition(watchersCopy, m_lastPosition); |
if (!hasListeners()) |
stopUpdating(); |
@@ -506,46 +465,75 @@ void Geolocation::positionChanged() |
makeSuccessCallbacks(); |
} |
-void Geolocation::setError(GeolocationError* error) |
+void Geolocation::startUpdating(GeoNotifier* notifier) |
{ |
- handleError(createPositionError(error)); |
+ m_updating = true; |
+ if (notifier->options().enableHighAccuracy() && !m_enableHighAccuracy) { |
+ m_enableHighAccuracy = true; |
+ if (m_geolocationService) |
+ m_geolocationService->SetHighAccuracy(true); |
+ } |
+ updateGeolocationServiceConnection(); |
} |
-bool Geolocation::startUpdating(GeoNotifier* notifier) |
+void Geolocation::stopUpdating() |
{ |
- LocalFrame* frame = this->frame(); |
- if (!frame) |
- return false; |
- |
- GeolocationController::from(frame)->addObserver(this, notifier->options().enableHighAccuracy()); |
- return true; |
+ m_updating = false; |
+ updateGeolocationServiceConnection(); |
+ m_enableHighAccuracy = false; |
} |
-void Geolocation::stopUpdating() |
+void Geolocation::updateGeolocationServiceConnection() |
{ |
- LocalFrame* frame = this->frame(); |
- if (!frame) |
+ if (!getExecutionContext() || !page() || !page()->isPageVisible() || !m_updating) { |
+ m_geolocationService.reset(); |
+ m_disconnectedGeolocationService = true; |
+ return; |
+ } |
+ if (m_geolocationService) |
return; |
- GeolocationController::from(frame)->removeObserver(this); |
+ frame()->serviceRegistry()->connectToRemoteService(mojo::GetProxy(&m_geolocationService)); |
+ m_geolocationService.set_connection_error_handler(sameThreadBindForMojo(&Geolocation::onGeolocationConnectionError, this)); |
+ if (m_enableHighAccuracy) |
+ m_geolocationService->SetHighAccuracy(true); |
+ queryNextPosition(); |
} |
-void Geolocation::handlePendingPermissionNotifiers() |
+void Geolocation::queryNextPosition() |
{ |
- // 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. |
- for (GeoNotifier* 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)) |
- notifier->startTimer(); |
- else |
- notifier->setFatalError(PositionError::create(PositionError::POSITION_UNAVAILABLE, failedToStartServiceErrorMessage)); |
- } else { |
- notifier->setFatalError(PositionError::create(PositionError::PERMISSION_DENIED, permissionDeniedErrorMessage)); |
- } |
+ m_geolocationService->QueryNextPosition( |
+ sameThreadBindForMojo(&Geolocation::onPositionUpdated, this)); |
+} |
+ |
+void Geolocation::onPositionUpdated(mojom::blink::GeopositionPtr position) |
+{ |
+ m_disconnectedGeolocationService = false; |
+ if (position->valid) { |
+ m_lastPosition = createGeoposition(*position); |
+ positionChanged(); |
+ } else { |
+ handleError(createPositionError(position->error_code, position->error_message)); |
} |
+ if (!m_disconnectedGeolocationService) |
+ queryNextPosition(); |
+} |
+ |
+void Geolocation::pageVisibilityChanged() |
+{ |
+ updateGeolocationServiceConnection(); |
+} |
+ |
+void Geolocation::onGeolocationConnectionError() |
+{ |
+ PositionError* error = PositionError::create(PositionError::POSITION_UNAVAILABLE, failedToStartServiceErrorMessage); |
+ error->setIsFatal(true); |
+ handleError(error); |
+} |
+ |
+void Geolocation::onPermissionConnectionError() |
+{ |
+ onGeolocationPermissionUpdated(mojom::blink::PermissionStatus::DENIED); |
} |
} // namespace blink |