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

#include "cc/layers/layer.h"

#include <stddef.h>
#include <stdint.h>

#include <algorithm>
#include <string>
#include <unordered_set>
#include <utility>
#include <vector>

#include "base/atomic_sequence_num.h"
#include "base/location.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/histogram.h"
#include "base/notreached.h"
#include "base/strings/stringprintf.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "cc/base/features.h"
#include "cc/base/simple_enclosed_region.h"
#include "cc/layers/layer_impl.h"
#include "cc/layers/picture_layer.h"
#include "cc/trees/clip_node.h"
#include "cc/trees/draw_property_utils.h"
#include "cc/trees/layer_tree_host.h"
#include "cc/trees/layer_tree_impl.h"
#include "cc/trees/mutator_host.h"
#include "cc/trees/property_tree_builder.h"
#include "cc/trees/scroll_node.h"
#include "cc/trees/transform_node.h"
#include "components/viz/common/frame_sinks/copy_output_request.h"
#include "components/viz/common/frame_sinks/copy_output_result.h"
#include "components/viz/common/view_transition_element_resource_id.h"
#include "third_party/skia/include/core/SkImageFilter.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/geometry/vector2d_conversions.h"

#if BUILDFLAG(IS_CHROMEOS)
#include "base/debug/dump_without_crashing.h"
#endif

namespace cc {

struct SameSizeAsLayer : public base::RefCounted<SameSizeAsLayer>,
                         public ProtectedSequenceSynchronizer {
 private:
  SameSizeAsLayer();
  ~SameSizeAsLayer() override;

  raw_ptr<void> raw_pointers[2];
  std::unique_ptr<void> unique_pointers[2];

  struct {
    LayerList children;
    gfx::Size bounds;
    HitTestOpaqueness hit_test_opaqueness;
    bool bitfields;
    SkColor4f background_color;
    TouchActionRegion touch_action_region;
    ElementId element_id;
    std::unique_ptr<void> rare_inputs;
  } inputs;
  gfx::Rect update_rect;
  int int_fields[7];
  gfx::Vector2dF offset;
  bool bool_fields[2];
#if DCHECK_IS_ON()
  bool allow_remove_for_readd;
#endif
  std::unique_ptr<LayerUtils> layer_utils_;
#if BUILDFLAG(ARKWEB_CUSTOM_VIDEO_PLAYER)
  bool should_overlay_;
#endif // ARKWEB_CUSTOM_VIDEO_PLAYER

  uint8_t bit_fields[2];
#if BUILDFLAG(IS_CHROMEOS)
  bool is_valid_to_destroy_;
#endif
#if BUILDFLAG(IS_ARKWEB) && defined(__arm__)
  char dummy[8];
#endif
};

static_assert(sizeof(Layer) == sizeof(SameSizeAsLayer),
              "Layer should stay small");

base::AtomicSequenceNumber g_next_layer_id;

constexpr gfx::Transform Layer::kIdentityTransform;
constexpr gfx::RoundedCornersF Layer::kNoRoundedCornersF;

LayerDebugInfo::LayerDebugInfo() = default;
LayerDebugInfo::LayerDebugInfo(const LayerDebugInfo&) = default;
LayerDebugInfo::~LayerDebugInfo() = default;

Layer::RareInputs::RareInputs() = default;
Layer::RareInputs::~RareInputs() = default;

Layer::Inputs::Inputs() = default;
Layer::Inputs::~Inputs() = default;

Layer::LayerTreeInputs::LayerTreeInputs() = default;
Layer::LayerTreeInputs::~LayerTreeInputs() = default;

int Layer::GetNextLayerId() {
  // Layer IDs start from 1.
  return g_next_layer_id.GetNext() + 1;
}

scoped_refptr<Layer> Layer::Create() {
  return base::WrapRefCounted(new Layer());
}

Layer::Layer()
    : parent_(nullptr),
      layer_tree_host_(nullptr),
      layer_id_(GetNextLayerId()),
      num_descendants_that_draw_content_(0),
      transform_tree_index_(kInvalidPropertyNodeId),
      effect_tree_index_(kInvalidPropertyNodeId),
      clip_tree_index_(kInvalidPropertyNodeId),
      scroll_tree_index_(kInvalidPropertyNodeId),
      property_tree_sequence_number_(-1),
      ignore_set_needs_commit_for_test_(false),
      subtree_property_changed_(false),
      bitflags_(0u),
      changed_properties_(0u) {
  layer_utils_ = std::make_unique<LayerUtils>(this);
}

Layer::~Layer() {
  // Our parent should be holding a reference to us so there should be no
  // way for us to be destroyed while we still have a parent.
  DCHECK(!parent());
  // Similarly we shouldn't have a layer tree host since it also keeps a
  // reference to us.
  DCHECK(!layer_tree_host());

  // Remove the parent reference from all children and dependents.
  RemoveAllChildren();
#if BUILDFLAG(IS_CHROMEOS)
  // `is_valid_to_destroy_` should never be false at this point.
  // DCHECK to catch this issue in bots and reports the stack if this ever
  // happened in production.
  DCHECK(is_valid_to_destroy_);
  if (!is_valid_to_destroy_) {
    base::debug::DumpWithoutCrashing();
  }
#endif
}

Layer::LayerTreeInputs& Layer::EnsureLayerTreeInputs() {
  DCHECK(!IsAttached() || !IsUsingLayerLists());
  auto& layer_tree_inputs = layer_tree_inputs_.Write(*this);
  if (!layer_tree_inputs)
    layer_tree_inputs = std::make_unique<LayerTreeInputs>();
  return *layer_tree_inputs;
}

#if DCHECK_IS_ON()
const Layer::LayerTreeInputs* Layer::layer_tree_inputs() const {
  DCHECK(!IsAttached() || !IsUsingLayerLists());
  return layer_tree_inputs_.Read(*this);
}
#endif

void Layer::SetLayerTreeHost(LayerTreeHost* host) {
  DCHECK(IsPropertyChangeAllowed());
  if (layer_tree_host() == host)
    return;

  bool property_tree_indices_invalid = false;
  auto& inputs = inputs_.Write(*this);
  ElementId element_id = inputs.element_id;
  if (IsAttached()) {
    // These two lines copied from ProtectedSequenceReadable::Write()
    DCHECK(IsOwnerThread());
    layer_tree_host()->WaitForProtectedSequenceCompletion();

    layer_tree_host()->UnregisterLayer(this);
    if (element_id)
      layer_tree_host()->UnregisterElement(element_id, this);
    if (!IsUsingLayerLists()) {
      layer_tree_host()->property_trees()->set_needs_rebuild(true);
      property_tree_indices_invalid = true;
    }
  }
  if (host) {
    host->RegisterLayer(this);
    if (element_id)
      host->RegisterElement(element_id, this);
    if (!host->IsUsingLayerLists()) {
      host->property_trees()->set_needs_rebuild(true);
      property_tree_indices_invalid = true;
    }
  }

  // See comment in layer.h to learn why this assignment is so weird.
  const_cast<raw_ptr<LayerTreeHost>&>(layer_tree_host_) = host;

  if (host) {
    // When changing hosts, the layer needs to commit its properties to
    // the impl side for the new host.
    changed_properties_.Write(*this) |= kChangedAllProperties;
    host->AddLayerShouldPushProperties(this);
  }

  if (property_tree_indices_invalid)
    InvalidatePropertyTreesIndices();

  for (auto child : inputs.children)
    child->SetLayerTreeHost(host);

  if (host && !host->IsUsingLayerLists() &&
      host->mutator_host()->IsElementAnimating(element_id)) {
    host->SetNeedsCommit();
  }
}

void Layer::SetNeedsCommit() {
  if (!IsAttached())
    return;

  SetNeedsPushProperties();

  if (ignore_set_needs_commit_for_test_.Read(*this))
    return;

  layer_tree_host()->SetNeedsCommit();
}

void Layer::SetDebugName(std::string name) {
  if (!name.empty() || debug_info_.Read(*this)) {
    EnsureDebugInfo().name = std::move(name);
  }
}

viz::ViewTransitionElementResourceId Layer::ViewTransitionResourceId() const {
  return viz::ViewTransitionElementResourceId();
}

bool Layer::IsSolidColorLayerForTesting() const {
  return false;
}

void Layer::SetNeedsFullTreeSync() {
  if (!IsAttached())
    return;

  layer_tree_host()->SetNeedsFullTreeSync();
}

void Layer::SetNeedsPushProperties(uint8_t changed_props) {
  uint8_t& changed = changed_properties_.Write(*this);
  if (!::features::IsCCSlimmingEnabled()) {
    changed_props = kChangedAllProperties;
  }
  if ((changed & changed_props) != changed_props) {
    if (!changed && IsAttached()) {
      layer_tree_host()->AddLayerShouldPushProperties(this);
    }
    changed |= changed_props;
  }
}

bool Layer::IsPropertyChangeAllowed() const {
  if (!IsAttached())
    return true;
  DCHECK(IsMainThread());

  return !layer_tree_host()->in_paint_layer_contents();
}

void Layer::CaptureContent(const gfx::Rect& rect,
                           std::vector<NodeInfo>* content) const {}

sk_sp<const SkPicture> Layer::GetPicture() const {
  return nullptr;
}

void Layer::SetParent(Layer* layer, RemovalReason reason) {
  DCHECK(!layer || !layer->HasAncestor(this));
  DCHECK(reason == RemovalReason::kNormal || !layer);

  raw_ptr<Layer>& parent = parent_.Write(*this);
  parent = layer;
  if (reason == RemovalReason::kForReadd) {
    // When passing kForReadd, the caller is responsible for calling
    // SetLayerTreeHost.  Deferring this until after the readd means that the
    // single SetLayerTreeHost call will generally set layer_tree_host_ to the
    // same value, and thus be able to optimize away the recursive tree walk.
#if DCHECK_IS_ON()
    DCHECK(allow_remove_for_readd_);
#endif
  } else {
    SetLayerTreeHost(parent ? parent->layer_tree_host() : nullptr);
  }

  SetPropertyTreesNeedRebuild();
}

void Layer::AddChild(scoped_refptr<Layer> child) {
  InsertChild(child, inputs_.Read(*this).children.size());
}

void Layer::InsertChild(scoped_refptr<Layer> child, size_t index) {
  DCHECK(IsPropertyChangeAllowed());
  AllowRemoveForReadd allow(child.get());
  child->RemoveFromParentForReadd();
  AddDrawableDescendants(child->NumDescendantsThatDrawContent() +
                         (child->draws_content() ? 1 : 0));
  child->SetParent(this, RemovalReason::kNormal);
  child->SetSubtreePropertyChanged();

  auto& inputs = inputs_.Write(*this);
  index = std::min(index, inputs.children.size());
  const auto* layer_tree_inputs = layer_tree_inputs_.Read(*this);
  if (layer_tree_inputs && layer_tree_inputs->mask_layer && index &&
      index == inputs.children.size()) {
    // Ensure that the mask layer is always the last child.
    DCHECK_EQ(mask_layer(), inputs.children.back().get());
    index--;
  }
  inputs.children.insert(inputs.children.begin() + index, child);
  SetNeedsFullTreeSync();
}

void Layer::RemoveFromParent() {
  DCHECK(IsPropertyChangeAllowed());
  if (parent_.Read(*this))
    parent_.Write(*this)->RemoveChild(this, RemovalReason::kNormal);
}

void Layer::RemoveFromParentForReadd() {
  DCHECK(IsPropertyChangeAllowed());
  if (parent_.Read(*this))
    parent_.Write(*this)->RemoveChild(this, RemovalReason::kForReadd);
}

void Layer::RemoveChild(Layer* child, RemovalReason reason) {
  const auto* layer_tree_inputs = layer_tree_inputs_.Read(*this);
  if (layer_tree_inputs && child == layer_tree_inputs->mask_layer)
    layer_tree_inputs_.Write(*this)->mask_layer = nullptr;

  auto& inputs = inputs_.Write(*this);
  for (auto iter = inputs.children.begin(); iter != inputs.children.end();
       ++iter) {
    if (iter->get() != child)
      continue;

    child->SetParent(nullptr, reason);
    AddDrawableDescendants(-child->NumDescendantsThatDrawContent() -
                           (child->draws_content() ? 1 : 0));
    inputs.children.erase(iter);
    SetNeedsFullTreeSync();
    return;
  }
}

bool Layer::GetBitFlag(uint8_t mask) const {
  return bitflags_.Read(*this) & mask;
}

bool Layer::SetBitFlag(bool new_value,
                       uint8_t mask,
                       bool invalidate,
                       bool needs_push) {
  if (GetBitFlag(mask) == new_value)
    return false;
  if (new_value)
    bitflags_.Write(*this) |= mask;
  else
    bitflags_.Write(*this) &= ~mask;
  if (invalidate) {
    SetPropertyTreesNeedRebuild();
    SetNeedsCommit();
  }
  if (needs_push)
    SetNeedsPushProperties();
  return true;
}

void Layer::ReorderChildren(LayerList* new_children_order) {
  auto& inputs = inputs_.Write(*this);
#if DCHECK_IS_ON()
  base::flat_set<Layer*> children_set;
  for (const auto& child : *new_children_order) {
    DCHECK_EQ(child->parent(), this);
    children_set.insert(child.get());
  }
  for (const auto& child : inputs.children)
    DCHECK_GT(children_set.count(child.get()), 0u);
#endif
  inputs.children = std::move(*new_children_order);

  // We do not need to call SetSubtreePropertyChanged for each child here
  // since SetSubtreePropertyChanged includes SetNeedsPushProperties, but this
  // change is not included in properties pushing.
  for (const auto& child : inputs.children)
    child->subtree_property_changed_.Write(*child) = true;

  SetNeedsFullTreeSync();
}

void Layer::ReplaceChild(Layer* reference, scoped_refptr<Layer> new_layer) {
  DCHECK(reference);
  DCHECK_EQ(reference->parent(), this);
  DCHECK(IsPropertyChangeAllowed());

  if (reference == new_layer.get())
    return;

  // Find the index of |reference| in |children_|.
  auto& inputs = inputs_.Write(*this);
  auto reference_it =
      std::ranges::find(inputs.children, reference, &scoped_refptr<Layer>::get);
  CHECK(reference_it != inputs.children.end());
  size_t reference_index = reference_it - inputs.children.begin();
  reference->RemoveFromParent();

  if (new_layer.get()) {
    InsertChild(new_layer, reference_index);
  }
}

void Layer::SetBounds(const gfx::Size& size) {
  DCHECK(IsPropertyChangeAllowed());
  if (bounds() == size)
    return;
  inputs_.Write(*this).bounds = size;

  if (!IsAttached())
    return;

  // Rounded corner clipping, bounds clipping and mask clipping can result in
  // new areas of subtrees being exposed on a bounds change. Ensure the damaged
  // areas are updated. Also, if the layer subtree (rooted at this layer) is
  // marked as capturable (via a valid SubtreeCaptureId), then the property tree
  // needs rebuild so that |EffectNode::subtree_size| is updated with the new
  // size of this layer.
  if (!IsUsingLayerLists()) {
    if (subtree_capture_id().is_valid() || masks_to_bounds() || mask_layer() ||
        HasMaskFilter()) {
      SetSubtreePropertyChanged();
      SetPropertyTreesNeedRebuild();
    }

    if (scrollable()) {
      auto& scroll_tree =
          layer_tree_host()->property_trees()->scroll_tree_mutable();
      if (auto* scroll_node = scroll_tree.Node(scroll_tree_index_.Read(*this)))
        scroll_node->bounds = inputs_.Read(*this).bounds;
      else
        SetPropertyTreesNeedRebuild();
    }
  }

  SetNeedsCommit();
}

Layer* Layer::RootLayer() {
  Layer* layer = this;
  while (layer->parent())
    layer = layer->mutable_parent();
  return layer;
}

void Layer::RemoveAllChildren() {
  DCHECK(IsPropertyChangeAllowed());
  auto& inputs = inputs_.Write(*this);
  while (inputs.children.size()) {
    Layer* layer = inputs.children[0].get();
    DCHECK_EQ(this, layer->parent());
    layer->RemoveFromParent();
  }
}

void Layer::SetChildLayerList(LayerList new_children) {
  DCHECK(IsUsingLayerLists());

  // Early out without calling |LayerTreeHost::SetNeedsFullTreeSync| if no
  // layer has changed.
  if (children() == new_children)
    return;

  // Remove existing children that will not be in the new child list.
  {
    std::unordered_set<Layer*> children_to_remove;
    for (auto& existing_child : children())
      children_to_remove.insert(existing_child.get());
    for (auto& new_child : new_children)
      children_to_remove.erase(new_child.get());
    for (auto* child : children_to_remove) {
      child->SetParent(nullptr, RemovalReason::kNormal);
      AddDrawableDescendants(-child->NumDescendantsThatDrawContent() -
                             (child->draws_content() ? 1 : 0));
    }
  }

  // Mark existing children as changed if their order changes.
  auto existing_child_it = children().begin();
  for (auto& child : new_children) {
    if (child->parent() == this) {
      // Search forward in the existing child list to find the new child.
      existing_child_it = std::find(existing_child_it, children().end(), child);
      if (existing_child_it == children().end())
        child->SetSubtreePropertyChanged();
    }
  }

  // Process new children and mark them as changed.
  // Because this changes the child's parent, it must be after code that uses
  // |child->parent()| such as the above loop.
  for (auto& child : new_children) {
    if (child->parent() != this) {
      AllowRemoveForReadd allow(child.get());
      child->RemoveFromParentForReadd();
      AddDrawableDescendants(child->NumDescendantsThatDrawContent() +
                             (child->draws_content() ? 1 : 0));
      child->SetParent(this, RemovalReason::kNormal);
      child->SetSubtreePropertyChanged();
    }
  }

  inputs_.Write(*this).children = std::move(new_children);

  layer_tree_host()->SetNeedsFullTreeSync();
}

bool Layer::HasAncestor(const Layer* ancestor) const {
  for (const Layer* layer = parent(); layer; layer = layer->parent()) {
    if (layer == ancestor)
      return true;
  }
  return false;
}

void Layer::RequestCopyOfOutput(
    std::unique_ptr<viz::CopyOutputRequest> request) {
  DCHECK(IsPropertyChangeAllowed());
  auto& inputs = EnsureLayerTreeInputs();
  if (request->has_source()) {
    const base::UnguessableToken& source = request->source();
    auto it = std::ranges::find_if(
        inputs.copy_requests,
        [&source](const std::unique_ptr<viz::CopyOutputRequest>& x) {
          return x->has_source() && x->source() == source;
        });
    if (it != inputs.copy_requests.end())
      inputs.copy_requests.erase(it);
  }
  inputs.copy_requests.push_back(std::move(request));
  SetSubtreePropertyChanged();
  SetPropertyTreesNeedRebuild();
  SetNeedsCommit();
  if (IsAttached())
    layer_tree_host()->SetHasCopyRequest(true);
}

void Layer::SetBackgroundColor(SkColor4f background_color) {
  DCHECK(IsPropertyChangeAllowed());
  auto& inputs = inputs_.Write(*this);
  if (inputs.background_color == background_color)
    return;
  inputs.background_color = background_color;
  SetPropertyTreesNeedRebuild();
  SetNeedsCommit();
}

void Layer::SetSafeOpaqueBackgroundColor(SkColor4f background_color) {
  DCHECK(IsPropertyChangeAllowed());
  SkColor4f opaque_color = background_color.makeOpaque();
  auto& inputs = EnsureLayerTreeInputs();
  if (inputs.safe_opaque_background_color == opaque_color)
    return;
  inputs.safe_opaque_background_color = opaque_color;
  SetNeedsPushProperties();
}

SkColor4f Layer::SafeOpaqueBackgroundColor() const {
  if (contents_opaque()) {
    if (!IsUsingLayerLists()) {
      // In layer tree mode, PropertyTreeBuilder should have calculated the safe
      // opaque background color and called SetSafeOpaqueBackgroundColor().
      DCHECK(layer_tree_inputs());
      DCHECK(layer_tree_inputs()->safe_opaque_background_color.isOpaque());
      return layer_tree_inputs()->safe_opaque_background_color;
    }
    // In layer list mode, the PropertyTreeBuilder algorithm doesn't apply
    // because it depends on the layer tree hierarchy. Instead we use
    // background_color() made opaque.
    return background_color().makeOpaque();
  }
  if (background_color().isOpaque()) {
    // The layer is not opaque while the background color is, meaning that the
    // background color doesn't cover the whole layer. Use
    // SkColors::kTransparent to avoid intrusive checkerboard where the layer is
    // not covered by the background color.
    return SkColors::kTransparent;
  }
  return background_color();
}

void Layer::SetMasksToBounds(bool masks_to_bounds) {
  DCHECK(IsPropertyChangeAllowed());
  auto& inputs = EnsureLayerTreeInputs();
  if (inputs.masks_to_bounds == masks_to_bounds)
    return;
  inputs.masks_to_bounds = masks_to_bounds;
  SetNeedsCommit();
  SetPropertyTreesNeedRebuild();
  SetSubtreePropertyChanged();
}

void Layer::SetClipRect(const gfx::Rect& clip_rect) {
  DCHECK(IsPropertyChangeAllowed());
  auto& inputs = EnsureLayerTreeInputs();
  if (inputs.clip_rect == clip_rect)
    return;
  inputs.clip_rect = clip_rect;

  // If the clip bounds have been cleared, the property trees needs a rebuild.
  const bool force_rebuild = clip_rect.IsEmpty() || !has_clip_node();

  SetSubtreePropertyChanged();
  if (clip_tree_index() != kInvalidPropertyNodeId && !force_rebuild) {
    PropertyTrees* property_trees = layer_tree_host()->property_trees();
    gfx::RectF effective_clip_rect = EffectiveClipRect();
    if (ClipNode* node =
            property_trees->clip_tree_mutable().Node(clip_tree_index())) {
      node->clip = effective_clip_rect;
      node->clip += offset_to_transform_parent();
      property_trees->clip_tree_mutable().set_needs_update(true);
    }
    if (HasMaskFilter() && effect_tree_index() != kInvalidPropertyNodeId) {
      if (EffectNode* node =
              property_trees->effect_tree_mutable().Node(effect_tree_index())) {
        node->mask_filter_info = gfx::MaskFilterInfo(
            effective_clip_rect, corner_radii(), gradient_mask());
        node->effect_changed = true;
        property_trees->effect_tree_mutable().set_needs_update(true);
      }
    }
  } else {
    SetPropertyTreesNeedRebuild();
  }
  SetNeedsCommit();
}

gfx::RectF Layer::EffectiveClipRect() const {
  // If this does not have a clip rect set, then the subtree is clipped by
  // the bounds.
  const gfx::RectF layer_bounds = gfx::RectF(gfx::SizeF(bounds()));
  if (clip_rect().IsEmpty())
    return layer_bounds;

  const gfx::RectF clip_rect_f(clip_rect());

  // Layer needs to clip to its bounds as well apply a clip rect. Intersect the
  // two to get the effective clip.
  if (masks_to_bounds() || mask_layer() || filters().HasFilterThatMovesPixels())
    return gfx::IntersectRects(layer_bounds, clip_rect_f);

  // Clip rect is the only clip effecting the layer.
  return clip_rect_f;
}

void Layer::SetMaskLayer(scoped_refptr<PictureLayer> mask_layer) {
  DCHECK(IsPropertyChangeAllowed());
  auto& inputs = EnsureLayerTreeInputs();
  if (inputs.mask_layer.get() == mask_layer)
    return;
  if (inputs.mask_layer) {
    DCHECK_EQ(this, inputs.mask_layer->parent());
    inputs.mask_layer->RemoveFromParent();
  }
  // Clear mask_layer first and set it later because InsertChild() checks it to
  // ensure the mask layer is the last child.
  inputs.mask_layer = nullptr;
  if (mask_layer) {
    // The mask layer should not have any children.
    DCHECK(mask_layer->children().empty());

    mask_layer->EnsureLayerTreeInputs().position = gfx::PointF();
    mask_layer->SetIsDrawable(true);
    mask_layer->SetBlendMode(SkBlendMode::kDstIn);
    // This flag will be updated in PropertyTreeBuilder.
    mask_layer->SetIsBackdropFilterMask(false);
    AddChild(mask_layer);
  }
  inputs.mask_layer = mask_layer.get();
  SetSubtreePropertyChanged();
}

void Layer::SetFilters(const FilterOperations& filters) {
  DCHECK(IsPropertyChangeAllowed());
  auto& inputs = EnsureLayerTreeInputs();
  if (inputs.filters == filters)
    return;
  inputs.filters = filters;
  SetSubtreePropertyChanged();
  SetPropertyTreesNeedRebuild();
  SetNeedsCommit();
}

void Layer::SetBackdropFilters(const FilterOperations& filters) {
  DCHECK(IsPropertyChangeAllowed());
  auto& inputs = EnsureLayerTreeInputs();
  if (inputs.backdrop_filters == filters)
    return;
  inputs.backdrop_filters = filters;

  SetSubtreePropertyChanged();
  SetPropertyTreesNeedRebuild();
  SetNeedsCommit();
}

void Layer::SetBackdropFilterBounds(const SkPath& backdrop_filter_bounds) {
  EnsureLayerTreeInputs().backdrop_filter_bounds = backdrop_filter_bounds;
}

void Layer::ClearBackdropFilterBounds() {
  if (layer_tree_inputs())
    layer_tree_inputs_.Write(*this)->backdrop_filter_bounds.reset();
}

void Layer::SetBackdropFilterQuality(const float quality) {
  EnsureLayerTreeInputs().backdrop_filter_quality = quality;
}

void Layer::UpdateMaskFilterInfo(const gfx::RoundedCornersF* corner_radii,
                                 const gfx::LinearGradient* gradient_mask) {
  DCHECK(IsPropertyChangeAllowed());
  auto& inputs = EnsureLayerTreeInputs();

  if (corner_radii)
    inputs.corner_radii = *corner_radii;

  if (gradient_mask)
    inputs.gradient_mask = *gradient_mask;

  SetSubtreePropertyChanged();
  SetNeedsCommit();
  PropertyTrees* property_trees =
      IsAttached() ? layer_tree_host()->property_trees() : nullptr;
  EffectNode* node = nullptr;
  if (property_trees && effect_tree_index() != kInvalidPropertyNodeId &&
      (node =
           property_trees->effect_tree_mutable().Node(effect_tree_index()))) {
    gfx::RectF effective_clip_rect = EffectiveClipRect();
    effective_clip_rect += offset_to_transform_parent();
    node->mask_filter_info = gfx::MaskFilterInfo(
        effective_clip_rect, inputs.corner_radii, inputs.gradient_mask);
    node->effect_changed = true;
    property_trees->effect_tree_mutable().set_needs_update(true);
  } else {
    SetPropertyTreesNeedRebuild();
  }
}

void Layer::SetRoundedCorner(const gfx::RoundedCornersF& corner_radii) {
  if (EnsureLayerTreeInputs().corner_radii == corner_radii)
    return;

  UpdateMaskFilterInfo(&corner_radii, nullptr);
}

void Layer::SetGradientMask(const gfx::LinearGradient& gradient_mask) {
  if (EnsureLayerTreeInputs().gradient_mask == gradient_mask)
    return;

  UpdateMaskFilterInfo(nullptr, &gradient_mask);
}

void Layer::SetIsFastRoundedCorner(bool enable) {
  DCHECK(IsPropertyChangeAllowed());
  auto& inputs = EnsureLayerTreeInputs();
  if (inputs.is_fast_rounded_corner == enable)
    return;
  inputs.is_fast_rounded_corner = enable;

  // If this layer does not have a rounded corner, then modifying this flag is
  // going to have no effect.
  if (!HasRoundedCorner())
    return;

  SetSubtreePropertyChanged();
  SetNeedsCommit();
  SetPropertyTreesNeedRebuild();
}

void Layer::SetOpacity(float opacity) {
  DCHECK(IsPropertyChangeAllowed());
  DCHECK_GE(opacity, 0.f);
  DCHECK_LE(opacity, 1.f);

  auto& inputs = EnsureLayerTreeInputs();
  if (inputs.opacity == opacity)
    return;
  // We need to force a property tree rebuild when opacity changes from 1 to a
  // non-1 value or vice-versa as render surfaces can change.
  bool force_rebuild = opacity == 1.f || inputs.opacity == 1.f;
  inputs.opacity = opacity;
  SetSubtreePropertyChanged();

  if (IsAttached()) {
    if (!force_rebuild) {
      PropertyTrees* property_trees = layer_tree_host()->property_trees();
      if (EffectNode* node =
              property_trees->effect_tree_mutable().Node(effect_tree_index())) {
        node->opacity = opacity;
        node->effect_changed = true;
        property_trees->effect_tree_mutable().set_needs_update(true);
      }
    } else {
      SetPropertyTreesNeedRebuild();
    }
  }

  SetNeedsCommit();
}

float Layer::EffectiveOpacity() const {
  if (!layer_tree_inputs())
    return 1.0f;
  return layer_tree_inputs()->hide_layer_and_subtree
             ? 0.f
             : layer_tree_inputs()->opacity;
}

bool Layer::OpacityCanAnimateOnImplThread() const {
  return false;
}

void Layer::SetBlendMode(SkBlendMode blend_mode) {
  DCHECK(IsPropertyChangeAllowed());
  auto& inputs = EnsureLayerTreeInputs();
  if (inputs.blend_mode == blend_mode)
    return;

  // Allowing only blend modes that are defined in the CSS Compositing standard,
  // plus destination-in which is used to implement masks.
  // http://dev.w3.org/fxtf/compositing-1/#blending
  switch (blend_mode) {
    case SkBlendMode::kSrcOver:
    case SkBlendMode::kDstIn:
    case SkBlendMode::kScreen:
    case SkBlendMode::kOverlay:
    case SkBlendMode::kDarken:
    case SkBlendMode::kLighten:
    case SkBlendMode::kColorDodge:
    case SkBlendMode::kColorBurn:
    case SkBlendMode::kHardLight:
    case SkBlendMode::kSoftLight:
    case SkBlendMode::kDifference:
    case SkBlendMode::kExclusion:
    case SkBlendMode::kMultiply:
    case SkBlendMode::kHue:
    case SkBlendMode::kSaturation:
    case SkBlendMode::kColor:
    case SkBlendMode::kLuminosity:
      // supported blend modes
      break;
    case SkBlendMode::kClear:
    case SkBlendMode::kSrc:
    case SkBlendMode::kDst:
    case SkBlendMode::kDstOver:
    case SkBlendMode::kSrcIn:
    case SkBlendMode::kSrcOut:
    case SkBlendMode::kDstOut:
    case SkBlendMode::kSrcATop:
    case SkBlendMode::kDstATop:
    case SkBlendMode::kXor:
    case SkBlendMode::kPlus:
    case SkBlendMode::kModulate:
      // Porter Duff Compositing Operators are not yet supported
      // http://dev.w3.org/fxtf/compositing-1/#porterduffcompositingoperators
      NOTREACHED();
  }

  inputs.blend_mode = blend_mode;
  SetNeedsCommit();
  SetSubtreePropertyChanged();
  SetPropertyTreesNeedRebuild();
}

void Layer::SetHitTestOpaqueness(HitTestOpaqueness opaqueness) {
  DCHECK(IsPropertyChangeAllowed());
  auto& inputs = inputs_.Write(*this);
  if (inputs.hit_test_opaqueness == opaqueness) {
    return;
  }
  inputs.hit_test_opaqueness = opaqueness;
  SetPropertyTreesNeedRebuild();
  SetNeedsCommit();
}

void Layer::SetHitTestable(bool hit_testable) {
  SetHitTestOpaqueness(hit_testable ? HitTestOpaqueness::kMixed
                                    : HitTestOpaqueness::kTransparent);
}

void Layer::SetContentsOpaque(bool opaque) {
  DCHECK(IsPropertyChangeAllowed());
  if (inputs_.Read(*this).contents_opaque == opaque)
    return;
  auto& inputs = inputs_.Write(*this);
  inputs.contents_opaque = opaque;
  inputs.contents_opaque_for_text = opaque;
  SetNeedsCommit();
  SetSubtreePropertyChanged();
  SetPropertyTreesNeedRebuild();
}

void Layer::SetContentsOpaqueForText(bool opaque) {
  DCHECK(IsPropertyChangeAllowed());
  if (inputs_.Read(*this).contents_opaque_for_text == opaque)
    return;
  DCHECK(!contents_opaque() || opaque);
  inputs_.Write(*this).contents_opaque_for_text = opaque;
  SetNeedsCommit();
}

void Layer::SetPosition(const gfx::PointF& position) {
  DCHECK(!IsAttached() || !IsUsingLayerLists());

  // The mask layer should always be at the same location as the masked layer
  // which is its parent, so its position should be always zero.
  if (parent() && parent()->mask_layer() == this) {
    DCHECK(this->position().IsOrigin());
    return;
  }

  DCHECK(IsPropertyChangeAllowed());
  auto& inputs = EnsureLayerTreeInputs();
  if (inputs.position == position)
    return;
  inputs.position = position;

  if (!IsAttached())
    return;

  SetSubtreePropertyChanged();

  if (has_transform_node()) {
    TransformNode* transform_node =
        layer_tree_host()->property_trees()->transform_tree_mutable().Node(
            transform_tree_index_.Read(*this));
    // We should never set root layer's position to non-zero.
    DCHECK(parent());
    transform_node->post_translation =
        position.OffsetFromOrigin() + parent()->offset_to_transform_parent();
    transform_node->needs_local_transform_update = true;
    transform_node->SetTransformChanged(DamageReason::kUntracked);
    layer_tree_host()
        ->property_trees()
        ->transform_tree_mutable()
        .set_needs_update(true);
  } else {
    SetPropertyTreesNeedRebuild();
  }

  SetNeedsCommit();
}

bool Are2dAxisAligned(const gfx::Transform& a, const gfx::Transform& b) {
  if (a.IsScaleOrTranslation() && b.IsScaleOrTranslation()) {
    return true;
  }

  gfx::Transform inverse;
  if (b.GetInverse(&inverse)) {
    inverse *= a;
    return inverse.Preserves2dAxisAlignment();
  } else {
    // TODO(weiliangc): Should return false because b is not invertible.
    return a.Preserves2dAxisAlignment();
  }
}

void Layer::SetTransform(const gfx::Transform& transform) {
  DCHECK(IsPropertyChangeAllowed());
  auto& inputs = EnsureLayerTreeInputs();
  if (inputs.transform == transform)
    return;

  SetSubtreePropertyChanged();
  if (IsAttached()) {
    if (has_transform_node()) {
      TransformNode* transform_node =
          layer_tree_host()->property_trees()->transform_tree_mutable().Node(
              transform_tree_index_.Read(*this));
      // We need to trigger a rebuild if we could have affected 2d axis
      // alignment. We'll check to see if transform and inputs_.transform are
      // axis align with respect to one another.
      DCHECK_EQ(transform_tree_index(), transform_node->id);
      bool preserves_2d_axis_alignment =
          Are2dAxisAligned(inputs.transform, transform);
      transform_node->local = transform;
      transform_node->needs_local_transform_update = true;
      transform_node->SetTransformChanged(DamageReason::kUntracked);
      layer_tree_host()
          ->property_trees()
          ->transform_tree_mutable()
          .set_needs_update(true);
      if (!preserves_2d_axis_alignment)
        SetPropertyTreesNeedRebuild();
    } else {
      SetPropertyTreesNeedRebuild();
    }
  }

  inputs.transform = transform;
  SetNeedsCommit();
}

void Layer::SetTransformOrigin(const gfx::Point3F& transform_origin) {
  DCHECK(IsPropertyChangeAllowed());
  auto& inputs = EnsureLayerTreeInputs();
  if (inputs.transform_origin == transform_origin)
    return;
  inputs.transform_origin = transform_origin;

  if (!IsAttached())
    return;

  SetSubtreePropertyChanged();

  if (has_transform_node()) {
    TransformNode* transform_node =
        layer_tree_host()->property_trees()->transform_tree_mutable().Node(
            transform_tree_index_.Read(*this));
    DCHECK_EQ(transform_tree_index(), transform_node->id);
    transform_node->origin = transform_origin;
    transform_node->needs_local_transform_update = true;
    transform_node->SetTransformChanged(DamageReason::kUntracked);
    layer_tree_host()
        ->property_trees()
        ->transform_tree_mutable()
        .set_needs_update(true);
  } else {
    SetPropertyTreesNeedRebuild();
  }

  SetNeedsCommit();
}

void Layer::SetScrollOffset(const gfx::PointF& scroll_offset) {
  DCHECK(IsPropertyChangeAllowed());

  auto& inputs = EnsureLayerTreeInputs();
  if (inputs.scroll_offset == scroll_offset)
    return;
  inputs.scroll_offset = scroll_offset;

  if (!IsAttached())
    return;

  UpdatePropertyTreeScrollOffset();

  SetNeedsCommit();
}

void Layer::SetScrollOffsetFromImplSide(const gfx::PointF& scroll_offset) {
  DCHECK(IsPropertyChangeAllowed());
  // This function only gets called during a BeginMainFrame, so there
  // is no need to call SetNeedsUpdate here.
  DCHECK(IsAttached() && layer_tree_host()->CommitRequested());

  auto& inputs = EnsureLayerTreeInputs();
  if (inputs.scroll_offset == scroll_offset)
    return;
  inputs.scroll_offset = scroll_offset;

  UpdatePropertyTreeScrollOffset();

  if (!inputs.did_scroll_callback.is_null())
    inputs.did_scroll_callback.Run(scroll_offset, element_id());

  // The callback could potentially change the layer structure:
  // "this" may have been destroyed during the process.
}

void Layer::UpdatePropertyTreeScrollOffset() {
  DCHECK(scrollable());
  DCHECK(!IsUsingLayerLists());

  if (scroll_tree_index() == kInvalidPropertyNodeId) {
    // Ensure the property trees just have not been built yet but are marked for
    // being built which will set the correct scroll offset values.
    DCHECK(layer_tree_host()->property_trees()->needs_rebuild());
    return;
  }

  // If a scroll node exists, it should have an associated transform node.
  DCHECK(transform_tree_index() != kInvalidPropertyNodeId);

  auto& property_trees = *layer_tree_host()->property_trees();
  property_trees.scroll_tree_mutable().SetScrollOffset(element_id(),
                                                       scroll_offset());
  auto* transform_node =
      property_trees.transform_tree_mutable().Node(transform_tree_index());
  DCHECK_EQ(transform_tree_index(), transform_node->id);
  transform_node->SetScrollOffset(scroll_offset(), DamageReason::kUntracked);
  transform_node->needs_local_transform_update = true;
  property_trees.transform_tree_mutable().set_needs_update(true);
}

void Layer::SetDidScrollCallback(
    base::RepeatingCallback<void(const gfx::PointF&, const ElementId&)>
        callback) {
  EnsureLayerTreeInputs().did_scroll_callback = std::move(callback);
}

void Layer::SetSubtreeCaptureId(viz::SubtreeCaptureId subtree_id) {
  DCHECK(IsPropertyChangeAllowed());

  auto& inputs = EnsureLayerTreeInputs();
  if (inputs.subtree_capture_id == subtree_id)
    return;

  DCHECK(!inputs.subtree_capture_id.is_valid() || !subtree_id.is_valid())
      << "Not allowed to change from a valid ID to another valid ID, as it may "
         "already be in use.";

  inputs.subtree_capture_id = subtree_id;
  SetPropertyTreesNeedRebuild();
  SetNeedsCommit();
}

void Layer::SetScrollable(const gfx::Size& bounds) {
  DCHECK(IsPropertyChangeAllowed());
  auto& inputs = EnsureLayerTreeInputs();
  if (inputs.scrollable && inputs.scroll_container_bounds == bounds)
    return;
  bool was_scrollable = inputs.scrollable;
  inputs.scrollable = true;
  inputs.scroll_container_bounds = bounds;

  if (!IsAttached())
    return;

  auto& scroll_tree =
      layer_tree_host()->property_trees()->scroll_tree_mutable();
  auto* scroll_node = scroll_tree.Node(scroll_tree_index_.Read(*this));
  if (was_scrollable && scroll_node)
    scroll_node->container_bounds = inputs.scroll_container_bounds;
  else
    SetPropertyTreesNeedRebuild();

  SetNeedsCommit();
}

bool Layer::IsScrollbarLayerForTesting() const {
  return false;
}

void Layer::SetMainThreadScrollHitTestRegion(const Region& region) {
  DCHECK(IsPropertyChangeAllowed());
  const auto& rare_inputs = inputs_.Read(*this).rare_inputs;
  if (!rare_inputs && region.IsEmpty())
    return;
  if (rare_inputs &&
      rare_inputs->main_thread_scroll_hit_test_region == region) {
    return;
  }
  EnsureRareInputs().main_thread_scroll_hit_test_region = region;
  SetPropertyTreesNeedRebuild();
  SetNeedsCommit();
}

void Layer::SetNonCompositedScrollHitTestRects(
    std::vector<ScrollHitTestRect> rects) {
  DCHECK(IsPropertyChangeAllowed());
  const auto& rare_inputs = inputs_.Read(*this).rare_inputs;
  if (!rare_inputs && rects.empty()) {
    return;
  }
  if (rare_inputs &&
      rare_inputs->non_composited_scroll_hit_test_rects == rects) {
    return;
  }
  EnsureRareInputs().non_composited_scroll_hit_test_rects = std::move(rects);
  SetNeedsCommit();
}

void Layer::SetTouchActionRegion(TouchActionRegion touch_action_region) {
  DCHECK(IsPropertyChangeAllowed());
  if (inputs_.Read(*this).touch_action_region == touch_action_region)
    return;

  inputs_.Write(*this).touch_action_region = std::move(touch_action_region);
  SetPropertyTreesNeedRebuild();
  SetNeedsCommit();
}

void Layer::SetCaptureBounds(viz::RegionCaptureBounds bounds) {
  DCHECK(IsPropertyChangeAllowed());
  const auto& rare_inputs = inputs_.Read(*this).rare_inputs;
  if (!rare_inputs && bounds.IsEmpty())
    return;
  if (rare_inputs && rare_inputs->capture_bounds == bounds)
    return;
  EnsureRareInputs().capture_bounds = std::move(bounds);
  SetPropertyTreesNeedRebuild();
  SetNeedsCommit();
  SetSubtreePropertyChanged();
}

void Layer::SetWheelEventRegion(Region wheel_event_region) {
  DCHECK(IsPropertyChangeAllowed());
  const auto& rare_inputs = inputs_.Read(*this).rare_inputs;
  if (!rare_inputs && wheel_event_region.IsEmpty())
    return;
  if (rare_inputs && rare_inputs->wheel_event_region == wheel_event_region)
    return;
  EnsureRareInputs().wheel_event_region = std::move(wheel_event_region);
  SetNeedsCommit();
}

#if BUILDFLAG(IS_ANDROID)
void Layer::SetXrHitTestOrder(std::vector<ElementId> xr_hit_test_order) {
  CHECK(IsPropertyChangeAllowed());
  const auto& rare_inputs = inputs_.Read(*this).rare_inputs;
  if (!rare_inputs && xr_hit_test_order.empty()) {
    return;
  }
  if (rare_inputs && rare_inputs->xr_hit_test_order == xr_hit_test_order) {
    return;
  }
  EnsureRareInputs().xr_hit_test_order = std::move(xr_hit_test_order);
}
#endif

RenderSurfaceReason Layer::GetRenderSurfaceReason() const {
  if (!IsAttached())
    return RenderSurfaceReason::kNone;
  const PropertyTrees* property_trees = layer_tree_host()->property_trees();
  DCHECK(!property_trees->needs_rebuild());
  const EffectNode* effect_node =
      property_trees->effect_tree().Node(this->effect_tree_index());

  // Effect node can also be the effect node of an ancestor layer.
  // Check if this effect node was created for this layer specifically.
  if (!effect_node ||
      (parent_.Read(*this) &&
       this->effect_tree_index() == parent_.Read(*this)->effect_tree_index())) {
    return RenderSurfaceReason::kNone;
  }
  return effect_node->render_surface_reason;
}

void Layer::SetTransformTreeIndex(int index) {
  DCHECK(IsPropertyChangeAllowed());
  if (transform_tree_index_.Read(*this) == index)
    return;
  SetHasTransformNode(index != kInvalidPropertyNodeId);
  transform_tree_index_.Write(*this) = index;
  SetNeedsPushProperties(kChangedPropertyTreeIndex);
}

int Layer::transform_tree_index(const PropertyTrees& property_trees) const {
  if (property_trees.sequence_number() !=
      property_tree_sequence_number_.Read(*this)) {
    return kInvalidPropertyNodeId;
  }
  return transform_tree_index_.Read(*this);
}

bool Layer::transform_tree_index_is_valid(
    const PropertyTrees& property_trees) const {
  return transform_tree_index_.Read(*this) != kInvalidPropertyNodeId &&
         property_trees.sequence_number() ==
             property_tree_sequence_number_.Read(*this);
}

int Layer::transform_tree_index() const {
  if (!IsAttached())
    return kInvalidPropertyNodeId;
  return transform_tree_index(*layer_tree_host()->property_trees());
}

void Layer::SetClipTreeIndex(int index) {
  DCHECK(IsPropertyChangeAllowed());
  if (clip_tree_index_.Read(*this) == index)
    return;
  clip_tree_index_.Write(*this) = index;
  SetNeedsPushProperties(kChangedPropertyTreeIndex);
}

int Layer::clip_tree_index(const PropertyTrees& property_trees) const {
  if (property_trees.sequence_number() !=
      property_tree_sequence_number_.Read(*this)) {
    return kInvalidPropertyNodeId;
  }
  return clip_tree_index_.Read(*this);
}

bool Layer::clip_tree_index_is_valid(
    const PropertyTrees& property_trees) const {
  return clip_tree_index_.Read(*this) != kInvalidPropertyNodeId &&
         property_trees.sequence_number() ==
             property_tree_sequence_number_.Read(*this);
}

int Layer::clip_tree_index() const {
  if (!IsAttached())
    return kInvalidPropertyNodeId;
  return clip_tree_index(*layer_tree_host()->property_trees());
}

void Layer::SetEffectTreeIndex(int index) {
  DCHECK(IsPropertyChangeAllowed());
  if (effect_tree_index_.Read(*this) == index)
    return;
  effect_tree_index_.Write(*this) = index;
  SetNeedsPushProperties(kChangedPropertyTreeIndex);
}

int Layer::effect_tree_index(const PropertyTrees& property_trees) const {
  if (property_trees.sequence_number() !=
      property_tree_sequence_number_.Read(*this)) {
    return kInvalidPropertyNodeId;
  }
  return effect_tree_index_.Read(*this);
}

bool Layer::effect_tree_index_is_valid(
    const PropertyTrees& property_trees) const {
  return effect_tree_index_.Read(*this) != kInvalidPropertyNodeId &&
         property_trees.sequence_number() ==
             property_tree_sequence_number_.Read(*this);
}

int Layer::effect_tree_index() const {
  if (!IsAttached())
    return kInvalidPropertyNodeId;
  return effect_tree_index(*layer_tree_host()->property_trees());
}

void Layer::SetScrollTreeIndex(int index) {
  DCHECK(IsPropertyChangeAllowed());
  if (scroll_tree_index_.Read(*this) == index)
    return;
  scroll_tree_index_.Write(*this) = index;
  SetNeedsPushProperties(kChangedPropertyTreeIndex);
}

int Layer::scroll_tree_index(const PropertyTrees& property_trees) const {
  if (property_trees.sequence_number() !=
      property_tree_sequence_number_.Read(*this)) {
    return kInvalidPropertyNodeId;
  }
  return scroll_tree_index_.Read(*this);
}

bool Layer::scroll_tree_index_is_valid(
    const PropertyTrees& property_trees) const {
  return scroll_tree_index_.Read(*this) != kInvalidPropertyNodeId &&
         property_trees.sequence_number() ==
             property_tree_sequence_number_.Read(*this);
}

int Layer::scroll_tree_index() const {
  if (!IsAttached())
    return kInvalidPropertyNodeId;
  return scroll_tree_index(*layer_tree_host()->property_trees());
}

void Layer::SetOffsetToTransformParent(gfx::Vector2dF offset) {
  if (offset_to_transform_parent_.Read(*this) == offset)
    return;
  offset_to_transform_parent_.Write(*this) = offset;
  SetNeedsPushProperties();
  SetSubtreePropertyChanged();
}

void Layer::InvalidatePropertyTreesIndices() {
  SetTransformTreeIndex(kInvalidPropertyNodeId);
  SetClipTreeIndex(kInvalidPropertyNodeId);
  SetEffectTreeIndex(kInvalidPropertyNodeId);
  SetScrollTreeIndex(kInvalidPropertyNodeId);
}

void Layer::SetPropertyTreesNeedRebuild() {
  if (IsAttached())
    layer_tree_host()->property_trees()->set_needs_rebuild(true);
}

LayerDebugInfo& Layer::EnsureDebugInfo() {
  auto& info = debug_info_.Write(*this);
  if (!info) {
    info = std::make_unique<LayerDebugInfo>();
    // We just enabled debug info collection. Force PushPropertiesTo() to ensure
    // the first layer tree snapshot contains the debug info. Otherwise we will
    // push debug_info when we have other changes to push.
    SetNeedsPushProperties();
  }
  return *info;
}

void Layer::ClearDebugInfo() {
  if (!debug_info_.Read(*this))
    return;

  debug_info_.Write(*this).reset();
  SetNeedsPushProperties();
}

std::string Layer::DebugName() const {
  const auto* info = debug_info_.Read(*this);
  return info ? info->name : "";
}

std::string Layer::ToString() const {
  return base::StringPrintf(
      "layer_id: %d\n"
      "  name: %s\n"
      "  Bounds: %s\n"
      "  ElementId: %s\n"
      "  HitTestOpaqueness: %s\n"
      "  OffsetToTransformParent: %s\n"
      "  clip_tree_index: %d\n"
      "  effect_tree_index: %d\n"
      "  scroll_tree_index: %d\n"
      "  transform_tree_index: %d\n",
      id(), DebugName().c_str(), bounds().ToString().c_str(),
      element_id().ToString().c_str(),
      HitTestOpaquenessToString(hit_test_opaqueness()),
      offset_to_transform_parent().ToString().c_str(), clip_tree_index(),
      effect_tree_index(), scroll_tree_index(), transform_tree_index());
}

void Layer::SetIsDrawable(bool is_drawable) {
  DCHECK(IsPropertyChangeAllowed());
  if (inputs_.Read(*this).is_drawable == is_drawable)
    return;

  inputs_.Write(*this).is_drawable = is_drawable;
  UpdateDrawsContent();
}

void Layer::SetHideLayerAndSubtree(bool hide) {
  DCHECK(IsPropertyChangeAllowed());
  auto& inputs = EnsureLayerTreeInputs();
  if (inputs.hide_layer_and_subtree == hide)
    return;

  inputs.hide_layer_and_subtree = hide;
  SetNeedsCommit();
  SetPropertyTreesNeedRebuild();
  SetSubtreePropertyChanged();
}

void Layer::SetNeedsDisplayRect(const gfx::Rect& dirty_rect) {
  if (dirty_rect.IsEmpty())
    return;

  SetNeedsPushProperties();
  update_rect_.Write(*this).Union(dirty_rect);

  if (draws_content() && IsAttached() &&
      !ignore_set_needs_commit_for_test_.Read(*this))
    layer_tree_host()->SetNeedsUpdateLayers();
}

bool Layer::RequiresSetNeedsDisplayOnHdrHeadroomChange() const {
  return false;
}

bool Layer::IsSnappedToPixelGridInTarget() const {
  return false;
}

void Layer::PushDirtyPropertiesTo(LayerImpl* layer,
                                  uint8_t dirty_flag,
                                  const CommitState& commit_state,
                                  const ThreadUnsafeCommitState& unsafe_state) {
  const PropertyTrees& property_trees = unsafe_state.property_trees;

  if (dirty_flag & kChangedPropertyTreeIndex) {
    layer->SetTransformTreeIndex(transform_tree_index(property_trees));
    layer->SetHasTransformNode(has_transform_node());
    layer->SetEffectTreeIndex(effect_tree_index(property_trees));
    layer->SetClipTreeIndex(clip_tree_index(property_trees));
    layer->SetScrollTreeIndex(scroll_tree_index(property_trees));
  }

  if (dirty_flag & kChangedGeneralProperty) {
    // The element id should be set first because other setters may
    // depend on it. Referencing element id on a layer is
    // deprecated. http://crbug.com/709137
    const auto& inputs = inputs_.Read(*this);

    layer->SetElementId(inputs.element_id);
    layer->SetBackgroundColor(inputs.background_color);
    layer->SetSafeOpaqueBackgroundColor(SafeOpaqueBackgroundColor());
    layer->SetBounds(inputs.bounds);

    layer->SetOffsetToTransformParent(offset_to_transform_parent_.Read(*this));
    layer->SetDrawsContent(draws_content());
    layer->SetHitTestOpaqueness(inputs.hit_test_opaqueness);
    // subtree_property_changed_ is propagated to all descendants while building
    // property trees. So, it is enough to check it only for the current layer.
    if (subtree_property_changed_.Read(*this)) {
      layer->NoteLayerPropertyChanged();
    }
#if BUILDFLAG(ARKWEB_SAME_LAYER)
    layer_utils_->PushPropertiesToImpl(layer);
#endif
    layer->SetTouchActionRegion(inputs.touch_action_region);
    layer->SetContentsOpaque(inputs.contents_opaque);
    layer->SetContentsOpaqueForText(inputs.contents_opaque_for_text);
    layer->SetShouldCheckBackfaceVisibility(should_check_backface_visibility());

    // The property trees must be safe to access because they will be used below
    // to call |SetScrollOffsetClobberActiveValue|.
    DCHECK(layer->layer_tree_impl()->lifecycle().AllowsPropertyTreeAccess());

    // When a scroll offset animation is interrupted the new scroll position on
    // the pending tree will clobber any impl-side scrolling occurring on the
    // active tree. To do so, avoid scrolling the pending tree along with it
    // instead of trying to undo that scrolling later.
    if (unsafe_state.mutator_host->ScrollOffsetAnimationWasInterrupted(
            element_id())) {
      PropertyTrees* trees = layer->layer_tree_impl()->property_trees();
      trees->scroll_tree_mutable().SetScrollOffsetClobberActiveValue(
          layer->element_id());
    }

    layer->UnionUpdateRect(update_rect_.Read(*this));

    // debug_info_->invalidations, if exist, will be cleared in the function.
    layer->UpdateDebugInfo(debug_info_.Write(*this).get());

    if (inputs.rare_inputs) {
      layer->SetFilterQuality(inputs.rare_inputs->filter_quality);
      layer->SetDynamicRangeLimit(inputs.rare_inputs->dynamic_range_limit);
      layer->SetMainThreadScrollHitTestRegion(
          inputs.rare_inputs->main_thread_scroll_hit_test_region);
      layer->SetNonCompositedScrollHitTestRects(
          inputs.rare_inputs->non_composited_scroll_hit_test_rects);
      layer->SetCaptureBounds(inputs.rare_inputs->capture_bounds);
      layer->SetWheelEventHandlerRegion(inputs.rare_inputs->wheel_event_region);
    } else {
      layer->ResetRareProperties();
    }

    // Reset any state that should be cleared for the next update.
    subtree_property_changed_.Write(*this) = false;
    update_rect_.Write(*this) = gfx::Rect();
  }

  layer->SetNeedsPushProperties(dirty_flag);
}

void Layer::PushPropertiesTo(LayerImpl* layer_impl,
                             const CommitState& commit_state,
                             const ThreadUnsafeCommitState& unsafe_state) {
  TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
               "Layer::PushPropertiesTo");
  DCHECK(IsAttached());

  const uint8_t changed_props = changed_properties_.Read(*this);

  PushDirtyPropertiesTo(layer_impl, changed_props, commit_state, unsafe_state);

  // Reset change flags for next update.
  changed_properties_.Write(*this) = 0u;
}

void Layer::TakeCopyRequests(
    std::vector<std::unique_ptr<viz::CopyOutputRequest>>* requests) {
  if (!layer_tree_inputs())
    return;

  auto& layer_tree_inputs = layer_tree_inputs_.Write(*this);
  for (std::unique_ptr<viz::CopyOutputRequest>& request :
       layer_tree_inputs->copy_requests) {
    // Ensure the result callback is not invoked on the compositing thread.
    if (!request->has_result_task_runner()) {
      request->set_result_task_runner(
          layer_tree_host()->GetTaskRunnerProvider()->MainThreadTaskRunner());
    }
    if (request->has_area()) {
      request->set_area(
          gfx::IntersectRects(request->area(), gfx::Rect(bounds())));
    }
    requests->push_back(std::move(request));
  }

  layer_tree_inputs->copy_requests.clear();
}

std::unique_ptr<LayerImpl> Layer::CreateLayerImpl(
    LayerTreeImpl* tree_impl) const {
  return LayerImpl::Create(tree_impl, id());
}

bool Layer::HasDrawableContent() const {
  return inputs_.Read(*this).is_drawable;
}

void Layer::UpdateDrawsContent() {
  bool value = HasDrawableContent();
  DCHECK(inputs_.Read(*this).is_drawable || !value);
  if (!SetBitFlag(value, kDrawsContentFlagMask, /*invalidate=*/true))
    return;
  if (parent())
    mutable_parent()->AddDrawableDescendants(value ? 1 : -1);
}

int Layer::NumDescendantsThatDrawContent() const {
  return num_descendants_that_draw_content_.Read(*this);
}

bool Layer::Update() {
  DCHECK(IsAttached());
  return false;
}

void Layer::SetSubtreePropertyChanged() {
  if (subtree_property_changed_.Read(*this))
    return;
  subtree_property_changed_.Write(*this) = true;
  SetNeedsPushProperties();
}

bool Layer::IsOwnerThread() const {
  return !IsAttached() || layer_tree_host_->IsOwnerThread();
}

bool Layer::IsMainThread() const {
  return IsAttached() && layer_tree_host_->IsMainThread();
}

bool Layer::InProtectedSequence() const {
  return IsAttached() && layer_tree_host_->InProtectedSequence();
}

void Layer::WaitForProtectedSequenceCompletion() const {
  if (IsAttached())
    layer_tree_host_->WaitForProtectedSequenceCompletion();
}

bool Layer::IsUsingLayerLists() const {
  return IsAttached() && layer_tree_host_->IsUsingLayerLists();
}

// On<Property>Animated is called due to an ongoing accelerated animation.
// Since this animation is also being run on the compositor thread, there
// is no need to request a commit to push this value over, so the value is
// set directly rather than by calling Set<Property>.
void Layer::OnFilterAnimated(const FilterOperations& filters) {
  EnsureLayerTreeInputs().filters = filters;
}

void Layer::OnBackdropFilterAnimated(const FilterOperations& backdrop_filters) {
  EnsureLayerTreeInputs().backdrop_filters = backdrop_filters;
}

void Layer::OnOpacityAnimated(float opacity) {
  EnsureLayerTreeInputs().opacity = opacity;
}

void Layer::OnTransformAnimated(const gfx::Transform& transform) {
  EnsureLayerTreeInputs().transform = transform;
}

void Layer::SetTrilinearFiltering(bool trilinear_filtering) {
  auto& inputs = EnsureLayerTreeInputs();
  if (inputs.trilinear_filtering == trilinear_filtering)
    return;
  inputs.trilinear_filtering = trilinear_filtering;
  // When true, makes a RenderSurface which makes an effect node.
  SetPropertyTreesNeedRebuild();
  // Adding a RenderSurface may change how things in the subtree appear, since
  // it flattens transforms.
  SetSubtreePropertyChanged();
  SetNeedsCommit();
}

void Layer::IncrementMirrorCount() {
  SetMirrorCount(mirror_count() + 1);
}

void Layer::DecrementMirrorCount() {
  SetMirrorCount(mirror_count() - 1);
}

void Layer::SetMirrorCount(int mirror_count) {
  auto& inputs = EnsureLayerTreeInputs();
  if (inputs.mirror_count == mirror_count)
    return;

  DCHECK_LE(0, mirror_count);
  bool was_mirrored = inputs.mirror_count > 0;
  inputs.mirror_count = mirror_count;
  bool is_mirrored = inputs.mirror_count > 0;
  if (was_mirrored != is_mirrored)
    SetPropertyTreesNeedRebuild();
  SetNeedsPushProperties();
}

ElementListType Layer::GetElementTypeForAnimation() const {
  return ElementListType::ACTIVE;
}

void Layer::AddDrawableDescendants(int num) {
  DCHECK_GE(num_descendants_that_draw_content_.Read(*this), 0);
  DCHECK_GE(num_descendants_that_draw_content_.Read(*this) + num, 0);
  if (num == 0)
    return;
  num_descendants_that_draw_content_.Write(*this) += num;
  SetNeedsCommit();
  if (parent())
    mutable_parent()->AddDrawableDescendants(num);
}

void Layer::RunMicroBenchmark(MicroBenchmark* benchmark) {}

void Layer::SetElementId(ElementId id) {
  DCHECK(IsPropertyChangeAllowed());
  if (inputs_.Read(*this).element_id == id)
    return;
  TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("cc.debug"), "Layer::SetElementId",
               "element", id.ToString());
  auto& inputs = inputs_.Write(*this);
  if (IsAttached() && inputs.element_id)
    layer_tree_host()->UnregisterElement(inputs.element_id, this);

  inputs.element_id = id;

  if (IsAttached() && inputs.element_id)
    layer_tree_host()->RegisterElement(inputs.element_id, this);

  SetNeedsCommit();
}

gfx::Transform Layer::ScreenSpaceTransform() const {
  DCHECK_NE(transform_tree_index_.Read(*this), kInvalidPropertyNodeId);
  return draw_property_utils::ScreenSpaceTransform(
      this, layer_tree_host()->property_trees()->transform_tree());
}

void Layer::SetFilterQuality(PaintFlags::FilterQuality filter_quality) {
  const auto& rare_inputs = inputs_.Read(*this).rare_inputs;
  const auto old_filter_quality = rare_inputs ? rare_inputs->filter_quality
                                              : PaintFlags::FilterQuality::kLow;
  if (old_filter_quality == filter_quality) {
    return;
  }
  EnsureRareInputs().filter_quality = filter_quality;
  SetNeedsCommit();
}

void Layer::SetDynamicRangeLimit(
    PaintFlags::DynamicRangeLimitMixture dynamic_range_limit) {
  const auto& rare_inputs = inputs_.Read(*this).rare_inputs;
  const auto old_dynamic_range_limit =
      rare_inputs ? rare_inputs->dynamic_range_limit
                  : PaintFlags::DynamicRangeLimitMixture(
                        PaintFlags::DynamicRangeLimit::kHigh);
  if (old_dynamic_range_limit == dynamic_range_limit) {
    return;
  }
  EnsureRareInputs().dynamic_range_limit = dynamic_range_limit;
  SetNeedsCommit();
}

}  // namespace cc