1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef mozilla_RubyUtils_h_
8 #define mozilla_RubyUtils_h_
9 
10 #include "nsCSSAnonBoxes.h"
11 #include "nsGkAtoms.h"
12 #include "nsIFrame.h"
13 #include "nsTArray.h"
14 
15 #define RTC_ARRAY_SIZE 1
16 
17 class nsRubyFrame;
18 class nsRubyBaseFrame;
19 class nsRubyTextFrame;
20 class nsRubyContentFrame;
21 class nsRubyBaseContainerFrame;
22 class nsRubyTextContainerFrame;
23 
24 namespace mozilla {
25 
26 /**
27  * Reserved ISize
28  *
29  * With some exceptions, each ruby internal box has two isizes, which
30  * are the reflowed isize and the final isize. The reflowed isize is
31  * what a box itself needs. It is determined when the box gets reflowed.
32  *
33  * The final isize is what a box should be as the final result. For a
34  * ruby base/text box, the final isize is the size of its ruby column.
35  * For a ruby base/text container, the final isize is the size of its
36  * ruby segment. The final isize is never smaller than the reflowed
37  * isize. It is initially determined when a ruby column/segment gets
38  * fully reflowed, and may be advanced when a box is expanded, e.g.
39  * for justification.
40  *
41  * The difference between the reflowed isize and the final isize is
42  * reserved in the line layout after reflowing a box, hence it is called
43  * "Reserved ISize" here. It is used to expand the ruby boxes from their
44  * reflowed isize to the final isize during alignment of the line.
45  *
46  * There are three exceptions for the final isize:
47  * 1. A ruby text container has a larger final isize only if it is for
48  *    a span or collapsed annotations.
49  * 2. A ruby base container has a larger final isize only if at least
50  *    one of its ruby text containers does.
51  * 3. If a ruby text container has a larger final isize, its children
52  *    must not have.
53  */
54 
55 class RubyUtils {
56  public:
IsRubyContentBox(LayoutFrameType aFrameType)57   static inline bool IsRubyContentBox(LayoutFrameType aFrameType) {
58     return aFrameType == mozilla::LayoutFrameType::RubyBase ||
59            aFrameType == mozilla::LayoutFrameType::RubyText;
60   }
61 
IsRubyContainerBox(LayoutFrameType aFrameType)62   static inline bool IsRubyContainerBox(LayoutFrameType aFrameType) {
63     return aFrameType == mozilla::LayoutFrameType::RubyBaseContainer ||
64            aFrameType == mozilla::LayoutFrameType::RubyTextContainer;
65   }
66 
IsRubyBox(LayoutFrameType aFrameType)67   static inline bool IsRubyBox(LayoutFrameType aFrameType) {
68     return aFrameType == mozilla::LayoutFrameType::Ruby ||
69            IsRubyContentBox(aFrameType) || IsRubyContainerBox(aFrameType);
70   }
71 
IsExpandableRubyBox(nsIFrame * aFrame)72   static inline bool IsExpandableRubyBox(nsIFrame* aFrame) {
73     mozilla::LayoutFrameType type = aFrame->Type();
74     return IsRubyContentBox(type) || IsRubyContainerBox(type);
75   }
76 
IsRubyPseudo(PseudoStyleType aPseudo)77   static inline bool IsRubyPseudo(PseudoStyleType aPseudo) {
78     return aPseudo == PseudoStyleType::blockRubyContent ||
79            aPseudo == PseudoStyleType::ruby ||
80            aPseudo == PseudoStyleType::rubyBase ||
81            aPseudo == PseudoStyleType::rubyText ||
82            aPseudo == PseudoStyleType::rubyBaseContainer ||
83            aPseudo == PseudoStyleType::rubyTextContainer;
84   }
85 
86   static void SetReservedISize(nsIFrame* aFrame, nscoord aISize);
87   static void ClearReservedISize(nsIFrame* aFrame);
88   static nscoord GetReservedISize(nsIFrame* aFrame);
89 };
90 
91 /**
92  * This array stores all ruby text containers of the ruby segment
93  * of the given ruby base container.
94  */
95 class MOZ_RAII AutoRubyTextContainerArray final
96     : public AutoTArray<nsRubyTextContainerFrame*, RTC_ARRAY_SIZE> {
97  public:
98   explicit AutoRubyTextContainerArray(nsRubyBaseContainerFrame* aBaseContainer);
99 };
100 
101 /**
102  * This enumerator enumerates each ruby segment.
103  */
104 class MOZ_STACK_CLASS RubySegmentEnumerator {
105  public:
106   explicit RubySegmentEnumerator(nsRubyFrame* aRubyFrame);
107 
108   void Next();
AtEnd()109   bool AtEnd() const { return !mBaseContainer; }
110 
GetBaseContainer()111   nsRubyBaseContainerFrame* GetBaseContainer() const { return mBaseContainer; }
112 
113  private:
114   nsRubyBaseContainerFrame* mBaseContainer;
115 };
116 
117 /**
118  * Ruby column is a unit consists of one ruby base and all ruby
119  * annotations paired with it.
120  * See http://dev.w3.org/csswg/css-ruby/#ruby-pairing
121  */
122 struct MOZ_STACK_CLASS RubyColumn {
123   nsRubyBaseFrame* mBaseFrame;
124   AutoTArray<nsRubyTextFrame*, RTC_ARRAY_SIZE> mTextFrames;
125   bool mIsIntraLevelWhitespace;
126 
RubyColumnRubyColumn127   RubyColumn() : mBaseFrame(nullptr), mIsIntraLevelWhitespace(false) {}
128 
129   // Helper class to support iteration across the frames within a single
130   // RubyColumn (the column's ruby base and its annotations).
131   class MOZ_STACK_CLASS Iterator {
132    public:
133     nsIFrame* operator*() const;
134 
135     Iterator& operator++() {
136       ++mIndex;
137       SkipUntilExistingFrame();
138       return *this;
139     }
140     Iterator operator++(int) {
141       auto ret = *this;
142       ++*this;
143       return ret;
144     }
145 
146     friend bool operator==(const Iterator& aIter1, const Iterator& aIter2) {
147       MOZ_ASSERT(&aIter1.mColumn == &aIter2.mColumn,
148                  "Should only compare iterators of the same ruby column");
149       return aIter1.mIndex == aIter2.mIndex;
150     }
151     friend bool operator!=(const Iterator& aIter1, const Iterator& aIter2) {
152       return !(aIter1 == aIter2);
153     }
154 
155    private:
IteratorRubyColumn156     Iterator(const RubyColumn& aColumn, int32_t aIndex)
157         : mColumn(aColumn), mIndex(aIndex) {
158       MOZ_ASSERT(
159           aIndex == -1 ||
160           (aIndex >= 0 && aIndex <= int32_t(aColumn.mTextFrames.Length())));
161       SkipUntilExistingFrame();
162     }
163     friend struct RubyColumn;  // for the constructor
164 
165     void SkipUntilExistingFrame();
166 
167     const RubyColumn& mColumn;
168     // -1 means the ruby base frame,
169     // non-negative means the index of ruby text frame
170     // a value of mTextFrames.Length() means we're done iterating
171     int32_t mIndex = -1;
172   };
173 
beginRubyColumn174   Iterator begin() const { return Iterator(*this, -1); }
endRubyColumn175   Iterator end() const { return Iterator(*this, mTextFrames.Length()); }
cbeginRubyColumn176   Iterator cbegin() const { return begin(); }
cendRubyColumn177   Iterator cend() const { return end(); }
178 };
179 
180 /**
181  * This enumerator enumerates ruby columns in a segment.
182  */
183 class MOZ_STACK_CLASS RubyColumnEnumerator {
184  public:
185   RubyColumnEnumerator(nsRubyBaseContainerFrame* aRBCFrame,
186                        const AutoRubyTextContainerArray& aRTCFrames);
187 
188   void Next();
189   bool AtEnd() const;
190 
GetLevelCount()191   uint32_t GetLevelCount() const { return mFrames.Length(); }
192   nsRubyContentFrame* GetFrameAtLevel(uint32_t aIndex) const;
193   void GetColumn(RubyColumn& aColumn) const;
194 
195  private:
196   // Frames in this array are NOT necessary part of the current column.
197   // When in doubt, use GetFrameAtLevel to access it.
198   // See GetFrameAtLevel() and Next() for more info.
199   AutoTArray<nsRubyContentFrame*, RTC_ARRAY_SIZE + 1> mFrames;
200   // Whether we are on a column for intra-level whitespaces
201   bool mAtIntraLevelWhitespace;
202 };
203 
204 /**
205  * Stores block-axis leadings produced from ruby annotations.
206  */
207 struct RubyBlockLeadings {
208   nscoord mStart = 0;
209   nscoord mEnd = 0;
210 
ResetRubyBlockLeadings211   void Reset() { mStart = mEnd = 0; }
UpdateRubyBlockLeadings212   void Update(nscoord aStart, nscoord aEnd) {
213     mStart = std::max(mStart, aStart);
214     mEnd = std::max(mEnd, aEnd);
215   }
UpdateRubyBlockLeadings216   void Update(const RubyBlockLeadings& aOther) {
217     Update(aOther.mStart, aOther.mEnd);
218   }
219 };
220 
221 }  // namespace mozilla
222 
223 #endif /* !defined(mozilla_RubyUtils_h_) */
224