// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/renderer/child_frame_compositing_helper.h"

#include <utility>

#include "build/build_config.h"
#include "cc/layers/picture_image_layer.h"
#include "cc/layers/solid_color_layer.h"
#include "cc/layers/surface_layer.h"
#include "cc/paint/paint_image.h"
#include "cc/paint/paint_image_builder.h"
#include "content/renderer/child_frame_compositor.h"
#include "skia/ext/image_operations.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkImage.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/skia_util.h"

namespace content {

ChildFrameCompositingHelper::ChildFrameCompositingHelper(
    ChildFrameCompositor* child_frame_compositor)
    : child_frame_compositor_(child_frame_compositor) {
  DCHECK(child_frame_compositor_);
}

ChildFrameCompositingHelper::~ChildFrameCompositingHelper() = default;

void ChildFrameCompositingHelper::ChildFrameGone(
    const gfx::Size& frame_size_in_dip,
    float device_scale_factor) {
  primary_surface_id_ = viz::SurfaceId();
  fallback_surface_id_ = viz::SurfaceId();

  scoped_refptr<cc::SolidColorLayer> crashed_layer =
      cc::SolidColorLayer::Create();
  crashed_layer->SetMasksToBounds(true);
  crashed_layer->SetBackgroundColor(SK_ColorBLACK);

  if (child_frame_compositor_->GetLayer()) {
    SkBitmap* sad_bitmap = child_frame_compositor_->GetSadPageBitmap();
    if (sad_bitmap && frame_size_in_dip.width() > sad_bitmap->width() &&
        frame_size_in_dip.height() > sad_bitmap->height()) {
      scoped_refptr<cc::PictureImageLayer> sad_layer =
          cc::PictureImageLayer::Create();
      sad_layer->SetImage(cc::PaintImageBuilder::WithDefault()
                              .set_id(cc::PaintImage::GetNextId())
                              .set_image(SkImage::MakeFromBitmap(*sad_bitmap),
                                         cc::PaintImage::GetNextContentId())
                              .TakePaintImage(),
                          SkMatrix::I(), false);
      sad_layer->SetBounds(
          gfx::Size(sad_bitmap->width() * device_scale_factor,
                    sad_bitmap->height() * device_scale_factor));
      sad_layer->SetPosition(
          gfx::PointF((frame_size_in_dip.width() - sad_bitmap->width()) / 2,
                      (frame_size_in_dip.height() - sad_bitmap->height()) / 2));
      sad_layer->SetIsDrawable(true);

      crashed_layer->AddChild(sad_layer);
    }
  }

  bool prevent_contents_opaque_changes = false;
  child_frame_compositor_->SetLayer(std::move(crashed_layer),
                                    prevent_contents_opaque_changes);
}

void ChildFrameCompositingHelper::SetPrimarySurfaceId(
    const viz::SurfaceId& surface_id,
    const gfx::Size& frame_size_in_dip,
    const cc::DeadlinePolicy& deadline) {
  if (primary_surface_id_ == surface_id)
    return;

  primary_surface_id_ = surface_id;

  surface_layer_ = cc::SurfaceLayer::Create();
  surface_layer_->SetMasksToBounds(true);
  surface_layer_->SetSurfaceHitTestable(true);
  surface_layer_->SetBackgroundColor(SK_ColorTRANSPARENT);

  surface_layer_->SetPrimarySurfaceId(surface_id, deadline);
  surface_layer_->SetFallbackSurfaceId(fallback_surface_id_);

  // TODO(lfg): Investigate if it's possible to propagate the information
  // about the child surface's opacity. https://crbug.com/629851.
  bool prevent_contents_opaque_changes = true;
  child_frame_compositor_->SetLayer(surface_layer_,
                                    prevent_contents_opaque_changes);

  UpdateVisibility(true);

  surface_layer_->SetBounds(frame_size_in_dip);
}

void ChildFrameCompositingHelper::SetFallbackSurfaceId(
    const viz::SurfaceId& surface_id,
    const gfx::Size& frame_size_in_dip) {
  fallback_surface_id_ = surface_id;

  if (!surface_layer_) {
    SetPrimarySurfaceId(surface_id, frame_size_in_dip,
                        cc::DeadlinePolicy::UseDefaultDeadline());
    return;
  }

  surface_layer_->SetFallbackSurfaceId(surface_id);
}

void ChildFrameCompositingHelper::UpdateVisibility(bool visible) {
  cc::Layer* layer = child_frame_compositor_->GetLayer();
  if (layer)
    layer->SetIsDrawable(visible);
}

}  // namespace content
