blob: 18bfc25c3b46eb5620ac4ee50593d0136052c676 [file] [log] [blame]
// Copyright (c) 2012 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 "content/browser/browser_plugin/browser_plugin_embedder.h"
#include "base/values.h"
#include "content/browser/browser_plugin/browser_plugin_guest.h"
#include "content/browser/browser_plugin/browser_plugin_guest_manager.h"
#include "content/browser/browser_plugin/browser_plugin_host_factory.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/common/browser_plugin/browser_plugin_constants.h"
#include "content/common/browser_plugin/browser_plugin_messages.h"
#include "content/common/drag_messages.h"
#include "content/common/gpu/gpu_messages.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/native_web_keyboard_event.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/user_metrics.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/result_codes.h"
#include "content/public/common/url_constants.h"
#include "net/base/escape.h"
#include "ui/events/keycodes/keyboard_codes.h"
namespace content {
// static
BrowserPluginHostFactory* BrowserPluginEmbedder::factory_ = NULL;
BrowserPluginEmbedder::BrowserPluginEmbedder(WebContentsImpl* web_contents)
: WebContentsObserver(web_contents),
next_get_render_view_request_id_(0) {
}
BrowserPluginEmbedder::~BrowserPluginEmbedder() {
CleanUp();
}
// static
BrowserPluginEmbedder* BrowserPluginEmbedder::Create(
WebContentsImpl* web_contents) {
if (factory_)
return factory_->CreateBrowserPluginEmbedder(web_contents);
return new BrowserPluginEmbedder(web_contents);
}
void BrowserPluginEmbedder::DragEnteredGuest(BrowserPluginGuest* guest) {
guest_dragging_over_ = guest->AsWeakPtr();
}
void BrowserPluginEmbedder::DragLeftGuest(BrowserPluginGuest* guest) {
// Avoid race conditions in switching between guests being hovered over by
// only un-setting if the caller is marked as the guest being dragged over.
if (guest_dragging_over_.get() == guest) {
guest_dragging_over_.reset();
}
}
void BrowserPluginEmbedder::StartDrag(BrowserPluginGuest* guest) {
guest_started_drag_ = guest->AsWeakPtr();
}
void BrowserPluginEmbedder::StopDrag(BrowserPluginGuest* guest) {
if (guest_started_drag_.get() == guest) {
guest_started_drag_.reset();
}
}
void BrowserPluginEmbedder::GetRenderViewHostAtPosition(
int x, int y, const WebContents::GetRenderViewHostCallback& callback) {
// Store the callback so we can call it later when we have the response.
pending_get_render_view_callbacks_.insert(
std::make_pair(next_get_render_view_request_id_, callback));
Send(new BrowserPluginMsg_PluginAtPositionRequest(
routing_id(),
next_get_render_view_request_id_,
gfx::Point(x, y)));
++next_get_render_view_request_id_;
}
bool BrowserPluginEmbedder::DidSendScreenRectsCallback(
BrowserPluginGuest* guest) {
static_cast<RenderViewHostImpl*>(
guest->GetWebContents()->GetRenderViewHost())->SendScreenRects();
// Not handled => Iterate over all guests.
return false;
}
void BrowserPluginEmbedder::DidSendScreenRects() {
WebContentsImpl* embedder =
static_cast<WebContentsImpl*>(web_contents());
GetBrowserPluginGuestManager()->ForEachGuest(embedder, base::Bind(
&BrowserPluginEmbedder::DidSendScreenRectsCallback,
base::Unretained(this)));
}
bool BrowserPluginEmbedder::UnlockMouseIfNecessaryCallback(
const NativeWebKeyboardEvent& event,
BrowserPluginGuest* guest) {
return guest->UnlockMouseIfNecessary(event);
}
bool BrowserPluginEmbedder::HandleKeyboardEvent(
const NativeWebKeyboardEvent& event) {
if ((event.type != blink::WebInputEvent::RawKeyDown) ||
(event.windowsKeyCode != ui::VKEY_ESCAPE) ||
(event.modifiers & blink::WebInputEvent::InputModifiers)) {
return false;
}
WebContentsImpl* embedder =
static_cast<WebContentsImpl*>(web_contents());
return GetBrowserPluginGuestManager()->ForEachGuest(embedder, base::Bind(
&BrowserPluginEmbedder::UnlockMouseIfNecessaryCallback,
base::Unretained(this),
event));
}
bool BrowserPluginEmbedder::SetZoomLevelCallback(
double level, BrowserPluginGuest* guest) {
guest->GetWebContents()->SetZoomLevel(level);
// Not handled => Iterate over all guests.
return false;
}
void BrowserPluginEmbedder::SetZoomLevel(double level) {
WebContentsImpl* embedder =
static_cast<WebContentsImpl*>(web_contents());
GetBrowserPluginGuestManager()->ForEachGuest(embedder, base::Bind(
&BrowserPluginEmbedder::SetZoomLevelCallback,
base::Unretained(this),
level));
}
void BrowserPluginEmbedder::RenderProcessGone(base::TerminationStatus status) {
CleanUp();
}
bool BrowserPluginEmbedder::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(BrowserPluginEmbedder, message)
IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_AllocateInstanceID,
OnAllocateInstanceID)
IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_Attach, OnAttach)
IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_PluginAtPositionResponse,
OnPluginAtPositionResponse)
IPC_MESSAGE_HANDLER_GENERIC(DragHostMsg_UpdateDragCursor,
OnUpdateDragCursor(&handled));
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void BrowserPluginEmbedder::DragSourceEndedAt(int client_x, int client_y,
int screen_x, int screen_y, blink::WebDragOperation operation) {
if (guest_started_drag_.get()) {
gfx::Point guest_offset =
guest_started_drag_->GetScreenCoordinates(gfx::Point());
guest_started_drag_->DragSourceEndedAt(client_x - guest_offset.x(),
client_y - guest_offset.y(), screen_x, screen_y, operation);
}
}
void BrowserPluginEmbedder::DragSourceMovedTo(int client_x, int client_y,
int screen_x, int screen_y) {
if (guest_started_drag_.get()) {
gfx::Point guest_offset =
guest_started_drag_->GetScreenCoordinates(gfx::Point());
guest_started_drag_->DragSourceMovedTo(client_x - guest_offset.x(),
client_y - guest_offset.y(), screen_x, screen_y);
}
}
void BrowserPluginEmbedder::SystemDragEnded() {
if (guest_started_drag_.get() &&
(guest_started_drag_.get() != guest_dragging_over_.get()))
guest_started_drag_->EndSystemDrag();
guest_started_drag_.reset();
guest_dragging_over_.reset();
}
void BrowserPluginEmbedder::OnUpdateDragCursor(bool* handled) {
*handled = (guest_dragging_over_.get() != NULL);
}
void BrowserPluginEmbedder::CleanUp() {
// CleanUp gets called when BrowserPluginEmbedder's WebContents goes away
// or the associated RenderViewHost is destroyed or swapped out. Therefore we
// don't need to care about the pending callbacks anymore.
pending_get_render_view_callbacks_.clear();
}
BrowserPluginGuestManager*
BrowserPluginEmbedder::GetBrowserPluginGuestManager() {
BrowserPluginGuestManager* guest_manager = static_cast<WebContentsImpl*>(
web_contents())->GetBrowserPluginGuestManager();
if (!guest_manager) {
guest_manager = BrowserPluginGuestManager::Create();
web_contents()->GetBrowserContext()->SetUserData(
browser_plugin::kBrowserPluginGuestManagerKeyName, guest_manager);
}
return guest_manager;
}
void BrowserPluginEmbedder::OnAllocateInstanceID(int request_id) {
int instance_id = GetBrowserPluginGuestManager()->get_next_instance_id();
Send(new BrowserPluginMsg_AllocateInstanceID_ACK(
routing_id(), request_id, instance_id));
}
void BrowserPluginEmbedder::OnAttach(
int instance_id,
const BrowserPluginHostMsg_Attach_Params& params,
const base::DictionaryValue& extra_params) {
if (!GetBrowserPluginGuestManager()->CanEmbedderAccessInstanceIDMaybeKill(
web_contents()->GetRenderProcessHost()->GetID(), instance_id))
return;
BrowserPluginGuest* guest =
GetBrowserPluginGuestManager()->GetGuestByInstanceID(
instance_id, web_contents()->GetRenderProcessHost()->GetID());
if (guest) {
// There is an implicit order expectation here:
// 1. The content embedder is made aware of the attachment.
// 2. BrowserPluginGuest::Attach is called.
// 3. The content embedder issues queued events if any that happened
// prior to attachment.
GetContentClient()->browser()->GuestWebContentsAttached(
guest->GetWebContents(),
web_contents(),
extra_params);
guest->Attach(
static_cast<WebContentsImpl*>(web_contents()), params, extra_params);
return;
}
scoped_ptr<base::DictionaryValue> copy_extra_params(extra_params.DeepCopy());
guest = GetBrowserPluginGuestManager()->CreateGuest(
web_contents()->GetSiteInstance(),
instance_id, params,
copy_extra_params.Pass());
if (guest) {
GetContentClient()->browser()->GuestWebContentsAttached(
guest->GetWebContents(),
web_contents(),
extra_params);
guest->Initialize(params, static_cast<WebContentsImpl*>(web_contents()));
}
}
void BrowserPluginEmbedder::OnPluginAtPositionResponse(
int instance_id, int request_id, const gfx::Point& position) {
const std::map<int, WebContents::GetRenderViewHostCallback>::iterator
callback_iter = pending_get_render_view_callbacks_.find(request_id);
if (callback_iter == pending_get_render_view_callbacks_.end())
return;
RenderViewHost* render_view_host;
BrowserPluginGuest* guest = NULL;
if (instance_id != browser_plugin::kInstanceIDNone) {
guest = GetBrowserPluginGuestManager()->GetGuestByInstanceID(
instance_id, web_contents()->GetRenderProcessHost()->GetID());
}
if (guest)
render_view_host = guest->GetWebContents()->GetRenderViewHost();
else // No plugin, use embedder's RenderViewHost.
render_view_host = web_contents()->GetRenderViewHost();
callback_iter->second.Run(render_view_host, position.x(), position.y());
pending_get_render_view_callbacks_.erase(callback_iter);
}
} // namespace content