1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code is subject to the terms of the Mozilla Public License
4 * version 2.0 (the "License"). You can obtain a copy of the License at
5 * http://mozilla.org/MPL/2.0/. */
7 /* rendering object for CSS "display: ruby" */
8 #include "nsRubyFrame.h"
9 #include "nsLineLayout.h"
10 #include "nsPresContext.h"
11 #include "nsStyleContext.h"
12 #include "WritingModes.h"
13 #include "nsRubyBaseContainerFrame.h"
14 #include "nsRubyTextContainerFrame.h"
16 using namespace mozilla
;
18 //----------------------------------------------------------------------
20 // Frame class boilerplate
21 // =======================
23 NS_QUERYFRAME_HEAD(nsRubyFrame
)
24 NS_QUERYFRAME_ENTRY(nsRubyFrame
)
25 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame
)
27 NS_IMPL_FRAMEARENA_HELPERS(nsRubyFrame
)
30 NS_NewRubyFrame(nsIPresShell
* aPresShell
,
31 nsStyleContext
* aContext
)
33 return new (aPresShell
) nsRubyFrame(aContext
);
36 //----------------------------------------------------------------------
38 // nsRubyFrame Method Implementations
39 // ==================================
42 nsRubyFrame::GetType() const
44 return nsGkAtoms::rubyFrame
;
48 nsRubyFrame::IsFrameOfType(uint32_t aFlags
) const
50 return nsContainerFrame::IsFrameOfType(aFlags
&
51 ~(nsIFrame::eLineParticipant
));
54 #ifdef DEBUG_FRAME_DUMP
56 nsRubyFrame::GetFrameName(nsAString
& aResult
) const
58 return MakeFrameName(NS_LITERAL_STRING("Ruby"), aResult
);
63 nsRubyFrame::CalculateColSizes(nsRenderingContext
* aRenderingContext
,
64 nsTArray
<nscoord
>& aColSizes
)
66 nsFrameList::Enumerator
e(this->PrincipalChildList());
67 uint32_t annotationNum
= 0;
70 nsTArray
<int> segmentBaseCounts
;
72 for(; !e
.AtEnd(); e
.Next()) {
73 nsIFrame
* childFrame
= e
.get();
74 if (childFrame
->GetType() == nsGkAtoms::rubyBaseContainerFrame
) {
76 segmentBaseCounts
.AppendElement(0);
77 nsFrameList::Enumerator
bases(childFrame
->PrincipalChildList());
78 for(; !bases
.AtEnd(); bases
.Next()) {
79 aColSizes
.AppendElement(bases
.get()->GetPrefISize(aRenderingContext
));
80 segmentBaseCounts
.ElementAt(segmentNum
)++;
82 } else if (childFrame
->GetType() == nsGkAtoms::rubyTextContainerFrame
) {
83 if (segmentNum
== -1) {
84 // No rbc exists for first segment, so act as if there is one
86 segmentBaseCounts
.AppendElement(1);
87 aColSizes
.AppendElement(0);
89 nsFrameList::Enumerator
annotations(childFrame
->PrincipalChildList());
90 uint32_t baseCount
= segmentBaseCounts
.ElementAt(segmentNum
);
91 for(; !annotations
.AtEnd(); annotations
.Next()) {
92 nsIFrame
* annotationFrame
= annotations
.get();
93 if (annotationNum
> baseCount
) {
94 aColSizes
.AppendElement(annotationFrame
->
95 GetPrefISize(aRenderingContext
));
97 segmentBaseCounts
.ElementAt(segmentNum
) = baseCount
;
99 } else if (annotationNum
< baseCount
- 1) {
100 //there are fewer annotations than bases, so the last annotation is
101 //associated with (spans) the remaining bases. This means these
102 //columns can't be broken up, so gather their entire ISize in one
103 //entry of aColSizes and clear the other entries.
105 for (uint32_t i
= annotationNum
; i
< annotationNum
+ baseCount
; i
++) {
106 baseSum
+= aColSizes
.ElementAt(i
);
107 if (i
> annotationNum
) {
108 aColSizes
.ElementAt(i
) = 0;
111 aColSizes
.ElementAt(annotationNum
) =
112 std::max(baseSum
, annotationFrame
->GetPrefISize(aRenderingContext
));
113 annotationNum
= baseCount
;
115 aColSizes
.ElementAt(annotationNum
) =
116 std::max(aColSizes
.ElementAt(annotationNum
),
117 annotationFrame
->GetPrefISize(aRenderingContext
));
122 NS_ASSERTION(false, "Unrecognized child type for ruby frame.");
128 nsRubyFrame::AddInlineMinISize(nsRenderingContext
*aRenderingContext
,
129 nsIFrame::InlineMinISizeData
*aData
)
131 //FIXME: This needs to handle the cases where it's possible for a ruby base to
132 //break, as well as forced breaks.
133 nsTArray
<int> colSizes
;
134 CalculateColSizes(aRenderingContext
, colSizes
);
137 for (uint32_t i
= 0; i
< colSizes
.Length(); i
++) {
138 if (colSizes
.ElementAt(i
) > max
) {
139 max
= colSizes
.ElementAt(i
);
143 aData
->currentLine
+= max
;
147 nsRubyFrame::AddInlinePrefISize(nsRenderingContext
*aRenderingContext
,
148 nsIFrame::InlinePrefISizeData
*aData
)
150 nsTArray
<int> colSizes
;
151 CalculateColSizes(aRenderingContext
, colSizes
);
154 for (uint32_t i
= 0; i
< colSizes
.Length(); i
++) {
155 sum
+= colSizes
.ElementAt(i
);
158 aData
->currentLine
+= sum
;
161 /* virtual */ nscoord
162 nsRubyFrame::GetLogicalBaseline(WritingMode aWritingMode
) const
168 nsRubyFrame::CanContinueTextRun() const
174 nsRubyFrame::Reflow(nsPresContext
* aPresContext
,
175 nsHTMLReflowMetrics
& aDesiredSize
,
176 const nsHTMLReflowState
& aReflowState
,
177 nsReflowStatus
& aStatus
)
179 DO_GLOBAL_REFLOW_COUNT("nsRubyFrame");
180 DISPLAY_REFLOW(aPresContext
, this, aReflowState
, aDesiredSize
, aStatus
);
182 if (!aReflowState
.mLineLayout
) {
183 NS_ASSERTION(aReflowState
.mLineLayout
,
184 "No line layout provided to RubyFrame reflow method.");
185 aStatus
= NS_FRAME_COMPLETE
;
189 // Begin the span for the ruby frame
190 WritingMode frameWM
= aReflowState
.GetWritingMode();
191 WritingMode lineWM
= aReflowState
.mLineLayout
->GetWritingMode();
192 LogicalMargin borderPadding
= aReflowState
.ComputedLogicalBorderPadding();
193 nscoord availableISize
= aReflowState
.AvailableISize();
194 NS_ASSERTION(availableISize
!= NS_UNCONSTRAINEDSIZE
,
195 "should no longer use available widths");
196 // Subtract off inline axis border+padding from availableISize
197 availableISize
-= borderPadding
.IStartEnd(frameWM
);
198 aReflowState
.mLineLayout
->BeginSpan(this, &aReflowState
,
199 borderPadding
.IStart(frameWM
),
200 availableISize
, &mBaseline
);
202 // FIXME: line breaking / continuations not yet implemented
203 aStatus
= NS_FRAME_COMPLETE
;
204 LogicalSize
availSize(lineWM
, aReflowState
.AvailableISize(),
205 aReflowState
.AvailableBSize());
206 // The ruby base container for the current segment
207 nsRubyBaseContainerFrame
* segmentRBC
= nullptr;
208 nscoord annotationBSize
= 0;
209 for (nsFrameList::Enumerator
e(mFrames
); !e
.AtEnd(); e
.Next()) {
210 nsIFrame
* childFrame
= e
.get();
211 if (e
.get()->GetType() == nsGkAtoms::rubyBaseContainerFrame
) {
216 // Figure out what all the text containers are for this segment (necessary
217 // for reflow calculations)
218 segmentRBC
= do_QueryFrame(childFrame
);
219 nsFrameList::Enumerator
segment(e
);
221 while (!segment
.AtEnd() && (segment
.get()->GetType() !=
222 nsGkAtoms::rubyBaseContainerFrame
)) {
223 if (segment
.get()->GetType() == nsGkAtoms::rubyTextContainerFrame
) {
224 segmentRBC
->AppendTextContainer(segment
.get());
229 nsReflowStatus frameReflowStatus
;
230 nsHTMLReflowMetrics
metrics(aReflowState
, aDesiredSize
.mFlags
);
231 nsHTMLReflowState
childReflowState(aPresContext
, aReflowState
,
232 childFrame
, availSize
);
233 childReflowState
.mLineLayout
= aReflowState
.mLineLayout
;
234 childFrame
->Reflow(aPresContext
, metrics
, childReflowState
,
236 NS_ASSERTION(frameReflowStatus
== NS_FRAME_COMPLETE
,
237 "Ruby line breaking is not yet implemented");
238 childFrame
->SetSize(LogicalSize(lineWM
, metrics
.ISize(lineWM
),
239 metrics
.BSize(lineWM
)));
240 FinishReflowChild(childFrame
, aPresContext
, metrics
, &childReflowState
, 0,
241 0, NS_FRAME_NO_MOVE_FRAME
| NS_FRAME_NO_MOVE_VIEW
);
243 } else if (childFrame
->GetType() == nsGkAtoms::rubyTextContainerFrame
) {
244 nsReflowStatus frameReflowStatus
;
245 nsHTMLReflowMetrics
metrics(aReflowState
, aDesiredSize
.mFlags
);
246 nsHTMLReflowState
childReflowState(aPresContext
, aReflowState
, childFrame
,
248 childReflowState
.mLineLayout
= aReflowState
.mLineLayout
;
249 childFrame
->Reflow(aPresContext
, metrics
, childReflowState
,
251 NS_ASSERTION(frameReflowStatus
== NS_FRAME_COMPLETE
,
252 "Ruby line breaking is not yet implemented");
253 annotationBSize
+= metrics
.BSize(lineWM
);
254 childFrame
->SetSize(LogicalSize(lineWM
, metrics
.ISize(lineWM
),
255 metrics
.BSize(lineWM
)));
256 // FIXME: This is a temporary calculation for finding the block coordinate
257 // of the ruby text container. A better one replace it once it's been
259 nscoord baseContainerBCoord
;
261 // Find the starting block coordinate of the ruby base container for
262 // this segment. For now, the ruby text container will be placed so that
263 // its bottom edge touches this coordinate.
264 baseContainerBCoord
= segmentRBC
->
265 GetLogicalPosition(this->GetParent()->GetLogicalSize().ISize(lineWM
)).
268 baseContainerBCoord
= 0;
270 FinishReflowChild(childFrame
, aPresContext
, metrics
, &childReflowState
, 0,
271 baseContainerBCoord
- metrics
.BSize(lineWM
), 0);
274 "Unrecognized child type for ruby frame will not be reflowed.");
278 // Null the pointers between child frames.
279 for (nsFrameList::Enumerator
children(mFrames
); !children
.AtEnd();
281 nsRubyBaseContainerFrame
* rbcFrame
= do_QueryFrame(children
.get());
283 rbcFrame
->ClearTextContainers();
287 aDesiredSize
.ISize(lineWM
) = aReflowState
.mLineLayout
->EndSpan(this);
288 nsLayoutUtils::SetBSizeFromFontMetrics(this, aDesiredSize
, aReflowState
,
289 borderPadding
, lineWM
, frameWM
);