Commit message: None
CVE: None
Vulnerability description: None

Project: Chrome

LogicalSize NGColumnLayoutAlgorithm::CalculateColumnSize(
    const LogicalSize& content_box_size) {
  LogicalSize column_size = content_box_size;
  DCHECK_GE(column_size.inline_size, LayoutUnit());
  column_size.inline_size =
      ResolveUsedColumnInlineSize(column_size.inline_size, Style());

  if (NeedsColumnBalancing(column_size.block_size, Style())) {
    int used_count =
        ResolveUsedColumnCount(content_box_size.inline_size, Style());
    column_size.block_size =
        CalculateBalancedColumnBlockSize(column_size, used_count);
  }

  return column_size;
}

Project: Chrome

base::Optional<MinMaxSize> NGColumnLayoutAlgorithm::ComputeMinMaxSize(
    const MinMaxSizeInput& input) const {
  NGConstraintSpace space = CreateConstraintSpaceForMinMax();
  NGFragmentGeometry fragment_geometry =
      CalculateInitialMinMaxFragmentGeometry(space, Node());
  NGBlockLayoutAlgorithm algorithm({Node(), fragment_geometry, space});
  MinMaxSizeInput child_input(input);
  child_input.size_type = NGMinMaxSizeType::kContentBoxSize;
  base::Optional<MinMaxSize> min_max_sizes =
      algorithm.ComputeMinMaxSize(child_input);
  DCHECK(min_max_sizes.has_value());
  MinMaxSize sizes = *min_max_sizes;

  if (!Style().HasAutoColumnWidth()) {
    sizes.min_size =
        std::max(sizes.min_size, LayoutUnit(Style().ColumnWidth()));
    sizes.max_size = std::max(sizes.max_size, sizes.min_size);
  }

  int column_count = Style().ColumnCount();
  DCHECK_GE(column_count, 1);
  sizes.min_size *= column_count;
  sizes.max_size *= column_count;
  LayoutUnit column_gap = ResolveUsedColumnGap(LayoutUnit(), Style());
  sizes += column_gap * (column_count - 1);

  if (input.size_type == NGMinMaxSizeType::kBorderBoxSize) {
    sizes += border_scrollbar_padding_.InlineSum();
  }

  return sizes;
}

Project: Chrome

LayoutUnit NGColumnLayoutAlgorithm::ConstrainColumnBlockSize(
    LayoutUnit size) const {

  LayoutUnit extra = border_scrollbar_padding_.BlockSum();
  size += extra;

  const ComputedStyle& style = Style();
  LayoutUnit max = ResolveMaxBlockLength(
      ConstraintSpace(), style, border_padding_, style.LogicalMaxHeight(), size,
      LengthResolvePhase::kLayout);
  LayoutUnit extent = ResolveMainBlockLength(
      ConstraintSpace(), style, border_padding_, style.LogicalHeight(), size,
      LengthResolvePhase::kLayout);
  if (extent != kIndefiniteSize) {
    max = std::min(max, extent);
  }

  size = std::min(size, max);
  return size - extra;
}

Project: Chrome

NGConstraintSpace NGColumnLayoutAlgorithm::CreateConstraintSpaceForBalancing(
    const LogicalSize& column_size) const {
  NGConstraintSpaceBuilder space_builder(
      ConstraintSpace(), Style().GetWritingMode(), /* is_new_fc */ true);
  space_builder.SetAvailableSize({column_size.inline_size, kIndefiniteSize});
  space_builder.SetPercentageResolutionSize(column_size);
  space_builder.SetIsAnonymous(true);
  space_builder.SetIsIntermediateLayout(true);

  return space_builder.ToConstraintSpace();
}

Project: Chrome

NGConstraintSpace NGColumnLayoutAlgorithm::CreateConstraintSpaceForMinMax()
    const {
  NGConstraintSpaceBuilder space_builder(
      ConstraintSpace(), Style().GetWritingMode(), /* is_new_fc */ true);
  space_builder.SetIsAnonymous(true);
  space_builder.SetIsIntermediateLayout(true);

  return space_builder.ToConstraintSpace();
}

Project: Chrome

scoped_refptr<const NGLayoutResult> NGColumnLayoutAlgorithm::Layout() {
  LogicalSize border_box_size = container_builder_.InitialBorderBoxSize();
  LogicalSize content_box_size =
      ShrinkAvailableSize(border_box_size, border_scrollbar_padding_);
  LogicalSize column_size = CalculateColumnSize(content_box_size);

  WritingMode writing_mode = ConstraintSpace().GetWritingMode();

  LayoutUnit column_block_offset = IsResumingLayout(BreakToken())
                                       ? LayoutUnit()
                                       : border_scrollbar_padding_.block_start;

  LayoutUnit previously_used_block_size;
  if (const auto* token = BreakToken())
    previously_used_block_size = token->UsedBlockSize();

  bool needs_more_fragments_in_outer = false;
  if (ConstraintSpace().HasBlockFragmentation()) {
    column_size.block_size -= previously_used_block_size;

    LayoutUnit available_outer_space =
        ConstraintSpace().FragmentainerSpaceAtBfcStart() - column_block_offset;
    if (column_size.block_size > available_outer_space) {
      column_size.block_size = available_outer_space;
      needs_more_fragments_in_outer = true;
    }
  }
  LayoutUnit column_inline_progression =
      column_size.inline_size +
      ResolveUsedColumnGap(content_box_size.inline_size, Style());
  int used_column_count =
      ResolveUsedColumnCount(content_box_size.inline_size, Style());

  if (ConstraintSpace().HasBlockFragmentation())
    container_builder_.SetHasBlockFragmentation();

  do {
    scoped_refptr<const NGBlockBreakToken> break_token;
    if (const auto* token = BreakToken()) {
      const auto child_tokens = token->ChildBreakTokens();
      if (child_tokens.size()) {
        break_token =
            To<NGBlockBreakToken>(child_tokens[child_tokens.size() - 1]);
      }
    }

    LayoutUnit intrinsic_block_size;
    LayoutUnit column_inline_offset(border_scrollbar_padding_.inline_start);
    int actual_column_count = 0;
    int forced_break_count = 0;

    LayoutUnit minimal_space_shortage(LayoutUnit::Max());

    bool separate_leading_margins = true;

    do {
      NGConstraintSpace child_space = CreateConstraintSpaceForColumns(
          column_size, separate_leading_margins);

      NGFragmentGeometry fragment_geometry =
          CalculateInitialFragmentGeometry(child_space, Node());

      NGBlockLayoutAlgorithm child_algorithm(
          {Node(), fragment_geometry, child_space, break_token.get()});
      child_algorithm.SetBoxType(NGPhysicalFragment::kColumnBox);
      scoped_refptr<const NGLayoutResult> result = child_algorithm.Layout();
      const auto& column = result->PhysicalFragment();

      LogicalOffset logical_offset(column_inline_offset, column_block_offset);
      container_builder_.AddChild(column, logical_offset);

      LayoutUnit space_shortage = result->MinimalSpaceShortage();
      if (space_shortage > LayoutUnit()) {
        minimal_space_shortage =
            std::min(minimal_space_shortage, space_shortage);
      }
      actual_column_count++;
      if (result->HasForcedBreak()) {
        forced_break_count++;
        separate_leading_margins = true;
      } else {
        separate_leading_margins = false;
      }

      LayoutUnit block_size = NGFragment(writing_mode, column).BlockSize();
      intrinsic_block_size =
          std::max(intrinsic_block_size, column_block_offset + block_size);

      column_inline_offset += column_inline_progression;
      break_token = To<NGBlockBreakToken>(column.BreakToken());

      if (ConstraintSpace().HasBlockFragmentation() && break_token &&
          actual_column_count >= used_column_count &&
          needs_more_fragments_in_outer) {
        LayoutUnit fragment_block_size = child_space.FragmentainerBlockSize();
        LayoutUnit used_block_size = fragment_block_size;
        if (previously_used_block_size)
          used_block_size += previously_used_block_size;
        else
          fragment_block_size += border_scrollbar_padding_.block_start;
        container_builder_.SetUsedBlockSize(used_block_size);
        container_builder_.SetBlockSize(fragment_block_size);
        container_builder_.SetDidBreak();
        break;
      }
    } while (break_token);

    if (container_builder_.DidBreak())
      break;

    if (actual_column_count > used_column_count &&
        actual_column_count > forced_break_count + 1 &&
        minimal_space_shortage != LayoutUnit::Max()) {
      LayoutUnit new_column_block_size =
          StretchColumnBlockSize(minimal_space_shortage, column_size.block_size,
                                 content_box_size.block_size);

      DCHECK_GE(new_column_block_size, column_size.block_size);
      if (new_column_block_size > column_size.block_size) {
        column_size.block_size = new_column_block_size;
        container_builder_.RemoveChildren();
        continue;
      }
    }
    container_builder_.SetIntrinsicBlockSize(intrinsic_block_size);
    break;
  } while (true);


  if (!container_builder_.DidBreak()) {
    LayoutUnit block_size;
    if (border_box_size.block_size == kIndefiniteSize) {
      block_size =
          column_size.block_size + border_scrollbar_padding_.BlockSum();
    } else {
      block_size = border_box_size.block_size - previously_used_block_size;
    }
    container_builder_.SetBlockSize(block_size);
  }

  NGOutOfFlowLayoutPart(
      Node(), ConstraintSpace(),
      container_builder_.Borders() + container_builder_.Scrollbar(),
      &container_builder_)
      .Run();

  return container_builder_.ToBoxFragment();
}

Project: Chrome

 NGColumnLayoutAlgorithm::NGColumnLayoutAlgorithm(
    const NGLayoutAlgorithmParams& params)
    : NGLayoutAlgorithm(params),
      border_padding_(params.fragment_geometry.border +
                      params.fragment_geometry.padding),
      border_scrollbar_padding_(border_padding_ +
                                params.fragment_geometry.scrollbar) {
  container_builder_.SetIsNewFormattingContext(
      params.space.IsNewFormattingContext());
  container_builder_.SetInitialFragmentGeometry(params.fragment_geometry);
}

Project: Chrome

inline bool NeedsColumnBalancing(LayoutUnit block_size,
                                 const ComputedStyle& style) {
  return block_size == kIndefiniteSize ||
          style.GetColumnFill() == EColumnFill::kBalance;
 }

Project: Chrome

LayoutUnit NGColumnLayoutAlgorithm::StretchColumnBlockSize(
    LayoutUnit minimal_space_shortage,
    LayoutUnit current_column_size,
    LayoutUnit container_content_box_block_size) const {
  if (!NeedsColumnBalancing(container_content_box_block_size, Style()))
    return current_column_size;
  LayoutUnit length = current_column_size + minimal_space_shortage;
  return ConstrainColumnBlockSize(length);
}

Project: Chrome

  String DumpFragmentTree(const NGPhysicalBoxFragment* fragment) {
    NGPhysicalFragment::DumpFlags flags =
        NGPhysicalFragment::DumpHeaderText | NGPhysicalFragment::DumpSubtree |
        NGPhysicalFragment::DumpIndentation | NGPhysicalFragment::DumpOffset |
        NGPhysicalFragment::DumpSize;

    return fragment->DumpFragmentTree(flags);
  }

Project: Chrome

  String DumpFragmentTree(Element* element) {
    auto fragment = RunBlockLayoutAlgorithm(element);
    return DumpFragmentTree(fragment.get());
  }

Project: Chrome

  NGColumnLayoutAlgorithmTest()
      : ScopedLayoutNGBlockFragmentationForTest(true) {}

Project: Chrome

  scoped_refptr<const NGPhysicalBoxFragment> RunBlockLayoutAlgorithm(
      Element* element) {
    NGBlockNode container(ToLayoutBox(element->GetLayoutObject()));
    NGConstraintSpace space = ConstructBlockLayoutTestConstraintSpace(
        WritingMode::kHorizontalTb, TextDirection::kLtr,
        LogicalSize(LayoutUnit(1000), kIndefiniteSize));
    return NGBaseLayoutAlgorithmTest::RunBlockLayoutAlgorithm(container, space);
  }