RubyUtils.h (7267B)
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: 57 static inline bool IsRubyContentBox(LayoutFrameType aFrameType) { 58 return aFrameType == mozilla::LayoutFrameType::RubyBase || 59 aFrameType == mozilla::LayoutFrameType::RubyText; 60 } 61 62 static inline bool IsRubyContainerBox(LayoutFrameType aFrameType) { 63 return aFrameType == mozilla::LayoutFrameType::RubyBaseContainer || 64 aFrameType == mozilla::LayoutFrameType::RubyTextContainer; 65 } 66 67 static inline bool IsRubyBox(LayoutFrameType aFrameType) { 68 return aFrameType == mozilla::LayoutFrameType::Ruby || 69 IsRubyContentBox(aFrameType) || IsRubyContainerBox(aFrameType); 70 } 71 72 static inline bool IsExpandableRubyBox(nsIFrame* aFrame) { 73 mozilla::LayoutFrameType type = aFrame->Type(); 74 return IsRubyContentBox(type) || IsRubyContainerBox(type); 75 } 76 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(); 109 bool AtEnd() const { return !mBaseContainer; } 110 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 127 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 bool operator==(const Iterator& aIter2) const { 147 MOZ_ASSERT(&mColumn == &aIter2.mColumn, 148 "Should only compare iterators of the same ruby column"); 149 return mIndex == aIter2.mIndex; 150 } 151 bool operator!=(const Iterator& aIter2) const = default; 152 153 private: 154 Iterator(const RubyColumn& aColumn, int32_t aIndex) 155 : mColumn(aColumn), mIndex(aIndex) { 156 MOZ_ASSERT( 157 aIndex == -1 || 158 (aIndex >= 0 && aIndex <= int32_t(aColumn.mTextFrames.Length()))); 159 SkipUntilExistingFrame(); 160 } 161 friend struct RubyColumn; // for the constructor 162 163 void SkipUntilExistingFrame(); 164 165 const RubyColumn& mColumn; 166 // -1 means the ruby base frame, 167 // non-negative means the index of ruby text frame 168 // a value of mTextFrames.Length() means we're done iterating 169 int32_t mIndex = -1; 170 }; 171 172 Iterator begin() const { return Iterator(*this, -1); } 173 Iterator end() const { return Iterator(*this, mTextFrames.Length()); } 174 Iterator cbegin() const { return begin(); } 175 Iterator cend() const { return end(); } 176 }; 177 178 /** 179 * This enumerator enumerates ruby columns in a segment. 180 */ 181 class MOZ_STACK_CLASS RubyColumnEnumerator { 182 public: 183 RubyColumnEnumerator(nsRubyBaseContainerFrame* aRBCFrame, 184 const AutoRubyTextContainerArray& aRTCFrames); 185 186 void Next(); 187 bool AtEnd() const; 188 189 uint32_t GetLevelCount() const { return mFrames.Length(); } 190 nsRubyContentFrame* GetFrameAtLevel(uint32_t aIndex) const; 191 void GetColumn(RubyColumn& aColumn) const; 192 193 private: 194 // Frames in this array are NOT necessary part of the current column. 195 // When in doubt, use GetFrameAtLevel to access it. 196 // See GetFrameAtLevel() and Next() for more info. 197 AutoTArray<nsRubyContentFrame*, RTC_ARRAY_SIZE + 1> mFrames; 198 // Whether we are on a column for intra-level whitespaces 199 bool mAtIntraLevelWhitespace; 200 }; 201 202 /** 203 * Stores block-axis leadings produced from ruby annotations. 204 */ 205 struct RubyBlockLeadings { 206 nscoord mStart = 0; 207 nscoord mEnd = 0; 208 209 void Reset() { mStart = mEnd = 0; } 210 void Update(nscoord aStart, nscoord aEnd) { 211 mStart = std::max(mStart, aStart); 212 mEnd = std::max(mEnd, aEnd); 213 } 214 void Update(const RubyBlockLeadings& aOther) { 215 Update(aOther.mStart, aOther.mEnd); 216 } 217 }; 218 219 } // namespace mozilla 220 221 #endif /* !defined(mozilla_RubyUtils_h_) */