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

#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_PAGINATED_ROOT_LAYOUT_ALGORITHM_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_PAGINATED_ROOT_LAYOUT_ALGORITHM_H_

#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/core/layout/box_fragment_builder.h"
#include "third_party/blink/renderer/core/layout/layout_algorithm.h"
#include "third_party/blink/renderer/platform/wtf/wtf_size_t.h"

namespace blink {

class BlockBreakToken;
class BlockNode;
class ConstraintSpace;
class CountersAttachmentContext;
struct PageAreaLayoutParams;

// This is the root layout algorithm when the document is paginated (for
// printing or PDF generation). It creates a series of page box child fragments,
// one per page.
//
// The fragment structure is different when paginating. The root fragment is
// generated by the LayoutView, as normally, but then there will be additional
// container fragments between the root fragment and the document contents, to
// support fragmentation, page properties and margins.
//
// This algorithm will create page container fragments, one for each page. Each
// page container contains a page border box fragment, and fragments for any
// page margin boxes (e.g. for author-specified headers and footers). A page
// border contains a page area box, which is a fragmentainer, into which
// document contents flow. The document root (e.g. <html>) and all descendants
// will be placed inside the page area fragmentainers.
//
// Example:
//
//   <!DOCTYPE html>
//   <style>
//     @page { size:600px 800px; margin: 50px; border: 10px solid; }
//   </style>
//   <div id="a">...</div>
//   <div id="b" style="break-before:page; height:100px;"></div>
//
// Fragment tree:
//
//   LayoutView
//     Page container          0,0    600x800
//       Page border box       50,50  500x700
//         Page area           0,0    480x680
//           HTML              0,0    480x680
//             BODY            8,8    464x672
//               DIV id="a"
//                 Line with "..."
//     Page container          0,0    600x800
//       Page border box       50,50  500x700
//         Page area           0,0    480x680
//           HTML              0,0    480x108
//             BODY            8,0    464x100
//               DIV id="b"    0,0    464x100
//
// A page area can be seen as the content box of a page box, although its offset
// doesn't reflect that (there's a 10px page border, but the offset is still
// 0,0). A page area fragment (and its entire subtree, which represents some of
// the document contents) lives in a different coordinate system than the rest.
//
// The page container fragments are sized and positioned with respect to the
// destination output size. If printing to an actual printer, there's a given
// physical paper size, and no @page size can change that fact (the output may
// need to be scaled and/or centered on paper). The page border box and page
// area, on the other hand, live in the coordinate system established by layout,
// which honors @page size, input scale factor from print settings, and
// additionally any shrink factor calculated by layout (if some content is too
// wide to fit the page size, the layout viewport may be enlarged to prevent it
// from overflowing (as much), an thus there needs to be an additional scale
// factor to shrink it back down to the destination size).
//
// Note that, although the page border box is in the coordinate system of layout
// as far as scaling is concerned, the page border box *offset* is in the
// destination coordinate system. Example: The paper size is 816x1056 (US
// Letter). Input scale factor is 2. Margins are 50px on each side. The size of
// the page container becomes 816x1056. The offset to the page border box
// becomes 50,50. The size of the page border box (and page area) becomes
// 358x478 - subtract margins from 816x1056, then divide by 2 (the inverse scale
// factor used by layout).
//
// When painted, the page areas are "stitched" together in the block direction,
// so that content that overflows from one page correctly overflows into the
// other pages, rather than being lost. Overflowing content may for instance be
// tall monolithic content, or transformed elements.
//
// See also https://drafts.csswg.org/css-page-3/#page-model
class CORE_EXPORT PaginatedRootLayoutAlgorithm
    : public LayoutAlgorithm<BlockNode, BoxFragmentBuilder, BlockBreakToken> {
 public:
  struct PageContainerResult {
    STACK_ALLOCATED();

   public:
    PageContainerResult(const PhysicalBoxFragment& fragment,
                        const BlockBreakToken* fragmentainer_break_token,
                        const CountersAttachmentContext& counters_context,
                        bool needs_total_page_count)
        : fragment(&fragment),
          fragmentainer_break_token(fragmentainer_break_token),
          counters_context(counters_context.ShallowClone()),
          needs_total_page_count(needs_total_page_count) {}

    const PhysicalBoxFragment* fragment;
    const BlockBreakToken* fragmentainer_break_token;
    CountersAttachmentContext counters_context;
    bool needs_total_page_count;
  };

  explicit PaginatedRootLayoutAlgorithm(const LayoutAlgorithmParams& params);

  const LayoutResult* Layout();

  MinMaxSizesResult ComputeMinMaxSizes(const MinMaxSizesFloatInput&) {
    NOTREACHED();
  }

  // Create an empty page box fragment, modeled after an existing fragmentainer.
  // The resulting page box may then be used and mutated by the out-of-flow
  // layout code, to add out-of-flow descendants.
  static const PhysicalBoxFragment& CreateEmptyPage(
      const BlockNode& node,
      const ConstraintSpace& parent_space,
      wtf_size_t page_index,
      const PhysicalBoxFragment& previous_fragmentainer,
      bool* needs_total_page_count);

 private:
  PageContainerResult LayoutPageContainer(
      wtf_size_t page_index,
      wtf_size_t total_page_count,
      const AtomicString& page_name,
      const CountersAttachmentContext& counters_context,
      const PageAreaLayoutParams& params,
      const PhysicalBoxFragment* existing_page_container = nullptr) const {
    return LayoutPageContainer(Node(), GetConstraintSpace(), page_index,
                               total_page_count, page_name, counters_context,
                               params, existing_page_container);
  }

  static PageContainerResult LayoutPageContainer(
      const BlockNode& root_node,
      const ConstraintSpace& parent_space,
      wtf_size_t page_index,
      wtf_size_t total_page_count,
      const AtomicString& page_name,
      const CountersAttachmentContext&,
      const PageAreaLayoutParams&,
      const PhysicalBoxFragment* existing_page_container = nullptr);
};

}  // namespace blink

#endif  // THIRD_PARTY_BLINK_RENDERER_CORE_LAYOUT_PAGINATED_ROOT_LAYOUT_ALGORITHM_H_
