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/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" 27 28 namespace content { 29 30 // static 31 BrowserPluginHostFactory* BrowserPluginEmbedder::factory_ = NULL; 32 33 BrowserPluginEmbedder::BrowserPluginEmbedder(WebContentsImpl* web_contents) 34 : WebContentsObserver(web_contents), 35 next_get_render_view_request_id_(0) { 36 } 37 38 BrowserPluginEmbedder::~BrowserPluginEmbedder() { 39 CleanUp(); 40 } 41 42 // static 43 BrowserPluginEmbedder* BrowserPluginEmbedder::Create( 44 WebContentsImpl* web_contents) { 45 if (factory_) 46 return factory_->CreateBrowserPluginEmbedder(web_contents); 47 return new BrowserPluginEmbedder(web_contents); 48 } 49 50 void BrowserPluginEmbedder::DragEnteredGuest(BrowserPluginGuest* guest) { 51 guest_dragging_over_ = guest->AsWeakPtr(); 52 } 53 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(); 59 } 60 } 61 62 void BrowserPluginEmbedder::StartDrag(BrowserPluginGuest* guest) { 63 guest_started_drag_ = guest->AsWeakPtr(); 64 } 65 66 void BrowserPluginEmbedder::StopDrag(BrowserPluginGuest* guest) { 67 if (guest_started_drag_.get() == guest) { 68 guest_started_drag_.reset(); 69 } 70 } 71 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( 78 routing_id(), 79 next_get_render_view_request_id_, 80 gfx::Point(x, y))); 81 ++next_get_render_view_request_id_; 82 } 83 84 bool BrowserPluginEmbedder::DidSendScreenRectsCallback( 85 BrowserPluginGuest* guest) { 86 static_cast<RenderViewHostImpl*>( 87 guest->GetWebContents()->GetRenderViewHost())->SendScreenRects(); 88 // Not handled => Iterate over all guests. 89 return false; 90 } 91 92 void BrowserPluginEmbedder::DidSendScreenRects() { 93 WebContentsImpl* embedder = 94 static_cast<WebContentsImpl*>(web_contents()); 95 GetBrowserPluginGuestManager()->ForEachGuest(embedder, base::Bind( 96 &BrowserPluginEmbedder::DidSendScreenRectsCallback, 97 base::Unretained(this))); 98 } 99 100 bool BrowserPluginEmbedder::UnlockMouseIfNecessaryCallback( 101 const NativeWebKeyboardEvent& event, 102 BrowserPluginGuest* guest) { 103 return guest->UnlockMouseIfNecessary(event); 104 } 105 106 bool BrowserPluginEmbedder::HandleKeyboardEvent( 107 const NativeWebKeyboardEvent& event) { 108 if ((event.type != blink::WebInputEvent::RawKeyDown) || 109 (event.windowsKeyCode != ui::VKEY_ESCAPE) || 110 (event.modifiers & blink::WebInputEvent::InputModifiers)) { 111 return false; 112 } 113 114 WebContentsImpl* embedder = 115 static_cast<WebContentsImpl*>(web_contents()); 116 return GetBrowserPluginGuestManager()->ForEachGuest(embedder, base::Bind( 117 &BrowserPluginEmbedder::UnlockMouseIfNecessaryCallback, 118 base::Unretained(this), 119 event)); 120 } 121 122 bool BrowserPluginEmbedder::SetZoomLevelCallback( 123 double level, BrowserPluginGuest* guest) { 124 guest->GetWebContents()->SetZoomLevel(level); 125 // Not handled => Iterate over all guests. 126 return false; 127 } 128 129 void BrowserPluginEmbedder::SetZoomLevel(double level) { 130 WebContentsImpl* embedder = 131 static_cast<WebContentsImpl*>(web_contents()); 132 GetBrowserPluginGuestManager()->ForEachGuest(embedder, base::Bind( 133 &BrowserPluginEmbedder::SetZoomLevelCallback, 134 base::Unretained(this), 135 level)); 136 } 137 138 void BrowserPluginEmbedder::RenderProcessGone(base::TerminationStatus status) { 139 CleanUp(); 140 } 141 142 bool BrowserPluginEmbedder::OnMessageReceived(const IPC::Message& message) { 143 bool handled = true; 144 IPC_BEGIN_MESSAGE_MAP(BrowserPluginEmbedder, message) 145 IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_AllocateInstanceID, 146 OnAllocateInstanceID) 147 IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_Attach, OnAttach) 148 IPC_MESSAGE_HANDLER(BrowserPluginHostMsg_PluginAtPositionResponse, 149 OnPluginAtPositionResponse) 150 IPC_MESSAGE_HANDLER_GENERIC(DragHostMsg_UpdateDragCursor, 151 OnUpdateDragCursor(&handled)); 152 IPC_MESSAGE_UNHANDLED(handled = false) 153 IPC_END_MESSAGE_MAP() 154 return handled; 155 } 156 157 void BrowserPluginEmbedder::DragSourceEndedAt(int client_x, int client_y, 158 int screen_x, int screen_y, blink::WebDragOperation operation) { 159 if (guest_started_drag_.get()) { 160 gfx::Point guest_offset = 161 guest_started_drag_->GetScreenCoordinates(gfx::Point()); 162 guest_started_drag_->DragSourceEndedAt(client_x - guest_offset.x(), 163 client_y - guest_offset.y(), screen_x, screen_y, operation); 164 } 165 } 166 167 void BrowserPluginEmbedder::DragSourceMovedTo(int client_x, int client_y, 168 int screen_x, int screen_y) { 169 if (guest_started_drag_.get()) { 170 gfx::Point guest_offset = 171 guest_started_drag_->GetScreenCoordinates(gfx::Point()); 172 guest_started_drag_->DragSourceMovedTo(client_x - guest_offset.x(), 173 client_y - guest_offset.y(), screen_x, screen_y); 174 } 175 } 176 177 void BrowserPluginEmbedder::SystemDragEnded() { 178 if (guest_started_drag_.get() && 179 (guest_started_drag_.get() != guest_dragging_over_.get())) 180 guest_started_drag_->EndSystemDrag(); 181 guest_started_drag_.reset(); 182 guest_dragging_over_.reset(); 183 } 184 185 void BrowserPluginEmbedder::OnUpdateDragCursor(bool* handled) { 186 *handled = (guest_dragging_over_.get() != NULL); 187 } 188 189 void BrowserPluginEmbedder::CleanUp() { 190 // CleanUp gets called when BrowserPluginEmbedder's WebContents goes away 191 // or the associated RenderViewHost is destroyed or swapped out. Therefore we 192 // don't need to care about the pending callbacks anymore. 193 pending_get_render_view_callbacks_.clear(); 194 } 195 196 BrowserPluginGuestManager* 197 BrowserPluginEmbedder::GetBrowserPluginGuestManager() { 198 BrowserPluginGuestManager* guest_manager = static_cast<WebContentsImpl*>( 199 web_contents())->GetBrowserPluginGuestManager(); 200 if (!guest_manager) { 201 guest_manager = BrowserPluginGuestManager::Create(); 202 web_contents()->GetBrowserContext()->SetUserData( 203 browser_plugin::kBrowserPluginGuestManagerKeyName, guest_manager); 204 } 205 return guest_manager; 206 } 207 208 void BrowserPluginEmbedder::OnAllocateInstanceID(int request_id) { 209 int instance_id = GetBrowserPluginGuestManager()->get_next_instance_id(); 210 Send(new BrowserPluginMsg_AllocateInstanceID_ACK( 211 routing_id(), request_id, instance_id)); 212 } 213 214 void BrowserPluginEmbedder::OnAttach( 215 int instance_id, 216 const BrowserPluginHostMsg_Attach_Params& params, 217 const base::DictionaryValue& extra_params) { 218 if (!GetBrowserPluginGuestManager()->CanEmbedderAccessInstanceIDMaybeKill( 219 web_contents()->GetRenderProcessHost()->GetID(), instance_id)) 220 return; 221 222 BrowserPluginGuest* guest = 223 GetBrowserPluginGuestManager()->GetGuestByInstanceID( 224 instance_id, web_contents()->GetRenderProcessHost()->GetID()); 225 226 if (guest) { 227 // There is an implicit order expectation here: 228 // 1. The content embedder is made aware of the attachment. 229 // 2. BrowserPluginGuest::Attach is called. 230 // 3. The content embedder issues queued events if any that happened 231 // prior to attachment. 232 GetContentClient()->browser()->GuestWebContentsAttached( 233 guest->GetWebContents(), 234 web_contents(), 235 extra_params); 236 guest->Attach( 237 static_cast<WebContentsImpl*>(web_contents()), params, extra_params); 238 return; 239 } 240 241 scoped_ptr<base::DictionaryValue> copy_extra_params(extra_params.DeepCopy()); 242 guest = GetBrowserPluginGuestManager()->CreateGuest( 243 web_contents()->GetSiteInstance(), 244 instance_id, params, 245 copy_extra_params.Pass()); 246 if (guest) { 247 GetContentClient()->browser()->GuestWebContentsAttached( 248 guest->GetWebContents(), 249 web_contents(), 250 extra_params); 251 guest->Initialize(params, static_cast<WebContentsImpl*>(web_contents())); 252 } 253 } 254 255 void BrowserPluginEmbedder::OnPluginAtPositionResponse( 256 int instance_id, int request_id, const gfx::Point& position) { 257 const std::map<int, WebContents::GetRenderViewHostCallback>::iterator 258 callback_iter = pending_get_render_view_callbacks_.find(request_id); 259 if (callback_iter == pending_get_render_view_callbacks_.end()) 260 return; 261 262 RenderViewHost* render_view_host; 263 BrowserPluginGuest* guest = NULL; 264 if (instance_id != browser_plugin::kInstanceIDNone) { 265 guest = GetBrowserPluginGuestManager()->GetGuestByInstanceID( 266 instance_id, web_contents()->GetRenderProcessHost()->GetID()); 267 } 268 269 if (guest) 270 render_view_host = guest->GetWebContents()->GetRenderViewHost(); 271 else // No plugin, use embedder's RenderViewHost. 272 render_view_host = web_contents()->GetRenderViewHost(); 273 274 callback_iter->second.Run(render_view_host, position.x(), position.y()); 275 pending_get_render_view_callbacks_.erase(callback_iter); 276 } 277 278 } // namespace content 279