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::GetRenderViewHostAtPosition(
67 int x, int y, const WebContents::GetRenderViewHostCallback& callback) {
68 // Store the callback so we can call it later when we have the response.
69 pending_get_render_view_callbacks_.insert(
70 std::make_pair(next_get_render_view_request_id_, callback));
71 Send(new BrowserPluginMsg_PluginAtPositionRequest(
73 next_get_render_view_request_id_,
75 ++next_get_render_view_request_id_;
78 WebContentsImpl* BrowserPluginEmbedder::GetWebContents() {
79 return static_cast<WebContentsImpl*>(web_contents());
82 bool BrowserPluginEmbedder::DidSendScreenRectsCallback(
83 BrowserPluginGuest* guest) {
84 static_cast<RenderViewHostImpl*>(
85 guest->GetWebContents()->GetRenderViewHost())->SendScreenRects();
86 // Not handled => Iterate over all guests.
90 void BrowserPluginEmbedder::DidSendScreenRects() {
91 GetBrowserPluginGuestManager()->ForEachGuest(GetWebContents(), base::Bind(
92 &BrowserPluginEmbedder::DidSendScreenRectsCallback,
93 base::Unretained(this)));
96 bool BrowserPluginEmbedder::UnlockMouseIfNecessaryCallback(
97 const NativeWebKeyboardEvent& event,
98 BrowserPluginGuest* guest) {
99 return guest->UnlockMouseIfNecessary(event);
102 bool BrowserPluginEmbedder::HandleKeyboardEvent(
103 const NativeWebKeyboardEvent& event) {
104 if ((event.type != blink::WebInputEvent::RawKeyDown) ||
105 (event.windowsKeyCode != ui::VKEY_ESCAPE) ||
106 (event.modifiers & blink::WebInputEvent::InputModifiers)) {
110 return GetBrowserPluginGuestManager()->ForEachGuest(GetWebContents(),
111 base::Bind(&BrowserPluginEmbedder::UnlockMouseIfNecessaryCallback,
112 base::Unretained(this),
116 bool BrowserPluginEmbedder::SetZoomLevelCallback(
117 double level, BrowserPluginGuest* guest) {
118 double zoom_factor = content::ZoomLevelToZoomFactor(level);
119 guest->SetZoom(zoom_factor);
120 // Not handled => Iterate over all guests.
124 void BrowserPluginEmbedder::SetZoomLevel(double level) {
125 GetBrowserPluginGuestManager()->ForEachGuest(GetWebContents(), base::Bind(
126 &BrowserPluginEmbedder::SetZoomLevelCallback,
127 base::Unretained(this),
131 void BrowserPluginEmbedder::RenderProcessGone(base::TerminationStatus status) {
135 bool BrowserPluginEmbedder::OnMessageReceived(const IPC::Message& message) {
137 IPC_BEGIN_MESSAGE_MAP(BrowserPluginEmbedder, message)
138 IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_AllocateInstanceID,
139 OnAllocateInstanceID)
140 IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_Attach, OnAttach)
141 IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_PluginAtPositionResponse,
142 OnPluginAtPositionResponse)
143 IPC_MESSAGE_HANDLER_GENERIC(DragHostMsg_UpdateDragCursor,
144 OnUpdateDragCursor(&handled));
145 IPC_MESSAGE_UNHANDLED(handled = false)
146 IPC_END_MESSAGE_MAP()
150 void BrowserPluginEmbedder::DragSourceEndedAt(int client_x, int client_y,
151 int screen_x, int screen_y, blink::WebDragOperation operation) {
152 if (guest_started_drag_.get()) {
153 gfx::Point guest_offset =
154 guest_started_drag_->GetScreenCoordinates(gfx::Point());
155 guest_started_drag_->DragSourceEndedAt(client_x - guest_offset.x(),
156 client_y - guest_offset.y(), screen_x, screen_y, operation);
160 void BrowserPluginEmbedder::DragSourceMovedTo(int client_x, int client_y,
161 int screen_x, int screen_y) {
162 if (guest_started_drag_.get()) {
163 gfx::Point guest_offset =
164 guest_started_drag_->GetScreenCoordinates(gfx::Point());
165 guest_started_drag_->DragSourceMovedTo(client_x - guest_offset.x(),
166 client_y - guest_offset.y(), screen_x, screen_y);
170 void BrowserPluginEmbedder::SystemDragEnded() {
171 // When the embedder's drag/drop operation ends, we need to pass the message
172 // to the guest that initiated the drag/drop operation. This will ensure that
173 // the guest's RVH state is reset properly.
174 if (guest_started_drag_.get())
175 guest_started_drag_->EndSystemDrag();
176 guest_started_drag_.reset();
177 guest_dragging_over_.reset();
180 void BrowserPluginEmbedder::OnUpdateDragCursor(bool* handled) {
181 *handled = (guest_dragging_over_.get() != NULL);
184 void BrowserPluginEmbedder::CleanUp() {
185 // CleanUp gets called when BrowserPluginEmbedder's WebContents goes away
186 // or the associated RenderViewHost is destroyed or swapped out. Therefore we
187 // don't need to care about the pending callbacks anymore.
188 pending_get_render_view_callbacks_.clear();
191 BrowserPluginGuestManager*
192 BrowserPluginEmbedder::GetBrowserPluginGuestManager() {
193 BrowserPluginGuestManager* guest_manager =
194 GetWebContents()->GetBrowserPluginGuestManager();
195 if (!guest_manager) {
196 guest_manager = BrowserPluginGuestManager::Create();
197 GetWebContents()->GetBrowserContext()->SetUserData(
198 browser_plugin::kBrowserPluginGuestManagerKeyName, guest_manager);
200 return guest_manager;
203 void BrowserPluginEmbedder::OnAllocateInstanceID(int request_id) {
204 int instance_id = GetBrowserPluginGuestManager()->get_next_instance_id();
205 Send(new BrowserPluginMsg_AllocateInstanceID_ACK(
206 routing_id(), request_id, instance_id));
209 void BrowserPluginEmbedder::OnAttach(
211 const BrowserPluginHostMsg_Attach_Params& params,
212 const base::DictionaryValue& extra_params) {
213 if (!GetBrowserPluginGuestManager()->CanEmbedderAccessInstanceIDMaybeKill(
214 GetWebContents()->GetRenderProcessHost()->GetID(), instance_id))
217 BrowserPluginGuest* guest =
218 GetBrowserPluginGuestManager()->GetGuestByInstanceID(
219 instance_id, GetWebContents()->GetRenderProcessHost()->GetID());
222 // There is an implicit order expectation here:
223 // 1. The content embedder is made aware of the attachment.
224 // 2. BrowserPluginGuest::Attach is called.
225 // 3. The content embedder issues queued events if any that happened
226 // prior to attachment.
227 GetContentClient()->browser()->GuestWebContentsAttached(
228 guest->GetWebContents(),
231 guest->Attach(GetWebContents(), params, extra_params);
235 scoped_ptr<base::DictionaryValue> copy_extra_params(extra_params.DeepCopy());
236 guest = GetBrowserPluginGuestManager()->CreateGuest(
237 GetWebContents()->GetSiteInstance(),
239 copy_extra_params.Pass());
241 GetContentClient()->browser()->GuestWebContentsAttached(
242 guest->GetWebContents(),
245 guest->Initialize(params, GetWebContents());
249 void BrowserPluginEmbedder::OnPluginAtPositionResponse(
250 int instance_id, int request_id, const gfx::Point& position) {
251 const std::map<int, WebContents::GetRenderViewHostCallback>::iterator
252 callback_iter = pending_get_render_view_callbacks_.find(request_id);
253 if (callback_iter == pending_get_render_view_callbacks_.end())
256 RenderViewHost* render_view_host;
257 BrowserPluginGuest* guest = NULL;
258 if (instance_id != browser_plugin::kInstanceIDNone) {
259 guest = GetBrowserPluginGuestManager()->GetGuestByInstanceID(
260 instance_id, GetWebContents()->GetRenderProcessHost()->GetID());
264 render_view_host = guest->GetWebContents()->GetRenderViewHost();
265 else // No plugin, use embedder's RenderViewHost.
266 render_view_host = GetWebContents()->GetRenderViewHost();
268 callback_iter->second.Run(render_view_host, position.x(), position.y());
269 pending_get_render_view_callbacks_.erase(callback_iter);
272 } // namespace content