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