Bumping manifests a=b2g-bump
[gecko.git] / layout / generic / nsRubyFrame.cpp
blobf6b3d7c4b97018fd0f0958e7400e409b334073ac
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)
29 nsContainerFrame*
30 NS_NewRubyFrame(nsIPresShell* aPresShell,
31 nsStyleContext* aContext)
33 return new (aPresShell) nsRubyFrame(aContext);
36 //----------------------------------------------------------------------
38 // nsRubyFrame Method Implementations
39 // ==================================
41 nsIAtom*
42 nsRubyFrame::GetType() const
44 return nsGkAtoms::rubyFrame;
47 /* virtual */ bool
48 nsRubyFrame::IsFrameOfType(uint32_t aFlags) const
50 return nsContainerFrame::IsFrameOfType(aFlags &
51 ~(nsIFrame::eLineParticipant));
54 #ifdef DEBUG_FRAME_DUMP
55 nsresult
56 nsRubyFrame::GetFrameName(nsAString& aResult) const
58 return MakeFrameName(NS_LITERAL_STRING("Ruby"), aResult);
60 #endif
62 void
63 nsRubyFrame::CalculateColSizes(nsRenderingContext* aRenderingContext,
64 nsTArray<nscoord>& aColSizes)
66 nsFrameList::Enumerator e(this->PrincipalChildList());
67 uint32_t annotationNum = 0;
68 int segmentNum = -1;
70 nsTArray<int> segmentBaseCounts;
72 for(; !e.AtEnd(); e.Next()) {
73 nsIFrame* childFrame = e.get();
74 if (childFrame->GetType() == nsGkAtoms::rubyBaseContainerFrame) {
75 segmentNum++;
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
85 segmentNum++;
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));
96 baseCount++;
97 segmentBaseCounts.ElementAt(segmentNum) = baseCount;
98 annotationNum++;
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.
104 int baseSum = 0;
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;
114 } else {
115 aColSizes.ElementAt(annotationNum) =
116 std::max(aColSizes.ElementAt(annotationNum),
117 annotationFrame->GetPrefISize(aRenderingContext));
118 annotationNum++;
121 } else {
122 NS_ASSERTION(false, "Unrecognized child type for ruby frame.");
127 /* virtual */ void
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);
136 nscoord max = 0;
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;
146 /* virtual */ void
147 nsRubyFrame::AddInlinePrefISize(nsRenderingContext *aRenderingContext,
148 nsIFrame::InlinePrefISizeData *aData)
150 nsTArray<int> colSizes;
151 CalculateColSizes(aRenderingContext, colSizes);
153 nscoord sum = 0;
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
164 return mBaseline;
167 /* virtual */ bool
168 nsRubyFrame::CanContinueTextRun() const
170 return true;
173 /* virtual */ void
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;
186 return;
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) {
212 if (segmentRBC) {
213 annotationBSize = 0;
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);
220 segment.Next();
221 while (!segment.AtEnd() && (segment.get()->GetType() !=
222 nsGkAtoms::rubyBaseContainerFrame)) {
223 if (segment.get()->GetType() == nsGkAtoms::rubyTextContainerFrame) {
224 segmentRBC->AppendTextContainer(segment.get());
226 segment.Next();
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,
235 frameReflowStatus);
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,
247 availSize);
248 childReflowState.mLineLayout = aReflowState.mLineLayout;
249 childFrame->Reflow(aPresContext, metrics, childReflowState,
250 frameReflowStatus);
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
258 // spec'ed.
259 nscoord baseContainerBCoord;
260 if (segmentRBC) {
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)).
266 B(lineWM);
267 } else {
268 baseContainerBCoord = 0;
270 FinishReflowChild(childFrame, aPresContext, metrics, &childReflowState, 0,
271 baseContainerBCoord - metrics.BSize(lineWM), 0);
272 } else {
273 NS_NOTREACHED(
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();
280 children.Next()) {
281 nsRubyBaseContainerFrame* rbcFrame = do_QueryFrame(children.get());
282 if (rbcFrame) {
283 rbcFrame->ClearTextContainers();
287 aDesiredSize.ISize(lineWM) = aReflowState.mLineLayout->EndSpan(this);
288 nsLayoutUtils::SetBSizeFromFontMetrics(this, aDesiredSize, aReflowState,
289 borderPadding, lineWM, frameWM);