Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; 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 0 : NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(ReservedISize, nscoord)
17 :
18 : /* static */ void
19 0 : RubyUtils::SetReservedISize(nsIFrame* aFrame, nscoord aISize)
20 : {
21 0 : MOZ_ASSERT(IsExpandableRubyBox(aFrame));
22 0 : aFrame->SetProperty(ReservedISize(), aISize);
23 0 : }
24 :
25 : /* static */ void
26 0 : RubyUtils::ClearReservedISize(nsIFrame* aFrame)
27 : {
28 0 : MOZ_ASSERT(IsExpandableRubyBox(aFrame));
29 0 : aFrame->RemoveProperty(ReservedISize());
30 0 : }
31 :
32 : /* static */ nscoord
33 0 : RubyUtils::GetReservedISize(nsIFrame* aFrame)
34 : {
35 0 : MOZ_ASSERT(IsExpandableRubyBox(aFrame));
36 0 : return aFrame->GetProperty(ReservedISize());
37 : }
38 :
39 0 : AutoRubyTextContainerArray::AutoRubyTextContainerArray(
40 0 : nsRubyBaseContainerFrame* aBaseContainer)
41 : {
42 0 : for (nsIFrame* frame = aBaseContainer->GetNextSibling();
43 0 : frame && frame->IsRubyTextContainerFrame();
44 : frame = frame->GetNextSibling()) {
45 0 : AppendElement(static_cast<nsRubyTextContainerFrame*>(frame));
46 : }
47 0 : }
48 :
49 : nsIFrame*
50 0 : RubyColumn::Iterator::operator*() const
51 : {
52 : nsIFrame* frame;
53 0 : if (mIndex == -1) {
54 0 : frame = mColumn.mBaseFrame;
55 : } else {
56 0 : frame = mColumn.mTextFrames[mIndex];
57 : }
58 0 : MOZ_ASSERT(frame, "Frame here cannot be null");
59 0 : return frame;
60 : }
61 :
62 : void
63 0 : RubyColumn::Iterator::SkipUntilExistingFrame()
64 : {
65 0 : if (mIndex == -1) {
66 0 : if (mColumn.mBaseFrame) {
67 0 : return;
68 : }
69 0 : ++mIndex;
70 : }
71 0 : int32_t numTextFrames = mColumn.mTextFrames.Length();
72 0 : for (; mIndex < numTextFrames; ++mIndex) {
73 0 : if (mColumn.mTextFrames[mIndex]) {
74 0 : break;
75 : }
76 : }
77 : }
78 :
79 0 : RubySegmentEnumerator::RubySegmentEnumerator(nsRubyFrame* aRubyFrame)
80 : {
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 0 : RubySegmentEnumerator::Next()
88 : {
89 0 : MOZ_ASSERT(mBaseContainer);
90 0 : nsIFrame* frame = mBaseContainer->GetNextSibling();
91 0 : while (frame && !frame->IsRubyBaseContainerFrame()) {
92 0 : frame = frame->GetNextSibling();
93 : }
94 0 : mBaseContainer = static_cast<nsRubyBaseContainerFrame*>(frame);
95 0 : }
96 :
97 0 : RubyColumnEnumerator::RubyColumnEnumerator(
98 : nsRubyBaseContainerFrame* aBaseContainer,
99 0 : const AutoRubyTextContainerArray& aTextContainers)
100 0 : : mAtIntraLevelWhitespace(false)
101 : {
102 0 : const uint32_t rtcCount = aTextContainers.Length();
103 0 : mFrames.SetCapacity(rtcCount + 1);
104 :
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 : // If the container is for span, leave a nullptr here.
111 : // 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 : }
117 :
118 : // We have to init mAtIntraLevelWhitespace to be correct for the
119 : // first column. There are two ways we could end up with intra-level
120 : // whitespace in our first colum:
121 : // 1. The current segment itself is an inter-segment whitespace;
122 : // 2. If our ruby segment is split across multiple lines, and some
123 : // intra-level whitespace happens to fall right after a line-break.
124 : // Each line will get its own nsRubyBaseContainerFrame, and the
125 : // container right after the line-break will end up with its first
126 : // 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 : }
133 : }
134 0 : }
135 :
136 : void
137 0 : RubyColumnEnumerator::Next()
138 : {
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 : // If we've got intra-level whitespace frames at some levels in the
143 : // current ruby column, we "faked" an anonymous box for all other
144 : // levels for this column. So when we advance off this column, we
145 : // don't advance any of the frames in those levels, because we're
146 : // 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 : "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 : }
157 : }
158 : }
159 0 : MOZ_ASSERT(!advancingToIntraLevelWhitespace || !mAtIntraLevelWhitespace,
160 : "Should never have adjacent intra-level whitespace columns");
161 0 : mAtIntraLevelWhitespace = advancingToIntraLevelWhitespace;
162 0 : }
163 :
164 : bool
165 0 : RubyColumnEnumerator::AtEnd() const
166 : {
167 0 : for (uint32_t i = 0, iend = mFrames.Length(); i < iend; i++) {
168 0 : if (mFrames[i]) {
169 0 : return false;
170 : }
171 : }
172 0 : return true;
173 : }
174 :
175 : nsRubyContentFrame*
176 0 : RubyColumnEnumerator::GetFrameAtLevel(uint32_t aIndex) const
177 : {
178 : // If the current ruby column is for intra-level whitespaces, we
179 : // return nullptr for any levels that do not have an actual intra-
180 : // level whitespace frame in this column. This nullptr represents
181 : // an anonymous empty intra-level whitespace box. (In this case,
182 : // it's important that we NOT return mFrames[aIndex], because it's
183 : // 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 : }
188 :
189 : void
190 0 : RubyColumnEnumerator::GetColumn(RubyColumn& aColumn) const
191 : {
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 : }
201 0 : aColumn.mIsIntraLevelWhitespace = mAtIntraLevelWhitespace;
202 0 : }
|