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