blob: 5e8209c80cfb09c569186eecb9b8d3d2b5bbbdac [file] [log] [blame]
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/geolocation/geolocation_dispatcher_host.h"
#include <map>
#include <set>
#include <utility>
#include "chrome/common/geoposition.h"
#include "chrome/browser/geolocation/geolocation_permission_context.h"
#include "chrome/browser/geolocation/geolocation_provider.h"
#include "chrome/browser/renderer_host/render_process_host.h"
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/browser/renderer_host/render_view_host_notification_task.h"
#include "chrome/browser/renderer_host/resource_message_filter.h"
#include "chrome/common/render_messages.h"
#include "ipc/ipc_message.h"
namespace {
class GeolocationDispatcherHostImpl : public GeolocationDispatcherHost,
public GeolocationObserver {
public:
GeolocationDispatcherHostImpl(
int resource_message_filter_process_id,
GeolocationPermissionContext* geolocation_permission_context);
// GeolocationDispatcherHost
// Called to possibly handle the incoming IPC message. Returns true if
// handled. Called in the browser process.
virtual bool OnMessageReceived(const IPC::Message& msg, bool* msg_was_ok);
// GeolocationArbitrator::Delegate
virtual void OnLocationUpdate(const Geoposition& position);
private:
friend class base::RefCountedThreadSafe<GeolocationDispatcherHostImpl>;
virtual ~GeolocationDispatcherHostImpl();
void OnRegisterDispatcher(int render_view_id);
void OnUnregisterDispatcher(int render_view_id);
void OnRequestPermission(
int render_view_id, int bridge_id, const GURL& requesting_frame);
void OnCancelPermissionRequest(
int render_view_id, int bridge_id, const GURL& requesting_frame);
void OnStartUpdating(
int render_view_id, int bridge_id, const GURL& requesting_frame,
bool enable_high_accuracy);
void OnStopUpdating(int render_view_id, int bridge_id);
void OnSuspend(int render_view_id, int bridge_id);
void OnResume(int render_view_id, int bridge_id);
// Updates the |location_arbitrator_| with the currently required update
// options, based on |bridge_update_options_|.
void RefreshGeolocationObserverOptions();
int resource_message_filter_process_id_;
scoped_refptr<GeolocationPermissionContext> geolocation_permission_context_;
// Iterated when sending location updates to renderer processes. The fan out
// to individual bridge IDs happens renderer side, in order to minimize
// context switches.
// Only used on the IO thread.
std::set<int> geolocation_renderer_ids_;
// Maps <renderer_id, bridge_id> to the location arbitrator update options
// that correspond to this particular bridge.
std::map<std::pair<int, int>, GeolocationObserverOptions>
bridge_update_options_;
// Only set whilst we are registered with the arbitrator.
GeolocationProvider* location_provider_;
DISALLOW_COPY_AND_ASSIGN(GeolocationDispatcherHostImpl);
};
GeolocationDispatcherHostImpl::GeolocationDispatcherHostImpl(
int resource_message_filter_process_id,
GeolocationPermissionContext* geolocation_permission_context)
: resource_message_filter_process_id_(resource_message_filter_process_id),
geolocation_permission_context_(geolocation_permission_context),
location_provider_(NULL) {
// This is initialized by ResourceMessageFilter. Do not add any non-trivial
// initialization here, defer to OnRegisterBridge which is triggered whenever
// a javascript geolocation object is actually initialized.
}
GeolocationDispatcherHostImpl::~GeolocationDispatcherHostImpl() {
if (location_provider_)
location_provider_->RemoveObserver(this);
}
bool GeolocationDispatcherHostImpl::OnMessageReceived(
const IPC::Message& msg, bool* msg_was_ok) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
*msg_was_ok = true;
bool handled = true;
IPC_BEGIN_MESSAGE_MAP_EX(GeolocationDispatcherHostImpl, msg, *msg_was_ok)
IPC_MESSAGE_HANDLER(ViewHostMsg_Geolocation_RegisterDispatcher,
OnRegisterDispatcher)
IPC_MESSAGE_HANDLER(ViewHostMsg_Geolocation_UnregisterDispatcher,
OnUnregisterDispatcher)
IPC_MESSAGE_HANDLER(ViewHostMsg_Geolocation_CancelPermissionRequest,
OnCancelPermissionRequest)
IPC_MESSAGE_HANDLER(ViewHostMsg_Geolocation_RequestPermission,
OnRequestPermission)
IPC_MESSAGE_HANDLER(ViewHostMsg_Geolocation_StartUpdating,
OnStartUpdating)
IPC_MESSAGE_HANDLER(ViewHostMsg_Geolocation_StopUpdating,
OnStopUpdating)
IPC_MESSAGE_HANDLER(ViewHostMsg_Geolocation_Suspend,
OnSuspend)
IPC_MESSAGE_HANDLER(ViewHostMsg_Geolocation_Resume,
OnResume)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void GeolocationDispatcherHostImpl::OnLocationUpdate(
const Geoposition& geoposition) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
for (std::set<int>::iterator it = geolocation_renderer_ids_.begin();
it != geolocation_renderer_ids_.end(); ++it) {
IPC::Message* message =
new ViewMsg_Geolocation_PositionUpdated(*it, geoposition);
CallRenderViewHost(resource_message_filter_process_id_, *it,
&RenderViewHost::Send, message);
}
}
void GeolocationDispatcherHostImpl::OnRegisterDispatcher(int render_view_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK_EQ(0u, geolocation_renderer_ids_.count(render_view_id));
geolocation_renderer_ids_.insert(render_view_id);
}
void GeolocationDispatcherHostImpl::OnUnregisterDispatcher(int render_view_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK_EQ(1u, geolocation_renderer_ids_.count(render_view_id));
geolocation_renderer_ids_.erase(render_view_id);
}
void GeolocationDispatcherHostImpl::OnRequestPermission(
int render_view_id,
int bridge_id,
const GURL& requesting_frame) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DVLOG(1) << __FUNCTION__ << " " << resource_message_filter_process_id_ << ":"
<< render_view_id << ":" << bridge_id;
geolocation_permission_context_->RequestGeolocationPermission(
resource_message_filter_process_id_, render_view_id, bridge_id,
requesting_frame);
}
void GeolocationDispatcherHostImpl::OnCancelPermissionRequest(
int render_view_id,
int bridge_id,
const GURL& requesting_frame) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DVLOG(1) << __FUNCTION__ << " " << resource_message_filter_process_id_ << ":"
<< render_view_id << ":" << bridge_id;
geolocation_permission_context_->CancelGeolocationPermissionRequest(
resource_message_filter_process_id_, render_view_id, bridge_id,
requesting_frame);
}
void GeolocationDispatcherHostImpl::OnStartUpdating(
int render_view_id,
int bridge_id,
const GURL& requesting_frame,
bool enable_high_accuracy) {
// WebKit sends the startupdating request before checking permissions, to
// optimize the no-location-available case and reduce latency in the success
// case (location lookup happens in parallel with the permission request).
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DVLOG(1) << __FUNCTION__ << " " << resource_message_filter_process_id_ << ":"
<< render_view_id << ":" << bridge_id;
bridge_update_options_[std::make_pair(render_view_id, bridge_id)] =
GeolocationObserverOptions(enable_high_accuracy);
geolocation_permission_context_->StartUpdatingRequested(
resource_message_filter_process_id_, render_view_id, bridge_id,
requesting_frame);
RefreshGeolocationObserverOptions();
}
void GeolocationDispatcherHostImpl::OnStopUpdating(int render_view_id,
int bridge_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DVLOG(1) << __FUNCTION__ << " " << resource_message_filter_process_id_ << ":"
<< render_view_id << ":" << bridge_id;
if (bridge_update_options_.erase(std::make_pair(render_view_id, bridge_id)))
RefreshGeolocationObserverOptions();
geolocation_permission_context_->StopUpdatingRequested(
resource_message_filter_process_id_, render_view_id, bridge_id);
}
void GeolocationDispatcherHostImpl::OnSuspend(int render_view_id,
int bridge_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DVLOG(1) << __FUNCTION__ << " " << resource_message_filter_process_id_ << ":"
<< render_view_id << ":" << bridge_id;
// TODO(bulach): connect this with GeolocationArbitrator.
}
void GeolocationDispatcherHostImpl::OnResume(int render_view_id,
int bridge_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DVLOG(1) << __FUNCTION__ << " " << resource_message_filter_process_id_ << ":"
<< render_view_id << ":" << bridge_id;
// TODO(bulach): connect this with GeolocationArbitrator.
}
void GeolocationDispatcherHostImpl::RefreshGeolocationObserverOptions() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
if (bridge_update_options_.empty()) {
if (location_provider_) {
location_provider_->RemoveObserver(this);
location_provider_ = NULL;
}
} else {
if (!location_provider_)
location_provider_ = GeolocationProvider::GetInstance();
// Re-add to re-establish our options, in case they changed.
location_provider_->AddObserver(this,
GeolocationObserverOptions::Collapse(
bridge_update_options_));
}
}
} // namespace
GeolocationDispatcherHost* GeolocationDispatcherHost::New(
int resource_message_filter_process_id,
GeolocationPermissionContext* geolocation_permission_context) {
return new GeolocationDispatcherHostImpl(resource_message_filter_process_id,
geolocation_permission_context);
}