RubyUtils.cpp (7023B)
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 #include "RubyUtils.h" 8 9 #include "nsRubyBaseContainerFrame.h" 10 #include "nsRubyBaseFrame.h" 11 #include "nsRubyFrame.h" 12 #include "nsRubyTextContainerFrame.h" 13 #include "nsRubyTextFrame.h" 14 15 using namespace mozilla; 16 17 NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(ReservedISize, nscoord) 18 19 /* static */ 20 void RubyUtils::SetReservedISize(nsIFrame* aFrame, nscoord aISize) { 21 MOZ_ASSERT(IsExpandableRubyBox(aFrame)); 22 aFrame->SetProperty(ReservedISize(), aISize); 23 } 24 25 /* static */ 26 void RubyUtils::ClearReservedISize(nsIFrame* aFrame) { 27 MOZ_ASSERT(IsExpandableRubyBox(aFrame)); 28 aFrame->RemoveProperty(ReservedISize()); 29 } 30 31 /* static */ 32 nscoord RubyUtils::GetReservedISize(nsIFrame* aFrame) { 33 MOZ_ASSERT(IsExpandableRubyBox(aFrame)); 34 return aFrame->GetProperty(ReservedISize()); 35 } 36 37 AutoRubyTextContainerArray::AutoRubyTextContainerArray( 38 nsRubyBaseContainerFrame* aBaseContainer) { 39 for (nsIFrame* frame = aBaseContainer->GetNextSibling(); 40 frame && frame->IsRubyTextContainerFrame(); 41 frame = frame->GetNextSibling()) { 42 AppendElement(static_cast<nsRubyTextContainerFrame*>(frame)); 43 } 44 } 45 46 nsIFrame* RubyColumn::Iterator::operator*() const { 47 nsIFrame* frame; 48 if (mIndex == -1) { 49 frame = mColumn.mBaseFrame; 50 } else { 51 frame = mColumn.mTextFrames[mIndex]; 52 } 53 MOZ_ASSERT(frame, "Frame here cannot be null"); 54 return frame; 55 } 56 57 void RubyColumn::Iterator::SkipUntilExistingFrame() { 58 if (mIndex == -1) { 59 if (mColumn.mBaseFrame) { 60 return; 61 } 62 ++mIndex; 63 } 64 int32_t numTextFrames = mColumn.mTextFrames.Length(); 65 for (; mIndex < numTextFrames; ++mIndex) { 66 if (mColumn.mTextFrames[mIndex]) { 67 break; 68 } 69 } 70 } 71 72 RubySegmentEnumerator::RubySegmentEnumerator(nsRubyFrame* aRubyFrame) { 73 nsIFrame* frame = aRubyFrame->PrincipalChildList().FirstChild(); 74 MOZ_ASSERT(!frame || frame->IsRubyBaseContainerFrame()); 75 mBaseContainer = static_cast<nsRubyBaseContainerFrame*>(frame); 76 } 77 78 void RubySegmentEnumerator::Next() { 79 MOZ_ASSERT(mBaseContainer); 80 nsIFrame* frame = mBaseContainer->GetNextSibling(); 81 while (frame && !frame->IsRubyBaseContainerFrame()) { 82 frame = frame->GetNextSibling(); 83 } 84 mBaseContainer = static_cast<nsRubyBaseContainerFrame*>(frame); 85 } 86 87 RubyColumnEnumerator::RubyColumnEnumerator( 88 nsRubyBaseContainerFrame* aBaseContainer, 89 const AutoRubyTextContainerArray& aTextContainers) 90 : mAtIntraLevelWhitespace(false) { 91 const uint32_t rtcCount = aTextContainers.Length(); 92 mFrames.SetCapacity(rtcCount + 1); 93 94 nsIFrame* rbFrame = aBaseContainer->PrincipalChildList().FirstChild(); 95 MOZ_ASSERT(!rbFrame || rbFrame->IsRubyBaseFrame()); 96 mFrames.AppendElement(static_cast<nsRubyContentFrame*>(rbFrame)); 97 for (uint32_t i = 0; i < rtcCount; i++) { 98 nsRubyTextContainerFrame* container = aTextContainers[i]; 99 // If the container is for span, leave a nullptr here. 100 // Spans do not take part in pairing. 101 nsIFrame* rtFrame = !container->IsSpanContainer() 102 ? container->PrincipalChildList().FirstChild() 103 : nullptr; 104 MOZ_ASSERT(!rtFrame || rtFrame->IsRubyTextFrame()); 105 mFrames.AppendElement(static_cast<nsRubyContentFrame*>(rtFrame)); 106 } 107 108 // We have to init mAtIntraLevelWhitespace to be correct for the 109 // first column. There are two ways we could end up with intra-level 110 // whitespace in our first colum: 111 // 1. The current segment itself is an inter-segment whitespace; 112 // 2. If our ruby segment is split across multiple lines, and some 113 // intra-level whitespace happens to fall right after a line-break. 114 // Each line will get its own nsRubyBaseContainerFrame, and the 115 // container right after the line-break will end up with its first 116 // column containing that intra-level whitespace. 117 for (uint32_t i = 0, iend = mFrames.Length(); i < iend; i++) { 118 nsRubyContentFrame* frame = mFrames[i]; 119 if (frame && frame->IsIntraLevelWhitespace()) { 120 mAtIntraLevelWhitespace = true; 121 break; 122 } 123 } 124 } 125 126 void RubyColumnEnumerator::Next() { 127 bool advancingToIntraLevelWhitespace = false; 128 for (uint32_t i = 0, iend = mFrames.Length(); i < iend; i++) { 129 nsRubyContentFrame* frame = mFrames[i]; 130 // If we've got intra-level whitespace frames at some levels in the 131 // current ruby column, we "faked" an anonymous box for all other 132 // levels for this column. So when we advance off this column, we 133 // don't advance any of the frames in those levels, because we're 134 // just advancing across the "fake" frames. 135 if (frame && 136 (!mAtIntraLevelWhitespace || frame->IsIntraLevelWhitespace())) { 137 nsIFrame* nextSibling = frame->GetNextSibling(); 138 MOZ_ASSERT(!nextSibling || nextSibling->Type() == frame->Type(), 139 "Frame type should be identical among a level"); 140 mFrames[i] = frame = static_cast<nsRubyContentFrame*>(nextSibling); 141 if (!advancingToIntraLevelWhitespace && frame && 142 frame->IsIntraLevelWhitespace()) { 143 advancingToIntraLevelWhitespace = true; 144 } 145 } 146 } 147 MOZ_ASSERT(!advancingToIntraLevelWhitespace || !mAtIntraLevelWhitespace, 148 "Should never have adjacent intra-level whitespace columns"); 149 mAtIntraLevelWhitespace = advancingToIntraLevelWhitespace; 150 } 151 152 bool RubyColumnEnumerator::AtEnd() const { 153 for (uint32_t i = 0, iend = mFrames.Length(); i < iend; i++) { 154 if (mFrames[i]) { 155 return false; 156 } 157 } 158 return true; 159 } 160 161 nsRubyContentFrame* RubyColumnEnumerator::GetFrameAtLevel( 162 uint32_t aIndex) const { 163 // If the current ruby column is for intra-level whitespaces, we 164 // return nullptr for any levels that do not have an actual intra- 165 // level whitespace frame in this column. This nullptr represents 166 // an anonymous empty intra-level whitespace box. (In this case, 167 // it's important that we NOT return mFrames[aIndex], because it's 168 // really part of the next column, not the current one.) 169 nsRubyContentFrame* frame = mFrames[aIndex]; 170 return !mAtIntraLevelWhitespace || (frame && frame->IsIntraLevelWhitespace()) 171 ? frame 172 : nullptr; 173 } 174 175 void RubyColumnEnumerator::GetColumn(RubyColumn& aColumn) const { 176 nsRubyContentFrame* rbFrame = GetFrameAtLevel(0); 177 MOZ_ASSERT(!rbFrame || rbFrame->IsRubyBaseFrame()); 178 aColumn.mBaseFrame = static_cast<nsRubyBaseFrame*>(rbFrame); 179 aColumn.mTextFrames.ClearAndRetainStorage(); 180 for (uint32_t i = 1, iend = mFrames.Length(); i < iend; i++) { 181 nsRubyContentFrame* rtFrame = GetFrameAtLevel(i); 182 MOZ_ASSERT(!rtFrame || rtFrame->IsRubyTextFrame()); 183 aColumn.mTextFrames.AppendElement(static_cast<nsRubyTextFrame*>(rtFrame)); 184 } 185 aColumn.mIsIntraLevelWhitespace = mAtIntraLevelWhitespace; 186 }