OLD | NEW |
(Empty) | |
| 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_host.h" |
| 6 |
| 7 #include "base/utf_string_conversions.h" |
| 8 #include "content/browser/browser_plugin/browser_plugin_host_helper.h" |
| 9 #include "content/browser/renderer_host/render_view_host_impl.h" |
| 10 #include "content/browser/web_contents/web_contents_impl.h" |
| 11 #include "content/common/browser_plugin_messages.h" |
| 12 #include "content/common/view_messages.h" |
| 13 #include "content/port/browser/render_widget_host_view_port.h" |
| 14 #include "content/public/browser/notification_details.h" |
| 15 #include "content/public/browser/notification_service.h" |
| 16 #include "content/public/browser/notification_source.h" |
| 17 #include "content/public/browser/notification_types.h" |
| 18 #include "content/public/browser/render_process_host.h" |
| 19 #include "content/public/browser/render_widget_host_view.h" |
| 20 #include "content/public/browser/site_instance.h" |
| 21 #include "content/public/browser/web_contents_view.h" |
| 22 #include "content/public/common/result_codes.h" |
| 23 #include "content/public/common/url_constants.h" |
| 24 |
| 25 namespace content { |
| 26 |
| 27 namespace { |
| 28 const int kGuestHangTimeout = 5000; |
| 29 } |
| 30 |
| 31 BrowserPluginHost::BrowserPluginHost( |
| 32 WebContentsImpl* web_contents) |
| 33 : WebContentsObserver(web_contents), |
| 34 embedder_render_process_host_(NULL), |
| 35 instance_id_(0), |
| 36 damage_buffer_(NULL), |
| 37 pending_update_counter_(0) { |
| 38 // Listen to visibility changes so that an embedder hides its guests |
| 39 // as well. |
| 40 registrar_.Add(this, |
| 41 NOTIFICATION_WEB_CONTENTS_VISIBILITY_CHANGED, |
| 42 Source<WebContents>(web_contents)); |
| 43 // Construct plumbing helpers when a new RenderViewHost is created for |
| 44 // this BrowserPluginHost's WebContentsImpl. |
| 45 registrar_.Add(this, |
| 46 NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED, |
| 47 Source<WebContents>(web_contents)); |
| 48 } |
| 49 |
| 50 BrowserPluginHost::~BrowserPluginHost() { |
| 51 if (damage_buffer_) |
| 52 delete damage_buffer_; |
| 53 } |
| 54 |
| 55 BrowserPluginHost* BrowserPluginHost::GetGuestByInstanceID( |
| 56 int instance_id) const { |
| 57 ContainerInstanceMap::const_iterator it = |
| 58 guests_by_instance_id_.find(instance_id); |
| 59 if (it != guests_by_instance_id_.end()) |
| 60 return it->second; |
| 61 return NULL; |
| 62 } |
| 63 |
| 64 void BrowserPluginHost::AddGuest(int instance_id, |
| 65 BrowserPluginHost* guest, |
| 66 int64 frame_id) { |
| 67 DCHECK(guests_by_instance_id_.find(instance_id) == |
| 68 guests_by_instance_id_.end()); |
| 69 guests_by_instance_id_[instance_id] = guest; |
| 70 guests_[guest->web_contents()] = frame_id; |
| 71 } |
| 72 |
| 73 bool BrowserPluginHost::TakeFocus(bool reverse) { |
| 74 embedder_render_process_host()->Send( |
| 75 new BrowserPluginMsg_AdvanceFocus(instance_id(), reverse)); |
| 76 return true; |
| 77 } |
| 78 |
| 79 void BrowserPluginHost::RendererUnresponsive(WebContents* source) { |
| 80 if (embedder_render_process_host()) { |
| 81 base::ProcessHandle process_handle = |
| 82 web_contents()->GetRenderProcessHost()->GetHandle(); |
| 83 base::KillProcess(process_handle, RESULT_CODE_HUNG, false); |
| 84 } |
| 85 } |
| 86 |
| 87 bool BrowserPluginHost::OnMessageReceived(const IPC::Message& message) { |
| 88 return false; |
| 89 } |
| 90 |
| 91 void BrowserPluginHost::NavigateGuest( |
| 92 RenderViewHost* render_view_host, |
| 93 int instance_id, |
| 94 int64 frame_id, |
| 95 const std::string& src, |
| 96 const gfx::Size& size) { |
| 97 BrowserPluginHost* guest = GetGuestByInstanceID(instance_id); |
| 98 WebContentsImpl* guest_web_contents = NULL; |
| 99 GURL url(src); |
| 100 if (!guest) { |
| 101 std::string host = render_view_host->GetSiteInstance()->GetSite().host(); |
| 102 GURL guest_site( |
| 103 base::StringPrintf("%s://%s", chrome::kGuestScheme, host.c_str())); |
| 104 // The SiteInstance of a given guest is based on the fact that it's a guest |
| 105 // in addition to which platform application the guest belongs to, rather |
| 106 // than the URL that the guest is being navigated to. |
| 107 SiteInstance* guest_site_instance = |
| 108 SiteInstance::CreateForURL(web_contents()->GetBrowserContext(), |
| 109 guest_site); |
| 110 guest_web_contents = |
| 111 static_cast<WebContentsImpl*>( |
| 112 WebContents::Create( |
| 113 web_contents()->GetBrowserContext(), |
| 114 guest_site_instance, |
| 115 MSG_ROUTING_NONE, |
| 116 NULL, // base WebContents |
| 117 NULL // session storage namespace |
| 118 )); |
| 119 guest = guest_web_contents->browser_plugin_host(); |
| 120 guest->set_embedder_render_process_host( |
| 121 render_view_host->GetProcess()); |
| 122 guest->set_instance_id(instance_id); |
| 123 guest_web_contents->GetMutableRendererPrefs()-> |
| 124 throttle_input_events = false; |
| 125 AddGuest(instance_id, guest, frame_id); |
| 126 } else { |
| 127 guest_web_contents = static_cast<WebContentsImpl*>(guest->web_contents()); |
| 128 } |
| 129 guest->web_contents()->SetDelegate(guest); |
| 130 guest->web_contents()->GetController().LoadURL( |
| 131 url, |
| 132 Referrer(), |
| 133 PAGE_TRANSITION_AUTO_SUBFRAME, |
| 134 std::string()); |
| 135 if (!size.IsEmpty()) |
| 136 guest_web_contents->GetView()->SizeContents(size); |
| 137 } |
| 138 |
| 139 void BrowserPluginHost::SetDamageBuffer(TransportDIB* damage_buffer, |
| 140 const gfx::Size& size, |
| 141 float scale_factor) { |
| 142 if (damage_buffer_) |
| 143 delete damage_buffer_; |
| 144 damage_buffer_ = damage_buffer; |
| 145 damage_buffer_size_ = size; |
| 146 damage_buffer_scale_factor_ = scale_factor; |
| 147 } |
| 148 |
| 149 void BrowserPluginHost::ResizeGuest(int instance_id, |
| 150 TransportDIB* damage_buffer, |
| 151 int width, int height, |
| 152 bool resize_pending, |
| 153 float scale_factor) { |
| 154 BrowserPluginHost* guest = GetGuestByInstanceID(instance_id); |
| 155 if (!guest) |
| 156 return; |
| 157 WebContentsImpl* guest_web_contents = |
| 158 guest ? static_cast<WebContentsImpl*>(guest->web_contents()): NULL; |
| 159 guest->SetDamageBuffer(damage_buffer, gfx::Size(width, height), scale_factor); |
| 160 if (!resize_pending) |
| 161 guest_web_contents->GetView()->SizeContents(gfx::Size(width, height)); |
| 162 } |
| 163 |
| 164 void BrowserPluginHost::UpdateRect( |
| 165 RenderViewHost* render_view_host, |
| 166 const ViewHostMsg_UpdateRect_Params& params) { |
| 167 // This handler is only of interest to us for the 2D software rendering path. |
| 168 // needs_ack should always be true for the 2D path. |
| 169 // TODO(fsamuel): Do we need to do something different in the 3D case? |
| 170 if (!params.needs_ack) |
| 171 return; |
| 172 |
| 173 // Only copy damage if the guest's view size is equal to the damage buffer's |
| 174 // size and the guest's scale factor is equal to the damage buffer's scale |
| 175 // factor. |
| 176 if (params.view_size.width() == damage_buffer_size().width() && |
| 177 params.view_size.height() == damage_buffer_size().height() && |
| 178 params.scale_factor == damage_buffer_scale_factor()) { |
| 179 TransportDIB* dib = render_view_host->GetProcess()-> |
| 180 GetTransportDIB(params.bitmap); |
| 181 if (dib) { |
| 182 void* guest_memory = dib->memory(); |
| 183 void* embedder_memory = damage_buffer_->memory(); |
| 184 int size = std::min(dib->size(), damage_buffer_->size()); |
| 185 memcpy(embedder_memory, guest_memory, size); |
| 186 } |
| 187 } |
| 188 DCHECK(embedder_render_process_host()); |
| 189 BrowserPluginMsg_UpdateRect_Params relay_params; |
| 190 relay_params.bitmap_rect = params.bitmap_rect; |
| 191 relay_params.dx = params.dx; |
| 192 relay_params.dy = params.dy; |
| 193 relay_params.scroll_rect = params.scroll_rect; |
| 194 relay_params.copy_rects = params.copy_rects; |
| 195 relay_params.view_size = params.view_size; |
| 196 relay_params.scale_factor = params.scale_factor; |
| 197 relay_params.is_resize_ack = ViewHostMsg_UpdateRect_Flags::is_resize_ack( |
| 198 params.flags); |
| 199 |
| 200 // We need to send the ACK to the same render_view_host that issued |
| 201 // the UpdateRect. We keep track of this correspondence via a message_id. |
| 202 int message_id = pending_update_counter_++; |
| 203 pending_updates_.AddWithID(render_view_host, message_id); |
| 204 |
| 205 gfx::Size param_size = gfx::Size( |
| 206 params.view_size.width(), |
| 207 params.view_size.height()); |
| 208 content::NotificationService::current()->Notify( |
| 209 NOTIFICATION_BROWSER_PLUGIN_UPDATE_RECT, |
| 210 content::Source<BrowserPluginHost>(this), |
| 211 content::Details<const gfx::Size>(¶m_size)); |
| 212 |
| 213 embedder_render_process_host()->Send( |
| 214 new BrowserPluginMsg_UpdateRect(instance_id(), |
| 215 message_id, |
| 216 relay_params)); |
| 217 } |
| 218 |
| 219 void BrowserPluginHost::UpdateRectACK(int message_id, |
| 220 const gfx::Size& size) { |
| 221 RenderViewHost* render_view_host = pending_updates_.Lookup(message_id); |
| 222 // If the guest has crashed since it sent the initial ViewHostMsg_UpdateRect |
| 223 // then the pending_updates_ map will have been cleared. |
| 224 if (!render_view_host) |
| 225 return; |
| 226 pending_updates_.Remove(message_id); |
| 227 render_view_host->Send( |
| 228 new ViewMsg_UpdateRect_ACK(render_view_host->GetRoutingID())); |
| 229 if (!size.IsEmpty()) |
| 230 render_view_host->GetView()->SetSize(size); |
| 231 } |
| 232 |
| 233 void BrowserPluginHost::HandleInputEvent( |
| 234 RenderViewHost* render_view_host, |
| 235 const gfx::Rect& guest_rect, |
| 236 const WebKit::WebInputEvent& event, |
| 237 IPC::Message* reply_message) { |
| 238 guest_rect_ = guest_rect; |
| 239 RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>( |
| 240 web_contents()->GetRenderViewHost()); |
| 241 IPC::Message* message = new ViewMsg_HandleInputEvent( |
| 242 guest_rvh->GetRoutingID()); |
| 243 |
| 244 // Copy the WebInputEvent and modify the event type. The guest expects |
| 245 // WebInputEvent::RawKeyDowns and not KeyDowns. |
| 246 scoped_array<char> input_buffer(new char[event.size]); |
| 247 memcpy(input_buffer.get(), &event, event.size); |
| 248 WebKit::WebInputEvent* input_event = |
| 249 reinterpret_cast<WebKit::WebInputEvent*>(input_buffer.get()); |
| 250 if (event.type == WebKit::WebInputEvent::KeyDown) |
| 251 input_event->type = WebKit::WebInputEvent::RawKeyDown; |
| 252 |
| 253 message->WriteData(input_buffer.get(), event.size); |
| 254 // TODO(fsamuel): What do we need to do here? This is for keyboard shortcuts. |
| 255 if (input_event->type == WebKit::WebInputEvent::RawKeyDown) |
| 256 message->WriteBool(false); |
| 257 guest_rvh->Send(message); |
| 258 |
| 259 DCHECK(!pending_input_event_reply_.get()); |
| 260 pending_input_event_reply_.reset(reply_message); |
| 261 guest_rvh->StartHangMonitorTimeout( |
| 262 base::TimeDelta::FromMilliseconds(kGuestHangTimeout)); |
| 263 } |
| 264 |
| 265 void BrowserPluginHost::HandleInputEventAck(RenderViewHost* render_view_host, |
| 266 bool handled) { |
| 267 DCHECK(pending_input_event_reply_.get()); |
| 268 IPC::Message* reply_message = pending_input_event_reply_.release(); |
| 269 BrowserPluginHostMsg_HandleInputEvent::WriteReplyParams(reply_message, |
| 270 handled, |
| 271 cursor_); |
| 272 embedder_render_process_host()->Send(reply_message); |
| 273 RenderViewHostImpl* guest_rvh = |
| 274 static_cast<RenderViewHostImpl*>(render_view_host); |
| 275 guest_rvh->StopHangMonitorTimeout(); |
| 276 } |
| 277 |
| 278 void BrowserPluginHost::SetFocus(bool focused) { |
| 279 RenderViewHost* render_view_host = web_contents()->GetRenderViewHost(); |
| 280 render_view_host->Send( |
| 281 new ViewMsg_SetFocus(render_view_host->GetRoutingID(), focused)); |
| 282 } |
| 283 |
| 284 void BrowserPluginHost::ShowWidget(RenderViewHost* render_view_host, |
| 285 int route_id, |
| 286 const gfx::Rect& initial_pos) { |
| 287 RenderWidgetHostView* popup_rwhv = |
| 288 static_cast<WebContentsImpl*>(web_contents())-> |
| 289 GetCreatedWidget(route_id); |
| 290 |
| 291 RenderWidgetHostViewPort* widget_host_view = |
| 292 RenderWidgetHostViewPort::FromRWHV(popup_rwhv); |
| 293 if (!widget_host_view) |
| 294 return; |
| 295 gfx::Rect screen_pos(initial_pos); |
| 296 screen_pos.Offset(guest_rect_.origin()); |
| 297 widget_host_view->InitAsPopup(web_contents()->GetRenderWidgetHostView(), |
| 298 screen_pos); |
| 299 RenderWidgetHostImpl* render_widget_host_impl = |
| 300 RenderWidgetHostImpl::From(widget_host_view->GetRenderWidgetHost()); |
| 301 render_widget_host_impl->Init(); |
| 302 render_widget_host_impl->set_allow_privileged_mouse_lock(false); |
| 303 } |
| 304 |
| 305 void BrowserPluginHost::SetCursor(const WebCursor& cursor) { |
| 306 cursor_ = cursor; |
| 307 } |
| 308 |
| 309 void BrowserPluginHost::DestroyGuests() { |
| 310 for (GuestMap::const_iterator it = guests_.begin(); |
| 311 it != guests_.end(); ++it) { |
| 312 WebContents* web_contents = it->first; |
| 313 delete web_contents; |
| 314 } |
| 315 guests_.clear(); |
| 316 guests_by_instance_id_.clear(); |
| 317 } |
| 318 |
| 319 void BrowserPluginHost::DestroyGuestByInstanceID(int instance_id) { |
| 320 |
| 321 BrowserPluginHost* guest = GetGuestByInstanceID(instance_id); |
| 322 if (!guest) |
| 323 return; |
| 324 WebContents* guest_web_contents = guest->web_contents(); |
| 325 DCHECK(guest_web_contents); |
| 326 delete guest_web_contents; |
| 327 |
| 328 GuestMap::iterator guest_it = guests_.find(guest_web_contents); |
| 329 DCHECK(guest_it != guests_.end()); |
| 330 |
| 331 guests_.erase(guest_it); |
| 332 guests_by_instance_id_.erase(instance_id); |
| 333 } |
| 334 |
| 335 void BrowserPluginHost::DidCommitProvisionalLoadForFrame( |
| 336 int64 frame_id, |
| 337 bool is_main_frame, |
| 338 const GURL& url, |
| 339 PageTransition transition_type, |
| 340 RenderViewHost* render_view_host) { |
| 341 // Clean-up guests that lie in the frame that we're navigating. |
| 342 typedef std::set<WebContents*> GuestSet; |
| 343 GuestSet guests_to_delete; |
| 344 for (GuestMap::const_iterator it = guests_.begin(); |
| 345 it != guests_.end(); ++it) { |
| 346 WebContents* web_contents = it->first; |
| 347 if (it->second == frame_id) { |
| 348 guests_to_delete.insert(web_contents); |
| 349 } |
| 350 } |
| 351 for (GuestSet::const_iterator it = guests_to_delete.begin(); |
| 352 it != guests_to_delete.end(); ++it) { |
| 353 int instance_id = static_cast<WebContentsImpl*>(*it)-> |
| 354 browser_plugin_host()->instance_id(); |
| 355 DestroyGuestByInstanceID(instance_id); |
| 356 } |
| 357 // If this is a guest, inform its embedder of the updated URL. |
| 358 // TODO(creis, fsamuel): Ensure this is safe/secure. |
| 359 if (is_main_frame && embedder_render_process_host()) { |
| 360 embedder_render_process_host()->Send( |
| 361 new BrowserPluginMsg_DidNavigate(instance_id(), url)); |
| 362 } |
| 363 } |
| 364 |
| 365 void BrowserPluginHost::RenderViewDeleted(RenderViewHost* render_view_host) { |
| 366 DestroyGuests(); |
| 367 } |
| 368 |
| 369 void BrowserPluginHost::RenderViewGone(base::TerminationStatus status) { |
| 370 DestroyGuests(); |
| 371 if (embedder_render_process_host()) { |
| 372 if (pending_input_event_reply_.get()) { |
| 373 IPC::Message* reply_message = pending_input_event_reply_.release(); |
| 374 BrowserPluginHostMsg_HandleInputEvent::WriteReplyParams(reply_message, |
| 375 false, |
| 376 cursor_); |
| 377 embedder_render_process_host()->Send(reply_message); |
| 378 } |
| 379 embedder_render_process_host()->Send( |
| 380 new BrowserPluginMsg_GuestCrashed(instance_id())); |
| 381 IDMap<RenderViewHost>::const_iterator iter(&pending_updates_); |
| 382 while (!iter.IsAtEnd()) { |
| 383 pending_updates_.Remove(iter.GetCurrentKey()); |
| 384 iter.Advance(); |
| 385 } |
| 386 content::NotificationService::current()->Notify( |
| 387 NOTIFICATION_BROWSER_PLUGIN_GUEST_CRASHED, |
| 388 content::Source<BrowserPluginHost>(this), |
| 389 content::NotificationService::NoDetails()); |
| 390 } |
| 391 } |
| 392 |
| 393 void BrowserPluginHost::Observe( |
| 394 int type, |
| 395 const NotificationSource& source, |
| 396 const NotificationDetails& details) { |
| 397 switch (type) { |
| 398 case NOTIFICATION_WEB_CONTENTS_RENDER_VIEW_HOST_CREATED: { |
| 399 RenderViewHost* render_view_host = |
| 400 Details<RenderViewHost>(details).ptr(); |
| 401 // BrowserPluginHostHelper is destroyed when its associated RenderViewHost |
| 402 // is destroyed. |
| 403 new BrowserPluginHostHelper(this, render_view_host); |
| 404 break; |
| 405 } |
| 406 case NOTIFICATION_WEB_CONTENTS_VISIBILITY_CHANGED: { |
| 407 bool visible = *Details<bool>(details).ptr(); |
| 408 // If the embedder is hidden we need to hide the guests as well. |
| 409 for (GuestMap::const_iterator it = guests_.begin(); |
| 410 it != guests_.end(); ++it) { |
| 411 WebContents* web_contents = it->first; |
| 412 if (visible) |
| 413 web_contents->WasShown(); |
| 414 else |
| 415 web_contents->WasHidden(); |
| 416 } |
| 417 break; |
| 418 } |
| 419 default: |
| 420 NOTREACHED() << "Unexpected notification type: " << type; |
| 421 } |
| 422 } |
| 423 |
| 424 } // namespace content |
OLD | NEW |