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/blink/web_layer_impl.h"
8#include "cc/layers/delegated_frame_provider.h"
9#include "cc/layers/delegated_frame_resource_collection.h"
10#include "cc/layers/delegated_renderer_layer.h"
11#include "cc/layers/solid_color_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/render_frame_impl.h"
22#include "content/renderer/render_frame_proxy.h"
23#include "content/renderer/render_thread_impl.h"
24#include "skia/ext/image_operations.h"
25#include "third_party/WebKit/public/web/WebFrame.h"
26#include "third_party/WebKit/public/web/WebPluginContainer.h"
27#include "third_party/khronos/GLES2/gl2.h"
28#include "ui/gfx/size_conversions.h"
29#include "ui/gfx/skia_util.h"
30
31namespace content {
32
33ChildFrameCompositingHelper*
34ChildFrameCompositingHelper::CreateForBrowserPlugin(
35    const base::WeakPtr<BrowserPlugin>& browser_plugin) {
36  return new ChildFrameCompositingHelper(
37      browser_plugin, NULL, NULL, browser_plugin->render_view_routing_id());
38}
39
40ChildFrameCompositingHelper*
41ChildFrameCompositingHelper::CreateForRenderFrameProxy(
42    RenderFrameProxy* render_frame_proxy) {
43  return new ChildFrameCompositingHelper(base::WeakPtr<BrowserPlugin>(),
44                                         render_frame_proxy->web_frame(),
45                                         render_frame_proxy,
46                                         render_frame_proxy->routing_id());
47}
48
49ChildFrameCompositingHelper::ChildFrameCompositingHelper(
50    const base::WeakPtr<BrowserPlugin>& browser_plugin,
51    blink::WebFrame* frame,
52    RenderFrameProxy* render_frame_proxy,
53    int host_routing_id)
54    : host_routing_id_(host_routing_id),
55      last_route_id_(0),
56      last_output_surface_id_(0),
57      last_host_id_(0),
58      ack_pending_(true),
59      opaque_(true),
60      browser_plugin_(browser_plugin),
61      render_frame_proxy_(render_frame_proxy),
62      frame_(frame) {}
63
64ChildFrameCompositingHelper::~ChildFrameCompositingHelper() {}
65
66BrowserPluginManager* ChildFrameCompositingHelper::GetBrowserPluginManager() {
67  if (!browser_plugin_)
68    return NULL;
69
70  return browser_plugin_->browser_plugin_manager();
71}
72
73blink::WebPluginContainer* ChildFrameCompositingHelper::GetContainer() {
74  if (!browser_plugin_)
75    return NULL;
76
77  return browser_plugin_->container();
78}
79
80int ChildFrameCompositingHelper::GetInstanceID() {
81  if (!browser_plugin_)
82    return 0;
83
84  return browser_plugin_->browser_plugin_instance_id();
85}
86
87void ChildFrameCompositingHelper::SendCompositorFrameSwappedACKToBrowser(
88    FrameHostMsg_CompositorFrameSwappedACK_Params& params) {
89  // This function will be removed when BrowserPluginManager is removed and
90  // BrowserPlugin is modified to use a RenderFrame.
91  if (GetBrowserPluginManager()) {
92    GetBrowserPluginManager()->Send(
93        new BrowserPluginHostMsg_CompositorFrameSwappedACK(
94            host_routing_id_, GetInstanceID(), params));
95  } else if (render_frame_proxy_) {
96    render_frame_proxy_->Send(
97        new FrameHostMsg_CompositorFrameSwappedACK(host_routing_id_, params));
98  }
99}
100
101void ChildFrameCompositingHelper::SendReclaimCompositorResourcesToBrowser(
102    FrameHostMsg_ReclaimCompositorResources_Params& params) {
103  // This function will be removed when BrowserPluginManager is removed and
104  // BrowserPlugin is modified to use a RenderFrame.
105  if (GetBrowserPluginManager()) {
106    GetBrowserPluginManager()->Send(
107        new BrowserPluginHostMsg_ReclaimCompositorResources(
108            host_routing_id_, GetInstanceID(), params));
109  } else if (render_frame_proxy_) {
110    render_frame_proxy_->Send(
111        new FrameHostMsg_ReclaimCompositorResources(host_routing_id_, params));
112  }
113}
114
115void ChildFrameCompositingHelper::CopyFromCompositingSurface(
116    int request_id,
117    gfx::Rect source_rect,
118    gfx::Size dest_size) {
119  CHECK(background_layer_.get());
120  scoped_ptr<cc::CopyOutputRequest> request =
121      cc::CopyOutputRequest::CreateBitmapRequest(base::Bind(
122          &ChildFrameCompositingHelper::CopyFromCompositingSurfaceHasResult,
123          this,
124          request_id,
125          dest_size));
126  request->set_area(source_rect);
127  background_layer_->RequestCopyOfOutput(request.Pass());
128}
129
130void ChildFrameCompositingHelper::DidCommitCompositorFrame() {
131  if (!resource_collection_.get() || !ack_pending_)
132    return;
133
134  FrameHostMsg_CompositorFrameSwappedACK_Params params;
135  params.producing_host_id = last_host_id_;
136  params.producing_route_id = last_route_id_;
137  params.output_surface_id = last_output_surface_id_;
138  resource_collection_->TakeUnusedResourcesForChildCompositor(
139      &params.ack.resources);
140
141  SendCompositorFrameSwappedACKToBrowser(params);
142
143  ack_pending_ = false;
144}
145
146void ChildFrameCompositingHelper::EnableCompositing(bool enable) {
147  if (enable && !background_layer_.get()) {
148    background_layer_ = cc::SolidColorLayer::Create();
149    background_layer_->SetMasksToBounds(true);
150    background_layer_->SetBackgroundColor(
151        SkColorSetARGBInline(255, 255, 255, 255));
152    web_layer_.reset(new cc_blink::WebLayerImpl(background_layer_));
153  }
154
155  if (GetContainer()) {
156    GetContainer()->setWebLayer(enable ? web_layer_.get() : NULL);
157  } else if (frame_) {
158    frame_->setRemoteWebLayer(enable ? web_layer_.get() : NULL);
159  }
160}
161
162void ChildFrameCompositingHelper::CheckSizeAndAdjustLayerProperties(
163    const gfx::Size& new_size,
164    float device_scale_factor,
165    cc::Layer* layer) {
166  if (buffer_size_ != new_size) {
167    buffer_size_ = new_size;
168    // The container size is in DIP, so is the layer size.
169    // Buffer size is in physical pixels, so we need to adjust
170    // it by the device scale factor.
171    gfx::Size device_scale_adjusted_size = gfx::ToFlooredSize(
172        gfx::ScaleSize(buffer_size_, 1.0f / device_scale_factor));
173    layer->SetBounds(device_scale_adjusted_size);
174  }
175
176  // Manually manage background layer for transparent webview.
177  if (!opaque_)
178    background_layer_->SetIsDrawable(false);
179}
180
181void ChildFrameCompositingHelper::OnContainerDestroy() {
182  if (GetContainer())
183    GetContainer()->setWebLayer(NULL);
184
185  if (resource_collection_.get())
186    resource_collection_->SetClient(NULL);
187
188  ack_pending_ = false;
189  resource_collection_ = NULL;
190  frame_provider_ = NULL;
191  delegated_layer_ = NULL;
192  background_layer_ = NULL;
193  web_layer_.reset();
194}
195
196void ChildFrameCompositingHelper::ChildFrameGone() {
197  background_layer_->SetBackgroundColor(SkColorSetARGBInline(255, 0, 128, 0));
198  background_layer_->RemoveAllChildren();
199  background_layer_->SetIsDrawable(true);
200  background_layer_->SetContentsOpaque(true);
201}
202
203void ChildFrameCompositingHelper::OnCompositorFrameSwapped(
204    scoped_ptr<cc::CompositorFrame> frame,
205    int route_id,
206    uint32 output_surface_id,
207    int host_id,
208    base::SharedMemoryHandle handle) {
209  cc::DelegatedFrameData* frame_data = frame->delegated_frame_data.get();
210  // Do nothing if we are getting destroyed or have no frame data.
211  if (!frame_data || !background_layer_.get())
212    return;
213
214  DCHECK(!frame_data->render_pass_list.empty());
215  cc::RenderPass* root_pass = frame_data->render_pass_list.back();
216  gfx::Size frame_size = root_pass->output_rect.size();
217
218  if (last_route_id_ != route_id ||
219      last_output_surface_id_ != output_surface_id ||
220      last_host_id_ != host_id) {
221    // Resource ids are scoped by the output surface.
222    // If the originating output surface doesn't match the last one, it
223    // indicates the guest's output surface may have been recreated, in which
224    // case we should recreate the DelegatedRendererLayer, to avoid matching
225    // resources from the old one with resources from the new one which would
226    // have the same id.
227    frame_provider_ = NULL;
228
229    // Drop the cc::DelegatedFrameResourceCollection so that we will not return
230    // any resources from the old output surface with the new output surface id.
231    if (resource_collection_.get()) {
232      resource_collection_->SetClient(NULL);
233
234      if (resource_collection_->LoseAllResources())
235        SendReturnedDelegatedResources();
236      resource_collection_ = NULL;
237    }
238    last_output_surface_id_ = output_surface_id;
239    last_route_id_ = route_id;
240    last_host_id_ = host_id;
241  }
242  if (!resource_collection_.get()) {
243    resource_collection_ = new cc::DelegatedFrameResourceCollection;
244    resource_collection_->SetClient(this);
245  }
246  if (!frame_provider_.get() || frame_provider_->frame_size() != frame_size) {
247    frame_provider_ = new cc::DelegatedFrameProvider(
248        resource_collection_.get(), frame->delegated_frame_data.Pass());
249    if (delegated_layer_.get())
250      delegated_layer_->RemoveFromParent();
251    delegated_layer_ =
252        cc::DelegatedRendererLayer::Create(frame_provider_.get());
253    delegated_layer_->SetIsDrawable(true);
254    buffer_size_ = gfx::Size();
255    SetContentsOpaque(opaque_);
256    background_layer_->AddChild(delegated_layer_);
257  } else {
258    frame_provider_->SetFrameData(frame->delegated_frame_data.Pass());
259  }
260
261  CheckSizeAndAdjustLayerProperties(
262      frame_data->render_pass_list.back()->output_rect.size(),
263      frame->metadata.device_scale_factor,
264      delegated_layer_.get());
265
266  ack_pending_ = true;
267}
268
269void ChildFrameCompositingHelper::UpdateVisibility(bool visible) {
270  if (delegated_layer_.get())
271    delegated_layer_->SetIsDrawable(visible);
272}
273
274void ChildFrameCompositingHelper::UnusedResourcesAreAvailable() {
275  if (ack_pending_)
276    return;
277
278  SendReturnedDelegatedResources();
279}
280
281void ChildFrameCompositingHelper::SendReturnedDelegatedResources() {
282  FrameHostMsg_ReclaimCompositorResources_Params params;
283  if (resource_collection_.get())
284    resource_collection_->TakeUnusedResourcesForChildCompositor(
285        &params.ack.resources);
286  DCHECK(!params.ack.resources.empty());
287
288  params.route_id = last_route_id_;
289  params.output_surface_id = last_output_surface_id_;
290  params.renderer_host_id = last_host_id_;
291  SendReclaimCompositorResourcesToBrowser(params);
292}
293
294void ChildFrameCompositingHelper::SetContentsOpaque(bool opaque) {
295  opaque_ = opaque;
296  if (delegated_layer_.get())
297    delegated_layer_->SetContentsOpaque(opaque_);
298}
299
300void ChildFrameCompositingHelper::CopyFromCompositingSurfaceHasResult(
301    int request_id,
302    gfx::Size dest_size,
303    scoped_ptr<cc::CopyOutputResult> result) {
304  scoped_ptr<SkBitmap> bitmap;
305  if (result && result->HasBitmap() && !result->size().IsEmpty())
306    bitmap = result->TakeBitmap();
307
308  SkBitmap resized_bitmap;
309  if (bitmap) {
310    resized_bitmap =
311        skia::ImageOperations::Resize(*bitmap,
312                                      skia::ImageOperations::RESIZE_BEST,
313                                      dest_size.width(),
314                                      dest_size.height());
315  }
316  if (GetBrowserPluginManager()) {
317    GetBrowserPluginManager()->Send(
318        new BrowserPluginHostMsg_CopyFromCompositingSurfaceAck(
319            host_routing_id_, GetInstanceID(), request_id, resized_bitmap));
320  }
321}
322
323}  // namespace content
324