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