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