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.
5 #include "content/browser/browser_plugin/browser_plugin_embedder.h"
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/renderer_host/render_view_host_impl.h"
12 #include "content/browser/web_contents/web_contents_impl.h"
13 #include "content/common/browser_plugin/browser_plugin_constants.h"
14 #include "content/common/browser_plugin/browser_plugin_messages.h"
15 #include "content/common/drag_messages.h"
16 #include "content/common/gpu/gpu_messages.h"
17 #include "content/public/browser/browser_context.h"
18 #include "content/public/browser/content_browser_client.h"
19 #include "content/public/browser/native_web_keyboard_event.h"
20 #include "content/public/browser/render_view_host.h"
21 #include "content/public/browser/user_metrics.h"
22 #include "content/public/common/content_switches.h"
23 #include "content/public/common/result_codes.h"
24 #include "content/public/common/url_constants.h"
25 #include "net/base/escape.h"
26 #include "ui/events/keycodes/keyboard_codes.h"
31 BrowserPluginHostFactory
* BrowserPluginEmbedder::factory_
= NULL
;
33 BrowserPluginEmbedder::BrowserPluginEmbedder(WebContentsImpl
* web_contents
)
34 : WebContentsObserver(web_contents
),
35 next_get_render_view_request_id_(0) {
38 BrowserPluginEmbedder::~BrowserPluginEmbedder() {
43 BrowserPluginEmbedder
* BrowserPluginEmbedder::Create(
44 WebContentsImpl
* web_contents
) {
46 return factory_
->CreateBrowserPluginEmbedder(web_contents
);
47 return new BrowserPluginEmbedder(web_contents
);
50 void BrowserPluginEmbedder::DragEnteredGuest(BrowserPluginGuest
* guest
) {
51 guest_dragging_over_
= guest
->AsWeakPtr();
54 void BrowserPluginEmbedder::DragLeftGuest(BrowserPluginGuest
* guest
) {
55 // Avoid race conditions in switching between guests being hovered over by
56 // only un-setting if the caller is marked as the guest being dragged over.
57 if (guest_dragging_over_
.get() == guest
) {
58 guest_dragging_over_
.reset();
62 void BrowserPluginEmbedder::StartDrag(BrowserPluginGuest
* guest
) {
63 guest_started_drag_
= guest
->AsWeakPtr();
66 void BrowserPluginEmbedder::StopDrag(BrowserPluginGuest
* guest
) {
67 if (guest_started_drag_
.get() == guest
) {
68 guest_started_drag_
.reset();
72 void BrowserPluginEmbedder::GetRenderViewHostAtPosition(
73 int x
, int y
, const WebContents::GetRenderViewHostCallback
& callback
) {
74 // Store the callback so we can call it later when we have the response.
75 pending_get_render_view_callbacks_
.insert(
76 std::make_pair(next_get_render_view_request_id_
, callback
));
77 Send(new BrowserPluginMsg_PluginAtPositionRequest(
79 next_get_render_view_request_id_
,
81 ++next_get_render_view_request_id_
;
84 WebContentsImpl
* BrowserPluginEmbedder::GetWebContents() {
85 return static_cast<WebContentsImpl
*>(web_contents());
88 bool BrowserPluginEmbedder::DidSendScreenRectsCallback(
89 BrowserPluginGuest
* guest
) {
90 static_cast<RenderViewHostImpl
*>(
91 guest
->GetWebContents()->GetRenderViewHost())->SendScreenRects();
92 // Not handled => Iterate over all guests.
96 void BrowserPluginEmbedder::DidSendScreenRects() {
97 GetBrowserPluginGuestManager()->ForEachGuest(GetWebContents(), base::Bind(
98 &BrowserPluginEmbedder::DidSendScreenRectsCallback
,
99 base::Unretained(this)));
102 bool BrowserPluginEmbedder::UnlockMouseIfNecessaryCallback(
103 const NativeWebKeyboardEvent
& event
,
104 BrowserPluginGuest
* guest
) {
105 return guest
->UnlockMouseIfNecessary(event
);
108 bool BrowserPluginEmbedder::HandleKeyboardEvent(
109 const NativeWebKeyboardEvent
& event
) {
110 if ((event
.type
!= blink::WebInputEvent::RawKeyDown
) ||
111 (event
.windowsKeyCode
!= ui::VKEY_ESCAPE
) ||
112 (event
.modifiers
& blink::WebInputEvent::InputModifiers
)) {
116 return GetBrowserPluginGuestManager()->ForEachGuest(GetWebContents(),
117 base::Bind(&BrowserPluginEmbedder::UnlockMouseIfNecessaryCallback
,
118 base::Unretained(this),
122 bool BrowserPluginEmbedder::SetZoomLevelCallback(
123 double level
, BrowserPluginGuest
* guest
) {
124 guest
->GetWebContents()->SetZoomLevel(level
);
125 // Not handled => Iterate over all guests.
129 void BrowserPluginEmbedder::SetZoomLevel(double level
) {
130 GetBrowserPluginGuestManager()->ForEachGuest(GetWebContents(), base::Bind(
131 &BrowserPluginEmbedder::SetZoomLevelCallback
,
132 base::Unretained(this),
136 void BrowserPluginEmbedder::RenderProcessGone(base::TerminationStatus status
) {
140 bool BrowserPluginEmbedder::OnMessageReceived(const IPC::Message
& message
) {
142 IPC_BEGIN_MESSAGE_MAP(BrowserPluginEmbedder
, message
)
143 IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_AllocateInstanceID
,
144 OnAllocateInstanceID
)
145 IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_Attach
, OnAttach
)
146 IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_PluginAtPositionResponse
,
147 OnPluginAtPositionResponse
)
148 IPC_MESSAGE_HANDLER_GENERIC(DragHostMsg_UpdateDragCursor
,
149 OnUpdateDragCursor(&handled
));
150 IPC_MESSAGE_UNHANDLED(handled
= false)
151 IPC_END_MESSAGE_MAP()
155 void BrowserPluginEmbedder::DragSourceEndedAt(int client_x
, int client_y
,
156 int screen_x
, int screen_y
, blink::WebDragOperation operation
) {
157 if (guest_started_drag_
.get()) {
158 gfx::Point guest_offset
=
159 guest_started_drag_
->GetScreenCoordinates(gfx::Point());
160 guest_started_drag_
->DragSourceEndedAt(client_x
- guest_offset
.x(),
161 client_y
- guest_offset
.y(), screen_x
, screen_y
, operation
);
165 void BrowserPluginEmbedder::DragSourceMovedTo(int client_x
, int client_y
,
166 int screen_x
, int screen_y
) {
167 if (guest_started_drag_
.get()) {
168 gfx::Point guest_offset
=
169 guest_started_drag_
->GetScreenCoordinates(gfx::Point());
170 guest_started_drag_
->DragSourceMovedTo(client_x
- guest_offset
.x(),
171 client_y
- guest_offset
.y(), screen_x
, screen_y
);
175 void BrowserPluginEmbedder::SystemDragEnded() {
176 if (guest_started_drag_
.get() &&
177 (guest_started_drag_
.get() != guest_dragging_over_
.get()))
178 guest_started_drag_
->EndSystemDrag();
179 guest_started_drag_
.reset();
180 guest_dragging_over_
.reset();
183 void BrowserPluginEmbedder::OnUpdateDragCursor(bool* handled
) {
184 *handled
= (guest_dragging_over_
.get() != NULL
);
187 void BrowserPluginEmbedder::CleanUp() {
188 // CleanUp gets called when BrowserPluginEmbedder's WebContents goes away
189 // or the associated RenderViewHost is destroyed or swapped out. Therefore we
190 // don't need to care about the pending callbacks anymore.
191 pending_get_render_view_callbacks_
.clear();
194 BrowserPluginGuestManager
*
195 BrowserPluginEmbedder::GetBrowserPluginGuestManager() {
196 BrowserPluginGuestManager
* guest_manager
=
197 GetWebContents()->GetBrowserPluginGuestManager();
198 if (!guest_manager
) {
199 guest_manager
= BrowserPluginGuestManager::Create();
200 GetWebContents()->GetBrowserContext()->SetUserData(
201 browser_plugin::kBrowserPluginGuestManagerKeyName
, guest_manager
);
203 return guest_manager
;
206 void BrowserPluginEmbedder::OnAllocateInstanceID(int request_id
) {
207 int instance_id
= GetBrowserPluginGuestManager()->get_next_instance_id();
208 Send(new BrowserPluginMsg_AllocateInstanceID_ACK(
209 routing_id(), request_id
, instance_id
));
212 void BrowserPluginEmbedder::OnAttach(
214 const BrowserPluginHostMsg_Attach_Params
& params
,
215 const base::DictionaryValue
& extra_params
) {
216 if (!GetBrowserPluginGuestManager()->CanEmbedderAccessInstanceIDMaybeKill(
217 GetWebContents()->GetRenderProcessHost()->GetID(), instance_id
))
220 BrowserPluginGuest
* guest
=
221 GetBrowserPluginGuestManager()->GetGuestByInstanceID(
222 instance_id
, GetWebContents()->GetRenderProcessHost()->GetID());
225 // There is an implicit order expectation here:
226 // 1. The content embedder is made aware of the attachment.
227 // 2. BrowserPluginGuest::Attach is called.
228 // 3. The content embedder issues queued events if any that happened
229 // prior to attachment.
230 GetContentClient()->browser()->GuestWebContentsAttached(
231 guest
->GetWebContents(),
234 guest
->Attach(GetWebContents(), params
, extra_params
);
238 scoped_ptr
<base::DictionaryValue
> copy_extra_params(extra_params
.DeepCopy());
239 guest
= GetBrowserPluginGuestManager()->CreateGuest(
240 GetWebContents()->GetSiteInstance(),
242 copy_extra_params
.Pass());
244 GetContentClient()->browser()->GuestWebContentsAttached(
245 guest
->GetWebContents(),
248 guest
->Initialize(params
, GetWebContents());
252 void BrowserPluginEmbedder::OnPluginAtPositionResponse(
253 int instance_id
, int request_id
, const gfx::Point
& position
) {
254 const std::map
<int, WebContents::GetRenderViewHostCallback
>::iterator
255 callback_iter
= pending_get_render_view_callbacks_
.find(request_id
);
256 if (callback_iter
== pending_get_render_view_callbacks_
.end())
259 RenderViewHost
* render_view_host
;
260 BrowserPluginGuest
* guest
= NULL
;
261 if (instance_id
!= browser_plugin::kInstanceIDNone
) {
262 guest
= GetBrowserPluginGuestManager()->GetGuestByInstanceID(
263 instance_id
, GetWebContents()->GetRenderProcessHost()->GetID());
267 render_view_host
= guest
->GetWebContents()->GetRenderViewHost();
268 else // No plugin, use embedder's RenderViewHost.
269 render_view_host
= GetWebContents()->GetRenderViewHost();
271 callback_iter
->second
.Run(render_view_host
, position
.x(), position
.y());
272 pending_get_render_view_callbacks_
.erase(callback_iter
);
275 } // namespace content