1 // Copyright 2014 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/renderer/child_frame_compositing_helper.h"
6
7 #include "cc/layers/delegated_frame_provider.h"
8 #include "cc/layers/delegated_frame_resource_collection.h"
9 #include "cc/layers/delegated_renderer_layer.h"
10 #include "cc/layers/solid_color_layer.h"
11 #include "cc/layers/texture_layer.h"
12 #include "cc/output/context_provider.h"
13 #include "cc/output/copy_output_request.h"
14 #include "cc/output/copy_output_result.h"
15 #include "cc/resources/single_release_callback.h"
16 #include "content/common/browser_plugin/browser_plugin_messages.h"
17 #include "content/common/frame_messages.h"
18 #include "content/common/gpu/client/context_provider_command_buffer.h"
19 #include "content/renderer/browser_plugin/browser_plugin.h"
20 #include "content/renderer/browser_plugin/browser_plugin_manager.h"
21 #include "content/renderer/compositor_bindings/web_layer_impl.h"
22 #include "content/renderer/render_frame_impl.h"
23 #include "content/renderer/render_frame_proxy.h"
24 #include "content/renderer/render_thread_impl.h"
25 #include "skia/ext/image_operations.h"
26 #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h"
27 #include "third_party/WebKit/public/web/WebFrame.h"
28 #include "third_party/WebKit/public/web/WebPluginContainer.h"
29 #include "third_party/khronos/GLES2/gl2.h"
30 #include "ui/gfx/size_conversions.h"
31 #include "ui/gfx/skia_util.h"
32
33 namespace content {
34
SwapBuffersInfo()35 ChildFrameCompositingHelper::SwapBuffersInfo::SwapBuffersInfo()
36 : route_id(0),
37 output_surface_id(0),
38 host_id(0),
39 software_frame_id(0),
40 shared_memory(NULL) {}
41
42 ChildFrameCompositingHelper*
CreateCompositingHelperForBrowserPlugin(const base::WeakPtr<BrowserPlugin> & browser_plugin)43 ChildFrameCompositingHelper::CreateCompositingHelperForBrowserPlugin(
44 const base::WeakPtr<BrowserPlugin>& browser_plugin) {
45 return new ChildFrameCompositingHelper(
46 browser_plugin, NULL, NULL, browser_plugin->render_view_routing_id());
47 }
48
49 ChildFrameCompositingHelper*
CreateCompositingHelperForRenderFrame(blink::WebFrame * frame,RenderFrameProxy * render_frame_proxy,int host_routing_id)50 ChildFrameCompositingHelper::CreateCompositingHelperForRenderFrame(
51 blink::WebFrame* frame,
52 RenderFrameProxy* render_frame_proxy,
53 int host_routing_id) {
54 return new ChildFrameCompositingHelper(
55 base::WeakPtr<BrowserPlugin>(), frame, render_frame_proxy,
56 host_routing_id);
57 }
58
ChildFrameCompositingHelper(const base::WeakPtr<BrowserPlugin> & browser_plugin,blink::WebFrame * frame,RenderFrameProxy * render_frame_proxy,int host_routing_id)59 ChildFrameCompositingHelper::ChildFrameCompositingHelper(
60 const base::WeakPtr<BrowserPlugin>& browser_plugin,
61 blink::WebFrame* frame,
62 RenderFrameProxy* render_frame_proxy,
63 int host_routing_id)
64 : host_routing_id_(host_routing_id),
65 last_route_id_(0),
66 last_output_surface_id_(0),
67 last_host_id_(0),
68 last_mailbox_valid_(false),
69 ack_pending_(true),
70 software_ack_pending_(false),
71 opaque_(true),
72 browser_plugin_(browser_plugin),
73 render_frame_proxy_(render_frame_proxy),
74 frame_(frame) {}
75
~ChildFrameCompositingHelper()76 ChildFrameCompositingHelper::~ChildFrameCompositingHelper() {}
77
GetBrowserPluginManager()78 BrowserPluginManager* ChildFrameCompositingHelper::GetBrowserPluginManager() {
79 if (!browser_plugin_)
80 return NULL;
81
82 return browser_plugin_->browser_plugin_manager();
83 }
84
GetContainer()85 blink::WebPluginContainer* ChildFrameCompositingHelper::GetContainer() {
86 if (!browser_plugin_)
87 return NULL;
88
89 return browser_plugin_->container();
90 }
91
GetInstanceID()92 int ChildFrameCompositingHelper::GetInstanceID() {
93 if (!browser_plugin_)
94 return 0;
95
96 return browser_plugin_->guest_instance_id();
97 }
98
SendCompositorFrameSwappedACKToBrowser(FrameHostMsg_CompositorFrameSwappedACK_Params & params)99 void ChildFrameCompositingHelper::SendCompositorFrameSwappedACKToBrowser(
100 FrameHostMsg_CompositorFrameSwappedACK_Params& params) {
101 // This function will be removed when BrowserPluginManager is removed and
102 // BrowserPlugin is modified to use a RenderFrame.
103 if (GetBrowserPluginManager()) {
104 GetBrowserPluginManager()->Send(
105 new BrowserPluginHostMsg_CompositorFrameSwappedACK(
106 host_routing_id_, GetInstanceID(), params));
107 } else if (render_frame_proxy_) {
108 render_frame_proxy_->Send(
109 new FrameHostMsg_CompositorFrameSwappedACK(host_routing_id_, params));
110 }
111 }
112
SendBuffersSwappedACKToBrowser(FrameHostMsg_BuffersSwappedACK_Params & params)113 void ChildFrameCompositingHelper::SendBuffersSwappedACKToBrowser(
114 FrameHostMsg_BuffersSwappedACK_Params& params) {
115 // This function will be removed when BrowserPluginManager is removed and
116 // BrowserPlugin is modified to use a RenderFrame.
117 if (GetBrowserPluginManager()) {
118 GetBrowserPluginManager()->Send(new BrowserPluginHostMsg_BuffersSwappedACK(
119 host_routing_id_, params));
120 } else if (render_frame_proxy_) {
121 render_frame_proxy_->Send(
122 new FrameHostMsg_BuffersSwappedACK(host_routing_id_, params));
123 }
124 }
125
SendReclaimCompositorResourcesToBrowser(FrameHostMsg_ReclaimCompositorResources_Params & params)126 void ChildFrameCompositingHelper::SendReclaimCompositorResourcesToBrowser(
127 FrameHostMsg_ReclaimCompositorResources_Params& params) {
128 // This function will be removed when BrowserPluginManager is removed and
129 // BrowserPlugin is modified to use a RenderFrame.
130 if (GetBrowserPluginManager()) {
131 GetBrowserPluginManager()->Send(
132 new BrowserPluginHostMsg_ReclaimCompositorResources(
133 host_routing_id_, GetInstanceID(), params));
134 } else if (render_frame_proxy_) {
135 render_frame_proxy_->Send(
136 new FrameHostMsg_ReclaimCompositorResources(host_routing_id_, params));
137 }
138 }
139
CopyFromCompositingSurface(int request_id,gfx::Rect source_rect,gfx::Size dest_size)140 void ChildFrameCompositingHelper::CopyFromCompositingSurface(
141 int request_id,
142 gfx::Rect source_rect,
143 gfx::Size dest_size) {
144 CHECK(background_layer_);
145 scoped_ptr<cc::CopyOutputRequest> request =
146 cc::CopyOutputRequest::CreateBitmapRequest(base::Bind(
147 &ChildFrameCompositingHelper::CopyFromCompositingSurfaceHasResult,
148 this,
149 request_id,
150 dest_size));
151 request->set_area(source_rect);
152 background_layer_->RequestCopyOfOutput(request.Pass());
153 }
154
DidCommitCompositorFrame()155 void ChildFrameCompositingHelper::DidCommitCompositorFrame() {
156 if (software_ack_pending_) {
157 FrameHostMsg_CompositorFrameSwappedACK_Params params;
158 params.producing_host_id = last_host_id_;
159 params.producing_route_id = last_route_id_;
160 params.output_surface_id = last_output_surface_id_;
161 if (!unacked_software_frames_.empty()) {
162 params.ack.last_software_frame_id = unacked_software_frames_.back();
163 unacked_software_frames_.pop_back();
164 }
165
166 SendCompositorFrameSwappedACKToBrowser(params);
167
168 software_ack_pending_ = false;
169 }
170 if (!resource_collection_.get() || !ack_pending_)
171 return;
172
173 FrameHostMsg_CompositorFrameSwappedACK_Params params;
174 params.producing_host_id = last_host_id_;
175 params.producing_route_id = last_route_id_;
176 params.output_surface_id = last_output_surface_id_;
177 resource_collection_->TakeUnusedResourcesForChildCompositor(
178 ¶ms.ack.resources);
179
180 SendCompositorFrameSwappedACKToBrowser(params);
181
182 ack_pending_ = false;
183 }
184
EnableCompositing(bool enable)185 void ChildFrameCompositingHelper::EnableCompositing(bool enable) {
186 if (enable && !background_layer_.get()) {
187 background_layer_ = cc::SolidColorLayer::Create();
188 background_layer_->SetMasksToBounds(true);
189 background_layer_->SetBackgroundColor(
190 SkColorSetARGBInline(255, 255, 255, 255));
191 web_layer_.reset(new WebLayerImpl(background_layer_));
192 }
193
194 if (GetContainer()) {
195 GetContainer()->setWebLayer(enable ? web_layer_.get() : NULL);
196 } else if (frame_) {
197 frame_->setRemoteWebLayer(enable ? web_layer_.get() : NULL);
198 }
199 }
200
CheckSizeAndAdjustLayerProperties(const gfx::Size & new_size,float device_scale_factor,cc::Layer * layer)201 void ChildFrameCompositingHelper::CheckSizeAndAdjustLayerProperties(
202 const gfx::Size& new_size,
203 float device_scale_factor,
204 cc::Layer* layer) {
205 if (buffer_size_ != new_size) {
206 buffer_size_ = new_size;
207 // The container size is in DIP, so is the layer size.
208 // Buffer size is in physical pixels, so we need to adjust
209 // it by the device scale factor.
210 gfx::Size device_scale_adjusted_size = gfx::ToFlooredSize(
211 gfx::ScaleSize(buffer_size_, 1.0f / device_scale_factor));
212 layer->SetBounds(device_scale_adjusted_size);
213 }
214
215 // Manually manage background layer for transparent webview.
216 if (!opaque_)
217 background_layer_->SetIsDrawable(false);
218 }
219
MailboxReleased(SwapBuffersInfo mailbox,uint32 sync_point,bool lost_resource)220 void ChildFrameCompositingHelper::MailboxReleased(SwapBuffersInfo mailbox,
221 uint32 sync_point,
222 bool lost_resource) {
223 if (mailbox.type == SOFTWARE_COMPOSITOR_FRAME) {
224 delete mailbox.shared_memory;
225 mailbox.shared_memory = NULL;
226 } else if (lost_resource) {
227 // Reset mailbox's name if the resource was lost.
228 mailbox.name.SetZero();
229 }
230
231 // This means the GPU process crashed or guest crashed.
232 if (last_host_id_ != mailbox.host_id ||
233 last_output_surface_id_ != mailbox.output_surface_id ||
234 last_route_id_ != mailbox.route_id)
235 return;
236
237 if (mailbox.type == SOFTWARE_COMPOSITOR_FRAME)
238 unacked_software_frames_.push_back(mailbox.software_frame_id);
239
240 // We need to send an ACK to for every buffer sent to us.
241 // However, if a buffer is freed up from
242 // the compositor in cases like switching back to SW mode without a new
243 // buffer arriving, no ACK is needed.
244 if (!ack_pending_) {
245 last_mailbox_valid_ = false;
246 return;
247 }
248 ack_pending_ = false;
249 switch (mailbox.type) {
250 case TEXTURE_IMAGE_TRANSPORT: {
251 FrameHostMsg_BuffersSwappedACK_Params params;
252 params.gpu_host_id = mailbox.host_id;
253 params.gpu_route_id = mailbox.route_id;
254 params.mailbox = mailbox.name;
255 params.sync_point = sync_point;
256 SendBuffersSwappedACKToBrowser(params);
257 break;
258 }
259 case GL_COMPOSITOR_FRAME: {
260 FrameHostMsg_CompositorFrameSwappedACK_Params params;
261 params.producing_host_id = mailbox.host_id;
262 params.producing_route_id = mailbox.route_id;
263 params.output_surface_id = mailbox.output_surface_id;
264 params.ack.gl_frame_data.reset(new cc::GLFrameData());
265 params.ack.gl_frame_data->mailbox = mailbox.name;
266 params.ack.gl_frame_data->size = mailbox.size;
267 params.ack.gl_frame_data->sync_point = sync_point;
268 SendCompositorFrameSwappedACKToBrowser(params);
269 break;
270 }
271 case SOFTWARE_COMPOSITOR_FRAME:
272 break;
273 }
274 }
275
OnContainerDestroy()276 void ChildFrameCompositingHelper::OnContainerDestroy() {
277 if (GetContainer())
278 GetContainer()->setWebLayer(NULL);
279
280 if (resource_collection_)
281 resource_collection_->SetClient(NULL);
282
283 ack_pending_ = false;
284 software_ack_pending_ = false;
285 resource_collection_ = NULL;
286 frame_provider_ = NULL;
287 texture_layer_ = NULL;
288 delegated_layer_ = NULL;
289 background_layer_ = NULL;
290 web_layer_.reset();
291 }
292
ChildFrameGone()293 void ChildFrameCompositingHelper::ChildFrameGone() {
294 background_layer_->SetBackgroundColor(SkColorSetARGBInline(255, 0, 128, 0));
295 background_layer_->RemoveAllChildren();
296 background_layer_->SetIsDrawable(true);
297 background_layer_->SetContentsOpaque(true);
298 }
299
OnBuffersSwappedPrivate(const SwapBuffersInfo & mailbox,uint32 sync_point,float device_scale_factor)300 void ChildFrameCompositingHelper::OnBuffersSwappedPrivate(
301 const SwapBuffersInfo& mailbox,
302 uint32 sync_point,
303 float device_scale_factor) {
304 DCHECK(!delegated_layer_.get());
305 // If these mismatch, we are either just starting up, GPU process crashed or
306 // guest renderer crashed.
307 // In this case, we are communicating with a new image transport
308 // surface and must ACK with the new ID's and an empty mailbox.
309 if (last_route_id_ != mailbox.route_id ||
310 last_output_surface_id_ != mailbox.output_surface_id ||
311 last_host_id_ != mailbox.host_id)
312 last_mailbox_valid_ = false;
313
314 last_route_id_ = mailbox.route_id;
315 last_output_surface_id_ = mailbox.output_surface_id;
316 last_host_id_ = mailbox.host_id;
317
318 ack_pending_ = true;
319 // Browser plugin getting destroyed, do a fast ACK.
320 if (!background_layer_.get()) {
321 MailboxReleased(mailbox, sync_point, false);
322 return;
323 }
324
325 if (!texture_layer_.get()) {
326 texture_layer_ = cc::TextureLayer::CreateForMailbox(NULL);
327 texture_layer_->SetIsDrawable(true);
328 SetContentsOpaque(opaque_);
329
330 background_layer_->AddChild(texture_layer_);
331 }
332
333 // The size of browser plugin container is not always equal to the size
334 // of the buffer that arrives here. This could be for a number of reasons,
335 // including autosize and a resize in progress.
336 // During resize, the container size changes first and then some time
337 // later, a new buffer with updated size will arrive. During this process,
338 // we need to make sure that things are still displayed pixel perfect.
339 // We accomplish this by modifying bounds of the texture layer only
340 // when a new buffer arrives.
341 // Visually, this will either display a smaller part of the buffer
342 // or introduce a gutter around it.
343 CheckSizeAndAdjustLayerProperties(
344 mailbox.size, device_scale_factor, texture_layer_.get());
345
346 bool is_software_frame = mailbox.type == SOFTWARE_COMPOSITOR_FRAME;
347 bool current_mailbox_valid = is_software_frame ? mailbox.shared_memory != NULL
348 : !mailbox.name.IsZero();
349 if (!is_software_frame && !last_mailbox_valid_) {
350 SwapBuffersInfo empty_info = mailbox;
351 empty_info.name.SetZero();
352 MailboxReleased(empty_info, 0, false);
353 if (!current_mailbox_valid)
354 return;
355 }
356
357 cc::TextureMailbox texture_mailbox;
358 scoped_ptr<cc::SingleReleaseCallback> release_callback;
359 if (current_mailbox_valid) {
360 release_callback =
361 cc::SingleReleaseCallback::Create(
362 base::Bind(&ChildFrameCompositingHelper::MailboxReleased,
363 scoped_refptr<ChildFrameCompositingHelper>(this),
364 mailbox)).Pass();
365 if (is_software_frame) {
366 texture_mailbox = cc::TextureMailbox(mailbox.shared_memory, mailbox.size);
367 } else {
368 texture_mailbox =
369 cc::TextureMailbox(mailbox.name, GL_TEXTURE_2D, sync_point);
370 }
371 }
372
373 texture_layer_->SetFlipped(!is_software_frame);
374 texture_layer_->SetTextureMailbox(texture_mailbox, release_callback.Pass());
375 texture_layer_->SetNeedsDisplay();
376 last_mailbox_valid_ = current_mailbox_valid;
377 }
378
OnBuffersSwapped(const gfx::Size & size,const gpu::Mailbox & mailbox,int gpu_route_id,int gpu_host_id,float device_scale_factor)379 void ChildFrameCompositingHelper::OnBuffersSwapped(
380 const gfx::Size& size,
381 const gpu::Mailbox& mailbox,
382 int gpu_route_id,
383 int gpu_host_id,
384 float device_scale_factor) {
385 SwapBuffersInfo swap_info;
386 swap_info.name = mailbox;
387 swap_info.type = TEXTURE_IMAGE_TRANSPORT;
388 swap_info.size = size;
389 swap_info.route_id = gpu_route_id;
390 swap_info.output_surface_id = 0;
391 swap_info.host_id = gpu_host_id;
392 OnBuffersSwappedPrivate(swap_info, 0, device_scale_factor);
393 }
394
OnCompositorFrameSwapped(scoped_ptr<cc::CompositorFrame> frame,int route_id,uint32 output_surface_id,int host_id,base::SharedMemoryHandle handle)395 void ChildFrameCompositingHelper::OnCompositorFrameSwapped(
396 scoped_ptr<cc::CompositorFrame> frame,
397 int route_id,
398 uint32 output_surface_id,
399 int host_id,
400 base::SharedMemoryHandle handle) {
401
402 if (frame->gl_frame_data) {
403 SwapBuffersInfo swap_info;
404 swap_info.name = frame->gl_frame_data->mailbox;
405 swap_info.type = GL_COMPOSITOR_FRAME;
406 swap_info.size = frame->gl_frame_data->size;
407 swap_info.route_id = route_id;
408 swap_info.output_surface_id = output_surface_id;
409 swap_info.host_id = host_id;
410 OnBuffersSwappedPrivate(swap_info,
411 frame->gl_frame_data->sync_point,
412 frame->metadata.device_scale_factor);
413 return;
414 }
415
416 if (frame->software_frame_data) {
417 cc::SoftwareFrameData* frame_data = frame->software_frame_data.get();
418
419 SwapBuffersInfo swap_info;
420 swap_info.type = SOFTWARE_COMPOSITOR_FRAME;
421 swap_info.size = frame_data->size;
422 swap_info.route_id = route_id;
423 swap_info.output_surface_id = output_surface_id;
424 swap_info.host_id = host_id;
425 swap_info.software_frame_id = frame_data->id;
426
427 scoped_ptr<base::SharedMemory> shared_memory(
428 new base::SharedMemory(handle, true));
429 const size_t size_in_bytes = 4 * frame_data->size.GetArea();
430 if (!shared_memory->Map(size_in_bytes)) {
431 LOG(ERROR) << "Failed to map shared memory of size " << size_in_bytes;
432 // Send ACK right away.
433 software_ack_pending_ = true;
434 MailboxReleased(swap_info, 0, false);
435 DidCommitCompositorFrame();
436 return;
437 }
438
439 swap_info.shared_memory = shared_memory.release();
440 OnBuffersSwappedPrivate(swap_info, 0, frame->metadata.device_scale_factor);
441 software_ack_pending_ = true;
442 last_route_id_ = route_id;
443 last_output_surface_id_ = output_surface_id;
444 last_host_id_ = host_id;
445 return;
446 }
447
448 DCHECK(!texture_layer_.get());
449
450 cc::DelegatedFrameData* frame_data = frame->delegated_frame_data.get();
451 // Do nothing if we are getting destroyed or have no frame data.
452 if (!frame_data || !background_layer_)
453 return;
454
455 DCHECK(!frame_data->render_pass_list.empty());
456 cc::RenderPass* root_pass = frame_data->render_pass_list.back();
457 gfx::Size frame_size = root_pass->output_rect.size();
458
459 if (last_route_id_ != route_id ||
460 last_output_surface_id_ != output_surface_id ||
461 last_host_id_ != host_id) {
462 // Resource ids are scoped by the output surface.
463 // If the originating output surface doesn't match the last one, it
464 // indicates the guest's output surface may have been recreated, in which
465 // case we should recreate the DelegatedRendererLayer, to avoid matching
466 // resources from the old one with resources from the new one which would
467 // have the same id.
468 frame_provider_ = NULL;
469
470 // Drop the cc::DelegatedFrameResourceCollection so that we will not return
471 // any resources from the old output surface with the new output surface id.
472 if (resource_collection_) {
473 resource_collection_->SetClient(NULL);
474
475 if (resource_collection_->LoseAllResources())
476 SendReturnedDelegatedResources();
477 resource_collection_ = NULL;
478 }
479 last_output_surface_id_ = output_surface_id;
480 last_route_id_ = route_id;
481 last_host_id_ = host_id;
482 }
483 if (!resource_collection_) {
484 resource_collection_ = new cc::DelegatedFrameResourceCollection;
485 resource_collection_->SetClient(this);
486 }
487 if (!frame_provider_.get() || frame_provider_->frame_size() != frame_size) {
488 frame_provider_ = new cc::DelegatedFrameProvider(
489 resource_collection_.get(), frame->delegated_frame_data.Pass());
490 if (delegated_layer_.get())
491 delegated_layer_->RemoveFromParent();
492 delegated_layer_ =
493 cc::DelegatedRendererLayer::Create(frame_provider_.get());
494 delegated_layer_->SetIsDrawable(true);
495 SetContentsOpaque(opaque_);
496 background_layer_->AddChild(delegated_layer_);
497 } else {
498 frame_provider_->SetFrameData(frame->delegated_frame_data.Pass());
499 }
500
501 CheckSizeAndAdjustLayerProperties(
502 frame_data->render_pass_list.back()->output_rect.size(),
503 frame->metadata.device_scale_factor,
504 delegated_layer_.get());
505
506 ack_pending_ = true;
507 }
508
UpdateVisibility(bool visible)509 void ChildFrameCompositingHelper::UpdateVisibility(bool visible) {
510 if (texture_layer_.get())
511 texture_layer_->SetIsDrawable(visible);
512 if (delegated_layer_.get())
513 delegated_layer_->SetIsDrawable(visible);
514 }
515
UnusedResourcesAreAvailable()516 void ChildFrameCompositingHelper::UnusedResourcesAreAvailable() {
517 if (ack_pending_)
518 return;
519
520 SendReturnedDelegatedResources();
521 }
522
SendReturnedDelegatedResources()523 void ChildFrameCompositingHelper::SendReturnedDelegatedResources() {
524 FrameHostMsg_ReclaimCompositorResources_Params params;
525 if (resource_collection_)
526 resource_collection_->TakeUnusedResourcesForChildCompositor(
527 ¶ms.ack.resources);
528 DCHECK(!params.ack.resources.empty());
529
530 params.route_id = last_route_id_;
531 params.output_surface_id = last_output_surface_id_;
532 params.renderer_host_id = last_host_id_;
533 SendReclaimCompositorResourcesToBrowser(params);
534 }
535
SetContentsOpaque(bool opaque)536 void ChildFrameCompositingHelper::SetContentsOpaque(bool opaque) {
537 opaque_ = opaque;
538
539 if (texture_layer_.get())
540 texture_layer_->SetContentsOpaque(opaque_);
541 if (delegated_layer_.get())
542 delegated_layer_->SetContentsOpaque(opaque_);
543 }
544
CopyFromCompositingSurfaceHasResult(int request_id,gfx::Size dest_size,scoped_ptr<cc::CopyOutputResult> result)545 void ChildFrameCompositingHelper::CopyFromCompositingSurfaceHasResult(
546 int request_id,
547 gfx::Size dest_size,
548 scoped_ptr<cc::CopyOutputResult> result) {
549 scoped_ptr<SkBitmap> bitmap;
550 if (result && result->HasBitmap() && !result->size().IsEmpty())
551 bitmap = result->TakeBitmap();
552
553 SkBitmap resized_bitmap;
554 if (bitmap) {
555 resized_bitmap =
556 skia::ImageOperations::Resize(*bitmap,
557 skia::ImageOperations::RESIZE_BEST,
558 dest_size.width(),
559 dest_size.height());
560 }
561 if (GetBrowserPluginManager()) {
562 GetBrowserPluginManager()->Send(
563 new BrowserPluginHostMsg_CopyFromCompositingSurfaceAck(
564 host_routing_id_, GetInstanceID(), request_id, resized_bitmap));
565 }
566 }
567
568 } // namespace content
569