summaryrefslogtreecommitdiffstats
path: root/layout/generic/nsSplittableFrame.h
blob: 923a3e462f30183b445cdee36d3f33b3bfba6a6c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/*
 * base class for rendering objects that can be split across lines,
 * columns, or pages
 */

#ifndef nsSplittableFrame_h___
#define nsSplittableFrame_h___

#include "mozilla/Attributes.h"
#include "nsIFrame.h"

// Derived class that allows splitting
class nsSplittableFrame : public nsIFrame {
 public:
  NS_DECL_ABSTRACT_FRAME(nsSplittableFrame)
  NS_DECL_QUERYFRAME_TARGET(nsSplittableFrame)
  NS_DECL_QUERYFRAME

  void Init(nsIContent* aContent, nsContainerFrame* aParent,
            nsIFrame* aPrevInFlow) override;

  void Destroy(DestroyContext&) override;

  /*
   * Frame continuations can be either fluid or non-fluid.
   *
   * Fluid continuations ("in-flows") are the result of line breaking,
   * column breaking, or page breaking.
   *
   * Non-fluid continuations can be the result of BiDi frame splitting,
   * column-span splitting, or <col span="N"> where N > 1.
   *
   * A "flow" is a chain of fluid continuations.
   *
   * For more information, see https://wiki.mozilla.org/Gecko:Continuation_Model
   */

  // Get the previous/next continuation, regardless of its type (fluid or
  // non-fluid).
  nsIFrame* GetPrevContinuation() const final;
  nsIFrame* GetNextContinuation() const final;

  // Set a previous non-fluid continuation.
  void SetPrevContinuation(nsIFrame*) final;

  // Set a next non-fluid continuation.
  //
  // WARNING: this method updates caches for next-continuations, so it has O(n)
  // time complexity over the length of next-continuations in the chain.
  void SetNextContinuation(nsIFrame*) final;

  // Get the first/last continuation for this frame.
  nsIFrame* FirstContinuation() const final;
  nsIFrame* LastContinuation() const final;

#ifdef DEBUG
  // Can aFrame2 be reached from aFrame1 by following prev/next continuations?
  static bool IsInPrevContinuationChain(nsIFrame* aFrame1, nsIFrame* aFrame2);
  static bool IsInNextContinuationChain(nsIFrame* aFrame1, nsIFrame* aFrame2);
#endif

  // Get the previous/next continuation, only if it is fluid (an "in-flow").
  nsIFrame* GetPrevInFlow() const final;
  nsIFrame* GetNextInFlow() const final;

  // Set a previous fluid continuation.
  void SetPrevInFlow(nsIFrame*) final;

  // Set a next fluid continuation.
  //
  // WARNING: this method updates caches for next-continuations, so it has O(n)
  // time complexity over the length of next-continuations in the chain.
  void SetNextInFlow(nsIFrame*) final;

  // Get the first/last frame in the current flow.
  nsIFrame* FirstInFlow() const final;
  nsIFrame* LastInFlow() const final;

  // Remove the frame from the flow. Connects the frame's prev-in-flow
  // and its next-in-flow. This should only be called in frame Destroy()
  // methods.
  static void RemoveFromFlow(nsIFrame* aFrame);

 protected:
  nsSplittableFrame(ComputedStyle* aStyle, nsPresContext* aPresContext,
                    ClassID aID)
      : nsIFrame(aStyle, aPresContext, aID) {}

  // Update the first-continuation and first-in-flow cache for this frame and
  // the next-continuations in the chain.
  //
  // Note: this function assumes that the first-continuation and first-in-flow
  // caches are already up-to-date on this frame's
  // prev-continuation/prev-in-flow frame (if there is such a frame).
  void UpdateFirstContinuationAndFirstInFlowCache();

  /**
   * Return the sum of the block-axis content size of our previous
   * continuations.
   *
   * Classes that call this are _required_ to call this at least once for each
   * reflow (unless you're the first continuation, in which case you can skip
   * it, because as an optimization we don't cache it there).
   *
   * This guarantees that the internal cache works, by refreshing it. Calling it
   * multiple times in the same reflow is wasteful, but not an error.
   */
  nscoord CalcAndCacheConsumedBSize();

  /**
   * This static wrapper over CalcAndCacheConsumedBSize() is intended for a
   * specific scenario where an nsSplittableFrame's subclass needs to access
   * another subclass' consumed block-size. For ordinary use cases,
   * CalcAndCacheConsumedBSize() should be called.
   *
   * This has the same requirements as CalcAndCacheConsumedBSize(). In
   * particular, classes that call this are _required_ to call this at least
   * once for each reflow.
   */
  static nscoord ConsumedBSize(nsSplittableFrame* aFrame) {
    return aFrame->CalcAndCacheConsumedBSize();
  }

  /**
   * Retrieve the effective computed block size of this frame, which is the
   * computed block size, minus the block size consumed by any previous
   * continuations.
   */
  nscoord GetEffectiveComputedBSize(const ReflowInput& aReflowInput,
                                    nscoord aConsumed) const;

  /**
   * @see nsIFrame::GetLogicalSkipSides()
   */
  LogicalSides GetLogicalSkipSides() const override {
    return GetBlockLevelLogicalSkipSides(true);
  }

  LogicalSides GetBlockLevelLogicalSkipSides(bool aAfterReflow) const;

  /**
   * A version of GetLogicalSkipSides() that is intended to be used inside
   * Reflow before it's known if |this| frame will be COMPLETE or not.
   * It returns a result that assumes this fragment is the last and thus
   * should apply the block-end border/padding etc (except for "true" overflow
   * containers which always skip block sides).  You're then expected to
   * recalculate the block-end side (as needed) when you know |this| frame's
   * reflow status is INCOMPLETE.
   * This method is intended for frames that break in the block axis.
   */
  LogicalSides PreReflowBlockLevelLogicalSkipSides() const {
    return GetBlockLevelLogicalSkipSides(false);
  };

  nsIFrame* mPrevContinuation = nullptr;
  nsIFrame* mNextContinuation = nullptr;
};

#endif /* nsSplittableFrame_h___ */