/*
 * Copyright (C) 2013 Google Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "third_party/blink/renderer/core/layout/layout_block_flow.h"

#include <algorithm>
#include <memory>
#include <utility>

#include "third_party/blink/renderer/core/dom/first_letter_pseudo_element.h"
#include "third_party/blink/renderer/core/editing/editing_behavior.h"
#include "third_party/blink/renderer/core/editing/editor.h"
#include "third_party/blink/renderer/core/editing/position_with_affinity.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/local_frame_view.h"
#include "third_party/blink/renderer/core/frame/web_feature.h"
#include "third_party/blink/renderer/core/html/forms/html_button_element.h"
#include "third_party/blink/renderer/core/html/html_image_element.h"
#include "third_party/blink/renderer/core/html/shadow/shadow_element_names.h"
#include "third_party/blink/renderer/core/layout/absolute_utils.h"
#include "third_party/blink/renderer/core/layout/forms/layout_text_control_inner_editor.h"
#include "third_party/blink/renderer/core/layout/fragmentation_utils.h"
#include "third_party/blink/renderer/core/layout/hit_test_location.h"
#include "third_party/blink/renderer/core/layout/inline/inline_cursor.h"
#include "third_party/blink/renderer/core/layout/inline/offset_mapping.h"
#include "third_party/blink/renderer/core/layout/layout_block_flow.h"
#include "third_party/blink/renderer/core/layout/layout_inline.h"
#include "third_party/blink/renderer/core/layout/layout_object_inlines.h"
#include "third_party/blink/renderer/core/layout/layout_result.h"
#include "third_party/blink/renderer/core/layout/layout_view.h"
#include "third_party/blink/renderer/core/layout/logical_box_fragment.h"
#include "third_party/blink/renderer/core/layout/physical_box_fragment.h"
#include "third_party/blink/renderer/core/layout/shapes/shape_outside_info.h"
#include "third_party/blink/renderer/core/layout/table/layout_table.h"
#include "third_party/blink/renderer/core/layout/unpositioned_float.h"
#include "third_party/blink/renderer/core/paint/box_fragment_painter.h"
#include "third_party/blink/renderer/core/paint/inline_paint_context.h"
#include "third_party/blink/renderer/core/paint/object_paint_invalidator.h"
#include "third_party/blink/renderer/core/paint/paint_layer.h"
#include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h"
#include "third_party/blink/renderer/core/paint/text_overflow_post_layout_snapshot.h"
#include "third_party/blink/renderer/platform/heap/collection_support/clear_collection_scope.h"
#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "third_party/blink/renderer/platform/wtf/size_assertions.h"

namespace blink {

namespace {

// Return true if this block container allows inline children. If false is
// returned, and there are inline children, an anonymous block wrapper needs to
// be created.
bool AllowsInlineChildren(const LayoutBlockFlow& block) {
  const auto* inner_editor = DynamicTo<LayoutTextControlInnerEditor>(block);
  return !block.IsMulticolContainer() && !block.IsScrollMarkerGroup() &&
         !(inner_editor && inner_editor->IsMultiline());
}

bool IsInnerEditorChild(const LayoutBlockFlow& block) {
  return block.Parent() && block.Parent()->IsTextControlInnerEditor();
}

bool IsMergeableAnonymousBlock(const LayoutBlockFlow& block) {
  return block.IsAnonymousBlockFlow() && !block.BeingDestroyed() &&
         !block.IsViewTransitionRoot() && !IsInnerEditorChild(block);
}

inline const LayoutObject* PreviousSiblingIgnoringOutsideListMarker(
    const LayoutObject* object) {
  for (object = object->PreviousSibling();
       object && object->IsLayoutOutsideListMarker();
       object = object->PreviousSibling()) {
  }
  return object;
}

void ReparentSubsequentFloatingOrOutOfFlow(LayoutBlockFlow* from,
                                           LayoutBlockFlow* to,
                                           LayoutObject* next) {
  while (next && next->IsFloatingOrOutOfFlowPositioned()) {
    LayoutObject* sibling = next->NextSibling();
    from->MoveChildTo(to, next, nullptr, true);
    next = sibling;
  }
}

void ReparentPrecedingFloatingOrOutOfFlow(LayoutBlockFlow* from,
                                          LayoutBlockFlow* to,
                                          LayoutObject* prev) {
  while (prev && prev->IsFloatingOrOutOfFlowPositioned()) {
    LayoutObject* sibling = prev->NextSibling();
    from->MoveChildTo(to, prev, to->FirstChild(), true);
    prev = sibling;
  }
}

}  // namespace

struct SameSizeAsLayoutBlockFlow : public LayoutBlock {
  Member<void*> inline_node_data;
};

ASSERT_SIZE(LayoutBlockFlow, SameSizeAsLayoutBlockFlow);

LayoutBlockFlow::LayoutBlockFlow(ContainerNode* node) : LayoutBlock(node) {
  if (AllowsInlineChildren(*this)) {
    SetChildrenInline(true);
  }
}

LayoutBlockFlow::~LayoutBlockFlow() = default;

LayoutBlockFlow* LayoutBlockFlow::CreateAnonymous(Document& document,
                                                  const ComputedStyle& style) {
  auto* layout_block_flow = MakeGarbageCollected<LayoutBlockFlow>(nullptr);
  layout_block_flow->SetDocumentForAnonymous(document);
  layout_block_flow->SetStyle(&style);
  return layout_block_flow;
}

bool LayoutBlockFlow::IsInitialLetterBox() const {
  NOT_DESTROYED();
  return IsA<FirstLetterPseudoElement>(GetNode()) &&
         !StyleRef().InitialLetter().IsNormal();
}

bool LayoutBlockFlow::CanContainFirstFormattedLine() const {
  NOT_DESTROYED();
  // The 'text-indent' only affects a line if it is the first formatted
  // line of an element. For example, the first line of an anonymous block
  // box is only affected if it is the first child of its parent element.
  // https://drafts.csswg.org/css-text-3/#text-indent-property
  return !IsAnonymousBlockFlow() ||
         (RuntimeEnabledFeatures::TextBoxTrimForNestedListEnabled()
              ? !PreviousSiblingIgnoringOutsideListMarker(this)
              : !PreviousSibling()) ||
         IsFlexItem() || IsGridItem();
}

void LayoutBlockFlow::AddChildBeforeDescendant(
    LayoutObject* new_child,
    LayoutObject* before_descendant) {
  NOT_DESTROYED();
  DCHECK_NE(before_descendant->Parent(), this);

  LayoutObject* before_descendant_container = before_descendant->Parent();
  while (before_descendant_container->Parent() != this) {
    before_descendant_container = before_descendant_container->Parent();
  }
  DCHECK(before_descendant_container);

  // We really can't go on if what we have found isn't anonymous. We're not
  // supposed to use some random non-anonymous object and put the child there.
  // That's a recipe for security issues.
  CHECK(before_descendant_container->IsAnonymous());

  // If the requested insertion point is not one of our children, then this is
  // because there is an anonymous container within this object that contains
  // the beforeDescendant.
  if (before_descendant_container->IsAnonymousBlockFlow()) {
    // Insert the child into the anonymous block box instead of here. Note that
    // a LayoutOutsideListMarker is out-of-flow for tree building purposes, and
    // that is not inline level, although IsInline() is true.
    if ((new_child->IsInline() && !new_child->IsLayoutOutsideListMarker()) ||
        new_child->IsFloatingOrOutOfFlowPositioned() ||
        before_descendant->PreviousSibling()) {
      before_descendant_container->AddChild(new_child, before_descendant);
    } else {
      AddChild(new_child, before_descendant->Parent());
    }
    return;
  }

  DCHECK(before_descendant_container->IsTable());
  if (new_child->IsTablePart()) {
    // Insert into the anonymous table.
    before_descendant_container->AddChild(new_child, before_descendant);
    return;
  }

  LayoutObject* before_child =
      SplitAnonymousBoxesAroundChild(before_descendant);
  CHECK_EQ(before_child->Parent(), this);

  AddChild(new_child, before_child);
}

void LayoutBlockFlow::AddChild(LayoutObject* new_child,
                               LayoutObject* before_child) {
  NOT_DESTROYED();
  if (before_child && before_child->Parent() != this) {
    AddChildBeforeDescendant(new_child, before_child);
    return;
  }

  bool made_boxes_non_inline = false;

  // A block has to either have all of its children inline, or all of its
  // children as blocks.
  // So, if our children are currently inline and a block child has to be
  // inserted, we move all our inline children into anonymous block boxes.
  const bool child_is_block_level =
      !new_child->IsInline() && !new_child->IsFloatingOrOutOfFlowPositioned();

  if (ChildrenInline()) {
    if (child_is_block_level) {
      // Wrap the inline content in anonymous blocks, to allow for the new block
      // child to be inserted.
      MakeChildrenNonInline(before_child);
      made_boxes_non_inline = true;

      if (before_child && before_child->Parent() != this) {
        before_child = before_child->Parent();
        DCHECK(before_child->IsAnonymousBlockFlow());
        DCHECK_EQ(before_child->Parent(), this);
      }
    }
  } else if (!child_is_block_level) {
    // This block has block children. We may want to put the new child into an
    // anomyous block. Floats and out-of-flow children may live among either
    // block or inline children, so for such children, only put them inside an
    // anonymous block if one already exists. If the child is inline, on the
    // other hand, we *have to* put it inside an anonymous block, so create a
    // new one if there is none for us there already.
    LayoutObject* after_child =
        before_child ? before_child->PreviousSibling() : LastChild();

    if (after_child && after_child->IsAnonymousBlockFlow()) {
      after_child->AddChild(new_child);
      return;
    }

    // LayoutOutsideListMarker is out-of-flow for the tree building purpose,
    // and that is not inline level, but IsInline().
    if (new_child->IsInline() && !new_child->IsLayoutOutsideListMarker()) {
      // No suitable existing anonymous block-flow - create a new one.
      auto* new_block_flow = To<LayoutBlockFlow>(CreateAnonymousBlock());
      LayoutBox::AddChild(new_block_flow, before_child);
      // Reparent adjacent floating or out-of-flow siblings to the new block.
      ReparentPrecedingFloatingOrOutOfFlow(this, new_block_flow,
                                           new_block_flow->PreviousSibling());
      new_block_flow->AddChild(new_child);
      ReparentSubsequentFloatingOrOutOfFlow(this, new_block_flow,
                                            new_block_flow->NextSibling());
      return;
    }
  }

  // Skip the LayoutBlock override, since that one deals with anonymous child
  // insertion in a way that isn't sufficient for us, and can only cause trouble
  // at this point.
  LayoutBox::AddChild(new_child, before_child);

  // If we are anonymous, and made our children block-level we can promote our
  // children to our parent and destroy ourselves as we aren't needed anymore.
  auto* parent_block_flow = DynamicTo<LayoutBlockFlow>(Parent());
  if (parent_block_flow && made_boxes_non_inline &&
      IsMergeableAnonymousBlock(*this)) {
    LayoutObject* next_sibling = NextSibling();
    // Invoking RemoveChild may destroy `parent_block_flow`.
    parent_block_flow->Children()->RemoveChildNode(parent_block_flow, this);
    MoveAllChildrenTo(parent_block_flow, next_sibling, true);
    Destroy();
  }
}

void LayoutBlockFlow::RemoveChild(LayoutObject* old_child) {
  NOT_DESTROYED();

  // If the old_child is block-level we need to check if any adjacent siblings
  // are floating or out-of-flow positioned, and if so reparent them into the
  // inline-level anonymous block.
  {
    LayoutObject* prev = old_child->PreviousSibling();
    LayoutObject* next = old_child->NextSibling();
    if (prev && next && !old_child->IsInline()) {
      auto* prev_block_flow = DynamicTo<LayoutBlockFlow>(prev);
      if (prev_block_flow && IsMergeableAnonymousBlock(*prev_block_flow)) {
        ReparentSubsequentFloatingOrOutOfFlow(this, prev_block_flow, next);
      }

      auto* next_block_flow = DynamicTo<LayoutBlockFlow>(prev->NextSibling());
      if (next_block_flow && IsMergeableAnonymousBlock(*next_block_flow)) {
        ReparentPrecedingFloatingOrOutOfFlow(this, next_block_flow, prev);
      }
    }
  }

  LayoutBlock::RemoveChild(old_child);

  const bool is_inner_editor_child = IsAnonymous() && IsInnerEditorChild(*this);
  if (is_inner_editor_child && !BeingDestroyed()) {
    if (old_child->IsBR() && FirstChild()) {
      // We removed a LayoutBR from `this`. If this still contains LayoutTexts,
      // we move them to the next anonymous block. Then, remove `this` from the
      // parent.
      if (auto* next_block_flow = To<LayoutBlockFlow>(NextSibling())) {
        // `next_block_flow` might be a non-anonymous block-flow for InsertHTML
        // TestRendering.
        if (next_block_flow->IsAnonymous()) {
          MoveAllChildrenTo(next_block_flow, next_block_flow->FirstChild(),
                            /* full_remove_insert */ true);
        }
      }
    }
    if (!FirstChild()) {
      Destroy();
    }
    return;
  }

  if (FirstChild() == LastChild()) {
    // If the removal has knocked us down to containing only a single anonymous
    // box we can go ahead and pull the content right back up into our box.
    if (auto* child_block_flow = DynamicTo<LayoutBlockFlow>(FirstChild())) {
      if (IsMergeableAnonymousBlock(*child_block_flow)) {
        CollapseAnonymousBlockChild(child_block_flow);
      }
    }
  }

  if (FirstChild() && !BeingDestroyed() &&
      !old_child->IsFloatingOrOutOfFlowPositioned() &&
      !old_child->IsAnonymousBlockFlow()) {
    // If the child we're removing means that we can now treat all children as
    // inline without the need for anonymous blocks, then do that.
    MakeChildrenInlineIfPossible();
  }

  if (!FirstChild() && IsMergeableAnonymousBlock(*this)) {
    // If we don't have any children, and this was created as an anonymous
    // block, remove this object as we aren't needed anymore.
    Destroy();
  }
}

bool LayoutBlockFlow::CanMergeWith(const LayoutBoxModelObject& other) const {
  const auto* other_block_flow = DynamicTo<LayoutBlockFlow>(other);
  if (!other_block_flow) {
    return false;
  }

  return IsMergeableAnonymousBlock(*this) &&
         IsMergeableAnonymousBlock(*other_block_flow);
}

static bool AllowsCollapseAnonymousBlockChild(const LayoutBlockFlow& parent,
                                              const LayoutBlockFlow& child) {
  // It's possible that this block's destruction may have been triggered by the
  // child's removal. Just bail if the anonymous child block is already being
  // destroyed. See crbug.com/282088
  if (child.BeingDestroyed())
    return false;
  // The ViewTransitionRoot is also anonymous by design and shouldn't be
  // elided.
  if (child.IsViewTransitionRoot()) {
    return false;
  }
  return !child.ChildrenInline() || AllowsInlineChildren(parent);
}

void LayoutBlockFlow::CollapseAnonymousBlockChild(LayoutBlockFlow* child) {
  NOT_DESTROYED();
  if (!AllowsCollapseAnonymousBlockChild(*this, *child))
    return;
  SetNeedsLayoutAndIntrinsicWidthsRecalcAndFullPaintInvalidation(
      layout_invalidation_reason::kChildAnonymousBlockChanged);

  child->MoveAllChildrenTo(this, child->NextSibling(), child->HasLayer());
  SetChildrenInline(child->ChildrenInline());

  Children()->RemoveChildNode(this, child, child->HasLayer());
  child->Destroy();
}

void LayoutBlockFlow::MakeChildrenInlineIfPossible() {
  NOT_DESTROYED();
  if (!AllowsInlineChildren(*this)) {
    return;
  }
  // Collapsing away anonymous wrappers isn't relevant for the children of
  // anonymous blocks.
  if (IsAnonymousBlockFlow()) {
    return;
  }

  HeapVector<Member<LayoutBlockFlow>, 3> blocks_to_remove;
  for (LayoutObject* child = FirstChild(); child;
       child = child->NextSibling()) {
    if (child->IsFloating())
      continue;
    if (child->IsOutOfFlowPositioned())
      continue;

    // There are still block children in the container, so any anonymous
    // wrappers are still needed.
    auto* child_block_flow = DynamicTo<LayoutBlockFlow>(child);
    if (!child->IsAnonymousBlockFlow() || !child_block_flow) {
      return;
    }
    // If one of the children is being destroyed then it is unsafe to clean up
    // anonymous wrappers as the
    // entire branch may be being destroyed.
    if (child_block_flow->BeingDestroyed())
      return;
    // We are only interested in removing anonymous wrappers if there are inline
    // siblings underneath them.
    if (!child->ChildrenInline())
      return;

    blocks_to_remove.push_back(child_block_flow);
  }

  for (LayoutBlockFlow* child : blocks_to_remove)
    CollapseAnonymousBlockChild(child);
  SetChildrenInline(true);
}

static void GetInlineRun(LayoutObject* start,
                         LayoutObject* boundary,
                         LayoutObject*& inline_run_start,
                         LayoutObject*& inline_run_end) {
  // Beginning at |start| we find the largest contiguous run of inlines that
  // we can.  We denote the run with start and end points, |inlineRunStart|
  // and |inlineRunEnd|.  Note that these two values may be the same if
  // we encounter only one inline.
  //
  // We skip any non-inlines we encounter as long as we haven't found any
  // inlines yet.
  //
  // |boundary| indicates a non-inclusive boundary point.  Regardless of whether
  // |boundary| is inline or not, we will not include it in a run with inlines
  // before it. It's as though we encountered a non-inline.

  // Start by skipping as many non-inlines as we can.
  LayoutObject* curr = start;

  // LayoutOutsideListMarker is out-of-flow for the tree building purpose.
  // Skip here because it's the first child.
  if (curr && curr->IsLayoutOutsideListMarker()) {
    curr = curr->NextSibling();
  }

  bool saw_inline;
  do {
    while (curr &&
           !(curr->IsInline() || curr->IsFloatingOrOutOfFlowPositioned()))
      curr = curr->NextSibling();

    inline_run_start = inline_run_end = curr;

    if (!curr)
      return;  // No more inline children to be found.

    saw_inline = curr->IsInline();

    curr = curr->NextSibling();
    while (curr &&
           (curr->IsInline() || curr->IsFloatingOrOutOfFlowPositioned()) &&
           (curr != boundary)) {
      inline_run_end = curr;
      if (curr->IsInline())
        saw_inline = true;
      curr = curr->NextSibling();
    }
  } while (!saw_inline);
}

void LayoutBlockFlow::MakeChildrenNonInline(LayoutObject* insertion_point) {
  NOT_DESTROYED();

  // makeChildrenNonInline takes a block whose children are *all* inline and it
  // makes sure that inline children are coalesced under anonymous blocks.
  // If |insertionPoint| is defined, then it represents the insertion point for
  // the new block child that is causing us to have to wrap all the inlines.
  // This means that we cannot coalesce inlines before |insertionPoint| with
  // inlines following |insertionPoint|, because the new child is going to be
  // inserted in between the inlines, splitting them.
  DCHECK(!insertion_point || insertion_point->Parent() == this);

  SetChildrenInline(false);
  ClearInlineNodeData();

  LayoutObject* child = FirstChild();
  if (!child)
    return;

  while (child) {
    LayoutObject* inline_run_start;
    LayoutObject* inline_run_end;
    GetInlineRun(child, insertion_point, inline_run_start, inline_run_end);

    if (!inline_run_start)
      break;

    child = inline_run_end->NextSibling();

    LayoutBlock* block = CreateAnonymousBlock();
    Children()->InsertChildNode(this, block, inline_run_start);
    MoveChildrenTo(block, inline_run_start, child,
                   /*full_remove_insert=*/false);
  }

#if DCHECK_IS_ON()
  for (LayoutObject* c = FirstChild(); c; c = c->NextSibling())
    DCHECK(!c->IsInline() || c->IsLayoutOutsideListMarker());
#endif

  SetShouldDoFullPaintInvalidation();
}

bool LayoutBlockFlow::ShouldTruncateOverflowingText() const {
  NOT_DESTROYED();
  const LayoutObject* object_to_check = this;
  if (IsAnonymousBlockFlow()) {
    const LayoutObject* parent = Parent();
    if (!parent || !parent->BehavesLikeBlockContainer()) {
      return false;
    }
    object_to_check = parent;
  }
  if (!object_to_check->HasNonVisibleOverflow() ||
      object_to_check->StyleRef().TextOverflow().IsClip()) {
    return false;
  }
  // If selection focus is inside this element, don't truncate (show full text).
  if (RuntimeEnabledFeatures::TextOverflowClipWithSelectionEnabled() &&
      object_to_check->ContainsSelectionFocus()) {
    return false;
  }
  if (RuntimeEnabledFeatures::DisableEllipsisWhenScrolledEnabled()) {
    if (const auto* box = DynamicTo<LayoutBox>(object_to_check)) {
      if (auto* scrollable_area = box->GetScrollableArea()) {
        auto* snapshot = scrollable_area->GetTextOverflowPostLayoutSnapshot();
        if (!snapshot) {
          snapshot = MakeGarbageCollected<TextOverflowPostLayoutSnapshot>(
              *scrollable_area);
        }
        return !snapshot->IsScrolled();
      }
    }
  }
  return true;
}

Node* LayoutBlockFlow::NodeForHitTest() const {
  NOT_DESTROYED();
  // If we are in the margins of block elements that are part of a
  // block-in-inline we're actually still inside the enclosing element
  // that was split. Use the appropriate inner node.
  if (IsBlockInInline()) [[unlikely]] {
    DCHECK(Parent());
    DCHECK(Parent()->IsLayoutInline());
    return Parent()->NodeForHitTest();
  }
  return LayoutBlock::NodeForHitTest();
}

void LayoutBlockFlow::AddOutlineRects(
    OutlineRectCollector& collector,
    LayoutObject::OutlineInfo* info,
    const PhysicalOffset& additional_offset,
    OutlineType include_block_overflows) const {
  NOT_DESTROYED();

  // TODO(crbug.com/40155711): Currently |PhysicalBoxFragment| does not support
  // NG block fragmentation. Fallback to the legacy code path.
  if (PhysicalFragmentCount() == 1) {
    const PhysicalBoxFragment* fragment = GetPhysicalFragment(0);
    if (fragment->HasItems()) {
      fragment->AddSelfOutlineRects(additional_offset, include_block_overflows,
                                    collector, info);
      return;
    }
  }

  LayoutBlock::AddOutlineRects(collector, info, additional_offset,
                               include_block_overflows);
}

void LayoutBlockFlow::DirtyLinesFromChangedChild(LayoutObject* child) {
  NOT_DESTROYED();

  // We need to dirty line box fragments only if the child is once laid out in
  // LayoutNG inline formatting context. New objects are handled in
  // InlineNode::MarkLineBoxesDirty().
  if (child->IsInLayoutNGInlineFormattingContext()) {
    FragmentItems::DirtyLinesFromChangedChild(*child, *this);
  }
}

bool LayoutBlockFlow::AllowsColumns() const {
  NOT_DESTROYED();
  // TODO(crbug.com/40414064): Ruby elements manage child insertion in a special
  // way, and this would come in conflict with the legacy multicol
  // implementation. However, that implementation is now gone, and it should be
  // safe to enable multicol for ruby.
  if (IsRuby())
    return false;

  // We don't allow custom layout and multicol on the same object. This is
  // similar to not allowing it for flexbox, grids and tables (although those
  // don't create LayoutBlockFlow, so we don't need to check for those here).
  if (StyleRef().IsDisplayLayoutCustom()) {
    return false;
  }

  // MathML layout objects don't support multicol.
  if (IsMathML())
    return false;

  if (IsA<HTMLImageElement>(GetNode())) {
    // We may create a LayoutBlockFlow for the ALT text of a broken image. Such
    // a block should not become a multicol container.
    return false;
  }

  return true;
}

void LayoutBlockFlow::UpdateForMulticol() {
  NOT_DESTROYED();
  auto ShouldBeMulticol = [this]() -> bool {
    if (!StyleRef().SpecifiesColumns() || !AllowsColumns()) {
      return false;
    }

    // Multicol is applied to the anonymous content box child of a fieldset, not
    // the fieldset itself, and the fieldset code will make sure that any
    // relevant multicol properties are copied to said child.
    if (IsFieldset()) {
      return false;
    }

    // Form controls are replaced content (also when implemented as a regular
    // block), and are therefore not supposed to support multicol. Buttons
    // contain regular flow content, though, so columns apply there.
    const auto* element = DynamicTo<Element>(GetNode());
    if (element && element->IsFormControlElement() &&
        !IsA<HTMLButtonElement>(element)) {
      return false;
    }

    return true;
  };

  bool should_be_multicol = ShouldBeMulticol();
  if (should_be_multicol == IsMulticolContainer()) {
    return;
  }

  SetIsMulticolContainer(should_be_multicol);

  if (IsListItem()) {
    UseCounter::Count(GetDocument(), WebFeature::kMultiColAndListItem);
  }

  // Descendants are inside multicol if this is now a multicol container, or if
  // this ex-multicol container is inside an outer multicol container.
  bool is_inside_multicol = should_be_multicol || IsInsideMulticol();
  for (LayoutObject* child = FirstChild(); child;
       child = child->NextSibling()) {
    child->SetIsInsideMulticolIncludingDescendants(is_inside_multicol);
  }

  if (should_be_multicol) {
    // Inline children need to be wrapped inside an anonymous block. This
    // anonymous block will participate in the fragmentation context established
    // by `this`, whereas `this` (the multicol container itself) won't.
    MakeChildrenNonInline();
  } else {
    // No longer a multicol, so no need to force anonymous blocks around all
    // inline children.
    MakeChildrenInlineIfPossible();
  }
}

void LayoutBlockFlow::SetShouldDoFullPaintInvalidationForFirstLine() {
  NOT_DESTROYED();
  DCHECK(ChildrenInline());

  const auto fragments = PhysicalFragments();
  if (fragments.IsEmpty()) {
    return;
  }
  for (const PhysicalBoxFragment& fragment : fragments) {
    InlineCursor first_line(fragment);
    if (!first_line) {
      continue;
    }
    first_line.MoveToFirstLine();
    if (!first_line) {
      continue;
    }
    if (first_line.Current().UsesFirstLineStyle()) {
      // Mark all descendants of the first line if first-line style.
      for (InlineCursor descendants = first_line.CursorForDescendants();
           descendants; descendants.MoveToNext()) {
        const FragmentItem* item = descendants.Current().Item();
        if (item->IsLayoutObjectDestroyedOrMoved()) [[unlikely]] {
          descendants.MoveToNextSkippingChildren();
          continue;
        }
        LayoutObject* layout_object = item->GetMutableLayoutObject();
        DCHECK(layout_object);
        layout_object->StyleRef().ClearCachedPseudoElementStyles();
        item->GetMutableForPainting().InvalidateInkOverflow();
        layout_object->SetShouldDoFullPaintInvalidation();
      }
      StyleRef().ClearCachedPseudoElementStyles();
      SetShouldDoFullPaintInvalidation();
      return;
    }
  }
}

PositionWithAffinity LayoutBlockFlow::PositionForPoint(
    const PhysicalOffset& point) const {
  NOT_DESTROYED();
  DCHECK_GE(GetDocument().Lifecycle().GetState(),
            DocumentLifecycle::kPrePaintClean);

  if (IsInline()) {
    PositionWithAffinity position =
        PositionForPointIfOutsideAtomicInlineLevel(point);
    if (!position.IsNull())
      return position;
  }
  if (!ChildrenInline())
    return LayoutBlock::PositionForPoint(point);

  if (PhysicalFragmentCount()) {
    return PositionForPointInFragments(point);
  }

  return CreatePositionWithAffinity(0);
}

bool LayoutBlockFlow::ShouldMoveCaretToHorizontalBoundaryWhenPastTopOrBottom()
    const {
  NOT_DESTROYED();
  return GetDocument()
      .GetFrame()
      ->GetEditor()
      .Behavior()
      .ShouldMoveCaretToHorizontalBoundaryWhenPastTopOrBottom();
}

void LayoutBlockFlow::InvalidateDisplayItemClients(
    PaintInvalidationReason invalidation_reason) const {
  NOT_DESTROYED();
  LayoutBlock::InvalidateDisplayItemClients(invalidation_reason);

  InlineCursor cursor(*this);
  if (!cursor) {
    return;
  }

  ObjectPaintInvalidator paint_invalidator(*this);
  // Line boxes record hit test data (see BoxFragmentPainter::PaintLineBox)
  // and should be invalidated if they change.
  bool invalidate_all_lines =
      HasEffectiveAllowedTouchAction() || InsideBlockingWheelEventHandler();

  for (cursor.MoveToFirstLine(); cursor; cursor.MoveToNextLine()) {
    // The first line LineBoxFragment paints the ::first-line background.
    // Because it may be expensive to figure out if the first line is affected
    // by any ::first-line selectors at all, we just invalidate
    // unconditionally which is typically cheaper.
    if (invalidate_all_lines || cursor.Current().UsesFirstLineStyle()) {
      DCHECK(cursor.Current().GetDisplayItemClient());
      paint_invalidator.InvalidateDisplayItemClient(
          *cursor.Current().GetDisplayItemClient(), invalidation_reason);
    }
    if (!invalidate_all_lines) {
      break;
    }
  }
}

}  // namespace blink
