browser_plugin_embedder.cc revision 0f1bc08d4cfcc34181b0b5cbf065c40f687bf740
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/browser/browser_plugin/browser_plugin_embedder.h"
6
7#include "base/values.h"
8#include "content/browser/browser_plugin/browser_plugin_guest.h"
9#include "content/browser/browser_plugin/browser_plugin_guest_manager.h"
10#include "content/browser/browser_plugin/browser_plugin_host_factory.h"
11#include "content/browser/web_contents/web_contents_impl.h"
12#include "content/common/browser_plugin/browser_plugin_constants.h"
13#include "content/common/browser_plugin/browser_plugin_messages.h"
14#include "content/common/drag_messages.h"
15#include "content/common/gpu/gpu_messages.h"
16#include "content/public/browser/browser_context.h"
17#include "content/public/browser/content_browser_client.h"
18#include "content/public/browser/native_web_keyboard_event.h"
19#include "content/public/browser/render_view_host.h"
20#include "content/public/browser/user_metrics.h"
21#include "content/public/common/content_switches.h"
22#include "content/public/common/result_codes.h"
23#include "content/public/common/url_constants.h"
24#include "net/base/escape.h"
25
26namespace content {
27
28// static
29BrowserPluginHostFactory* BrowserPluginEmbedder::factory_ = NULL;
30
31BrowserPluginEmbedder::BrowserPluginEmbedder(WebContentsImpl* web_contents)
32    : WebContentsObserver(web_contents),
33      next_get_render_view_request_id_(0) {
34}
35
36BrowserPluginEmbedder::~BrowserPluginEmbedder() {
37  CleanUp();
38}
39
40// static
41BrowserPluginEmbedder* BrowserPluginEmbedder::Create(
42    WebContentsImpl* web_contents) {
43  if (factory_)
44    return factory_->CreateBrowserPluginEmbedder(web_contents);
45  return new BrowserPluginEmbedder(web_contents);
46}
47
48void BrowserPluginEmbedder::DragEnteredGuest(BrowserPluginGuest* guest) {
49  guest_dragging_over_ = guest->AsWeakPtr();
50}
51
52void BrowserPluginEmbedder::DragLeftGuest(BrowserPluginGuest* guest) {
53  // Avoid race conditions in switching between guests being hovered over by
54  // only un-setting if the caller is marked as the guest being dragged over.
55  if (guest_dragging_over_.get() == guest) {
56    guest_dragging_over_.reset();
57  }
58}
59
60void BrowserPluginEmbedder::StartDrag(BrowserPluginGuest* guest) {
61  guest_started_drag_ = guest->AsWeakPtr();
62}
63
64void BrowserPluginEmbedder::StopDrag(BrowserPluginGuest* guest) {
65  if (guest_started_drag_.get() == guest) {
66    guest_started_drag_.reset();
67  }
68}
69
70void BrowserPluginEmbedder::GetRenderViewHostAtPosition(
71    int x, int y, const WebContents::GetRenderViewHostCallback& callback) {
72  // Store the callback so we can call it later when we have the response.
73  pending_get_render_view_callbacks_.insert(
74      std::make_pair(next_get_render_view_request_id_, callback));
75  Send(new BrowserPluginMsg_PluginAtPositionRequest(
76      routing_id(),
77      next_get_render_view_request_id_,
78      gfx::Point(x, y)));
79  ++next_get_render_view_request_id_;
80}
81
82void BrowserPluginEmbedder::DidSendScreenRects() {
83  GetBrowserPluginGuestManager()->DidSendScreenRects(
84      static_cast<WebContentsImpl*>(web_contents()));
85}
86
87bool BrowserPluginEmbedder::HandleKeyboardEvent(
88    const NativeWebKeyboardEvent& event) {
89  return GetBrowserPluginGuestManager()->UnlockMouseIfNecessary(
90      static_cast<WebContentsImpl*>(web_contents()), event);
91}
92
93void BrowserPluginEmbedder::RenderProcessGone(base::TerminationStatus status) {
94  CleanUp();
95}
96
97bool BrowserPluginEmbedder::OnMessageReceived(const IPC::Message& message) {
98  bool handled = true;
99  IPC_BEGIN_MESSAGE_MAP(BrowserPluginEmbedder, message)
100    IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_AllocateInstanceID,
101                        OnAllocateInstanceID)
102    IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_Attach, OnAttach)
103    IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_PluginAtPositionResponse,
104                        OnPluginAtPositionResponse)
105    IPC_MESSAGE_HANDLER_GENERIC(DragHostMsg_UpdateDragCursor,
106                                OnUpdateDragCursor(&handled));
107    IPC_MESSAGE_UNHANDLED(handled = false)
108  IPC_END_MESSAGE_MAP()
109  return handled;
110}
111
112void BrowserPluginEmbedder::DragSourceEndedAt(int client_x, int client_y,
113    int screen_x, int screen_y, WebKit::WebDragOperation operation) {
114  if (guest_started_drag_.get()) {
115    gfx::Point guest_offset =
116        guest_started_drag_->GetScreenCoordinates(gfx::Point());
117    guest_started_drag_->DragSourceEndedAt(client_x - guest_offset.x(),
118        client_y - guest_offset.y(), screen_x, screen_y, operation);
119  }
120}
121
122void BrowserPluginEmbedder::DragSourceMovedTo(int client_x, int client_y,
123                                              int screen_x, int screen_y) {
124  if (guest_started_drag_.get()) {
125    gfx::Point guest_offset =
126        guest_started_drag_->GetScreenCoordinates(gfx::Point());
127    guest_started_drag_->DragSourceMovedTo(client_x - guest_offset.x(),
128        client_y - guest_offset.y(), screen_x, screen_y);
129  }
130}
131
132void BrowserPluginEmbedder::SystemDragEnded() {
133  if (guest_started_drag_.get() &&
134      (guest_started_drag_.get() != guest_dragging_over_.get()))
135    guest_started_drag_->EndSystemDrag();
136  guest_started_drag_.reset();
137  guest_dragging_over_.reset();
138}
139
140void BrowserPluginEmbedder::OnUpdateDragCursor(bool* handled) {
141  *handled = (guest_dragging_over_.get() != NULL);
142}
143
144void BrowserPluginEmbedder::CleanUp() {
145  // CleanUp gets called when BrowserPluginEmbedder's WebContents goes away
146  // or the associated RenderViewHost is destroyed or swapped out. Therefore we
147  // don't need to care about the pending callbacks anymore.
148  pending_get_render_view_callbacks_.clear();
149}
150
151BrowserPluginGuestManager*
152    BrowserPluginEmbedder::GetBrowserPluginGuestManager() {
153  BrowserPluginGuestManager* guest_manager = static_cast<WebContentsImpl*>(
154      web_contents())->GetBrowserPluginGuestManager();
155  if (!guest_manager) {
156    guest_manager = BrowserPluginGuestManager::Create();
157    web_contents()->GetBrowserContext()->SetUserData(
158        browser_plugin::kBrowserPluginGuestManagerKeyName, guest_manager);
159  }
160  return guest_manager;
161}
162
163void BrowserPluginEmbedder::OnAllocateInstanceID(int request_id) {
164  int instance_id = GetBrowserPluginGuestManager()->get_next_instance_id();
165  Send(new BrowserPluginMsg_AllocateInstanceID_ACK(
166      routing_id(), request_id, instance_id));
167}
168
169void BrowserPluginEmbedder::OnAttach(
170    int instance_id,
171    const BrowserPluginHostMsg_Attach_Params& params,
172    const base::DictionaryValue& extra_params) {
173  if (!GetBrowserPluginGuestManager()->CanEmbedderAccessInstanceIDMaybeKill(
174          web_contents()->GetRenderProcessHost()->GetID(), instance_id))
175    return;
176
177  BrowserPluginGuest* guest =
178      GetBrowserPluginGuestManager()->GetGuestByInstanceID(
179          instance_id, web_contents()->GetRenderProcessHost()->GetID());
180
181  if (guest) {
182    // There is an implicit order expectation here:
183    // 1. The content embedder is made aware of the attachment.
184    // 2. BrowserPluginGuest::Attach is called.
185    // 3. The content embedder issues queued events if any that happened
186    //    prior to attachment.
187    GetContentClient()->browser()->GuestWebContentsAttached(
188        guest->GetWebContents(),
189        web_contents(),
190        extra_params);
191    guest->Attach(
192        static_cast<WebContentsImpl*>(web_contents()), params, extra_params);
193    return;
194  }
195
196  scoped_ptr<base::DictionaryValue> copy_extra_params(extra_params.DeepCopy());
197  guest = GetBrowserPluginGuestManager()->CreateGuest(
198      web_contents()->GetSiteInstance(),
199      instance_id, params,
200      copy_extra_params.Pass());
201  if (guest) {
202    GetContentClient()->browser()->GuestWebContentsAttached(
203        guest->GetWebContents(),
204        web_contents(),
205        extra_params);
206    guest->Initialize(static_cast<WebContentsImpl*>(web_contents()), params);
207  }
208}
209
210void BrowserPluginEmbedder::OnPluginAtPositionResponse(
211    int instance_id, int request_id, const gfx::Point& position) {
212  const std::map<int, WebContents::GetRenderViewHostCallback>::iterator
213      callback_iter = pending_get_render_view_callbacks_.find(request_id);
214  if (callback_iter == pending_get_render_view_callbacks_.end())
215    return;
216
217  RenderViewHost* render_view_host;
218  BrowserPluginGuest* guest = NULL;
219  if (instance_id != browser_plugin::kInstanceIDNone) {
220    guest = GetBrowserPluginGuestManager()->GetGuestByInstanceID(
221                instance_id, web_contents()->GetRenderProcessHost()->GetID());
222  }
223
224  if (guest)
225    render_view_host = guest->GetWebContents()->GetRenderViewHost();
226  else  // No plugin, use embedder's RenderViewHost.
227    render_view_host = web_contents()->GetRenderViewHost();
228
229  callback_iter->second.Run(render_view_host, position.x(), position.y());
230  pending_get_render_view_callbacks_.erase(callback_iter);
231}
232
233}  // namespace content
234