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