Bumping manifests a=b2g-bump
[gecko.git] / layout / generic / nsFirstLetterFrame.cpp
blobe1a3c52511583c91857427cdc825fa4138c61941
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 /* rendering object for CSS :first-letter pseudo-element */
8 #include "nsFirstLetterFrame.h"
9 #include "nsPresContext.h"
10 #include "nsStyleContext.h"
11 #include "nsIContent.h"
12 #include "nsLineLayout.h"
13 #include "nsGkAtoms.h"
14 #include "nsAutoPtr.h"
15 #include "nsStyleSet.h"
16 #include "nsFrameManager.h"
17 #include "RestyleManager.h"
18 #include "nsPlaceholderFrame.h"
19 #include "nsCSSFrameConstructor.h"
21 using namespace mozilla;
22 using namespace mozilla::layout;
24 nsFirstLetterFrame*
25 NS_NewFirstLetterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
27 return new (aPresShell) nsFirstLetterFrame(aContext);
30 NS_IMPL_FRAMEARENA_HELPERS(nsFirstLetterFrame)
32 NS_QUERYFRAME_HEAD(nsFirstLetterFrame)
33 NS_QUERYFRAME_ENTRY(nsFirstLetterFrame)
34 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
36 #ifdef DEBUG_FRAME_DUMP
37 nsresult
38 nsFirstLetterFrame::GetFrameName(nsAString& aResult) const
40 return MakeFrameName(NS_LITERAL_STRING("Letter"), aResult);
42 #endif
44 nsIAtom*
45 nsFirstLetterFrame::GetType() const
47 return nsGkAtoms::letterFrame;
50 void
51 nsFirstLetterFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
52 const nsRect& aDirtyRect,
53 const nsDisplayListSet& aLists)
55 BuildDisplayListForInline(aBuilder, aDirtyRect, aLists);
58 void
59 nsFirstLetterFrame::Init(nsIContent* aContent,
60 nsContainerFrame* aParent,
61 nsIFrame* aPrevInFlow)
63 nsRefPtr<nsStyleContext> newSC;
64 if (aPrevInFlow) {
65 // Get proper style context for ourselves. We're creating the frame
66 // that represents everything *except* the first letter, so just create
67 // a style context like we would for a text node.
68 nsStyleContext* parentStyleContext = mStyleContext->GetParent();
69 if (parentStyleContext) {
70 newSC = PresContext()->StyleSet()->
71 ResolveStyleForNonElement(parentStyleContext);
72 SetStyleContextWithoutNotification(newSC);
76 nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
79 void
80 nsFirstLetterFrame::SetInitialChildList(ChildListID aListID,
81 nsFrameList& aChildList)
83 RestyleManager* restyleManager = PresContext()->RestyleManager();
85 for (nsFrameList::Enumerator e(aChildList); !e.AtEnd(); e.Next()) {
86 NS_ASSERTION(e.get()->GetParent() == this, "Unexpected parent");
87 restyleManager->ReparentStyleContext(e.get());
88 nsLayoutUtils::MarkDescendantsDirty(e.get());
91 mFrames.SetFrames(aChildList);
94 nsresult
95 nsFirstLetterFrame::GetChildFrameContainingOffset(int32_t inContentOffset,
96 bool inHint,
97 int32_t* outFrameContentOffset,
98 nsIFrame **outChildFrame)
100 nsIFrame *kid = mFrames.FirstChild();
101 if (kid)
103 return kid->GetChildFrameContainingOffset(inContentOffset, inHint, outFrameContentOffset, outChildFrame);
105 else
106 return nsFrame::GetChildFrameContainingOffset(inContentOffset, inHint, outFrameContentOffset, outChildFrame);
109 // Needed for non-floating first-letter frames and for the continuations
110 // following the first-letter that we also use nsFirstLetterFrame for.
111 /* virtual */ void
112 nsFirstLetterFrame::AddInlineMinISize(nsRenderingContext *aRenderingContext,
113 nsIFrame::InlineMinISizeData *aData)
115 DoInlineIntrinsicISize(aRenderingContext, aData, nsLayoutUtils::MIN_ISIZE);
118 // Needed for non-floating first-letter frames and for the continuations
119 // following the first-letter that we also use nsFirstLetterFrame for.
120 /* virtual */ void
121 nsFirstLetterFrame::AddInlinePrefISize(nsRenderingContext *aRenderingContext,
122 nsIFrame::InlinePrefISizeData *aData)
124 DoInlineIntrinsicISize(aRenderingContext, aData, nsLayoutUtils::PREF_ISIZE);
127 // Needed for floating first-letter frames.
128 /* virtual */ nscoord
129 nsFirstLetterFrame::GetMinISize(nsRenderingContext *aRenderingContext)
131 return nsLayoutUtils::MinISizeFromInline(this, aRenderingContext);
134 // Needed for floating first-letter frames.
135 /* virtual */ nscoord
136 nsFirstLetterFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
138 return nsLayoutUtils::PrefISizeFromInline(this, aRenderingContext);
141 /* virtual */
142 LogicalSize
143 nsFirstLetterFrame::ComputeSize(nsRenderingContext *aRenderingContext,
144 WritingMode aWM,
145 const LogicalSize& aCBSize,
146 nscoord aAvailableISize,
147 const LogicalSize& aMargin,
148 const LogicalSize& aBorder,
149 const LogicalSize& aPadding,
150 uint32_t aFlags)
152 if (GetPrevInFlow()) {
153 // We're wrapping the text *after* the first letter, so behave like an
154 // inline frame.
155 return LogicalSize(aWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
157 return nsContainerFrame::ComputeSize(aRenderingContext, aWM,
158 aCBSize, aAvailableISize, aMargin, aBorder, aPadding, aFlags);
161 void
162 nsFirstLetterFrame::Reflow(nsPresContext* aPresContext,
163 nsHTMLReflowMetrics& aMetrics,
164 const nsHTMLReflowState& aReflowState,
165 nsReflowStatus& aReflowStatus)
167 DO_GLOBAL_REFLOW_COUNT("nsFirstLetterFrame");
168 DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aReflowStatus);
169 nsresult rv = NS_OK;
171 // Grab overflow list
172 DrainOverflowFrames(aPresContext);
174 nsIFrame* kid = mFrames.FirstChild();
176 // Setup reflow state for our child
177 WritingMode wm = aReflowState.GetWritingMode();
178 LogicalSize availSize = aReflowState.AvailableSize();
179 const LogicalMargin& bp = aReflowState.ComputedLogicalBorderPadding();
180 NS_ASSERTION(availSize.ISize(wm) != NS_UNCONSTRAINEDSIZE,
181 "should no longer use unconstrained inline size");
182 availSize.ISize(wm) -= bp.IStartEnd(wm);
183 if (NS_UNCONSTRAINEDSIZE != availSize.BSize(wm)) {
184 availSize.BSize(wm) -= bp.BStartEnd(wm);
187 WritingMode lineWM = aMetrics.GetWritingMode();
188 nsHTMLReflowMetrics kidMetrics(lineWM);
190 // Reflow the child
191 if (!aReflowState.mLineLayout) {
192 // When there is no lineLayout provided, we provide our own. The
193 // only time that the first-letter-frame is not reflowing in a
194 // line context is when its floating.
195 WritingMode kidWritingMode = GetWritingMode(kid);
196 LogicalSize kidAvailSize = availSize.ConvertTo(kidWritingMode, wm);
197 nsHTMLReflowState rs(aPresContext, aReflowState, kid, kidAvailSize);
198 nsLineLayout ll(aPresContext, nullptr, &aReflowState, nullptr);
200 ll.BeginLineReflow(bp.IStart(wm), bp.BStart(wm),
201 availSize.ISize(wm), NS_UNCONSTRAINEDSIZE,
202 false, true, kidWritingMode,
203 aReflowState.AvailableWidth());
204 rs.mLineLayout = &ll;
205 ll.SetInFirstLetter(true);
206 ll.SetFirstLetterStyleOK(true);
208 kid->WillReflow(aPresContext);
209 kid->Reflow(aPresContext, kidMetrics, rs, aReflowStatus);
211 ll.EndLineReflow();
212 ll.SetInFirstLetter(false);
214 // In the floating first-letter case, we need to set this ourselves;
215 // nsLineLayout::BeginSpan will set it in the other case
216 mBaseline = kidMetrics.BlockStartAscent();
218 // Place and size the child and update the output metrics
219 LogicalSize convertedSize = kidMetrics.Size(lineWM).ConvertTo(wm, lineWM);
220 kid->SetRect(nsRect(bp.IStart(wm), bp.BStart(wm),
221 convertedSize.ISize(wm), convertedSize.BSize(wm)));
222 kid->FinishAndStoreOverflow(&kidMetrics);
223 kid->DidReflow(aPresContext, nullptr, nsDidReflowStatus::FINISHED);
225 convertedSize.ISize(wm) += bp.IStartEnd(wm);
226 convertedSize.BSize(wm) += bp.BStartEnd(wm);
227 aMetrics.SetSize(wm, convertedSize);
228 aMetrics.SetBlockStartAscent(kidMetrics.BlockStartAscent() +
229 bp.BStart(wm));
231 else {
232 // Pretend we are a span and reflow the child frame
233 nsLineLayout* ll = aReflowState.mLineLayout;
234 bool pushedFrame;
236 ll->SetInFirstLetter(
237 mStyleContext->GetPseudo() == nsCSSPseudoElements::firstLetter);
238 ll->BeginSpan(this, &aReflowState, bp.IStart(wm),
239 availSize.ISize(wm), &mBaseline);
240 ll->ReflowFrame(kid, aReflowStatus, &kidMetrics, pushedFrame);
241 NS_ASSERTION(lineWM.IsVertical() == wm.IsVertical(),
242 "we're assuming we can mix sizes between lineWM and wm "
243 "since we shouldn't have orthogonal writing modes within "
244 "a line.");
245 aMetrics.ISize(lineWM) = ll->EndSpan(this) + bp.IStartEnd(wm);
246 ll->SetInFirstLetter(false);
248 nsLayoutUtils::SetBSizeFromFontMetrics(this, aMetrics, aReflowState,
249 bp, lineWM, wm);
252 // Ensure that the overflow rect contains the child textframe's overflow rect.
253 // Note that if this is floating, the overline/underline drawable area is in
254 // the overflow rect of the child textframe.
255 aMetrics.UnionOverflowAreasWithDesiredBounds();
256 ConsiderChildOverflow(aMetrics.mOverflowAreas, kid);
258 if (!NS_INLINE_IS_BREAK_BEFORE(aReflowStatus)) {
259 // Create a continuation or remove existing continuations based on
260 // the reflow completion status.
261 if (NS_FRAME_IS_COMPLETE(aReflowStatus)) {
262 if (aReflowState.mLineLayout) {
263 aReflowState.mLineLayout->SetFirstLetterStyleOK(false);
265 nsIFrame* kidNextInFlow = kid->GetNextInFlow();
266 if (kidNextInFlow) {
267 // Remove all of the childs next-in-flows
268 kidNextInFlow->GetParent()->DeleteNextInFlowChild(kidNextInFlow, true);
271 else {
272 // Create a continuation for the child frame if it doesn't already
273 // have one.
274 if (!IsFloating()) {
275 nsIFrame* nextInFlow;
276 rv = CreateNextInFlow(kid, nextInFlow);
277 if (NS_FAILED(rv)) {
278 return;
281 // And then push it to our overflow list
282 const nsFrameList& overflow = mFrames.RemoveFramesAfter(kid);
283 if (overflow.NotEmpty()) {
284 SetOverflowFrames(overflow);
286 } else if (!kid->GetNextInFlow()) {
287 // For floating first letter frames (if a continuation wasn't already
288 // created for us) we need to put the continuation with the rest of the
289 // text that the first letter frame was made out of.
290 nsIFrame* continuation;
291 CreateContinuationForFloatingParent(aPresContext, kid,
292 &continuation, true);
297 FinishAndStoreOverflow(&aMetrics);
299 NS_FRAME_SET_TRUNCATION(aReflowStatus, aReflowState, aMetrics);
302 /* virtual */ bool
303 nsFirstLetterFrame::CanContinueTextRun() const
305 // We can continue a text run through a first-letter frame.
306 return true;
309 nsresult
310 nsFirstLetterFrame::CreateContinuationForFloatingParent(nsPresContext* aPresContext,
311 nsIFrame* aChild,
312 nsIFrame** aContinuation,
313 bool aIsFluid)
315 NS_ASSERTION(IsFloating(),
316 "can only call this on floating first letter frames");
317 NS_PRECONDITION(aContinuation, "bad args");
319 *aContinuation = nullptr;
321 nsIPresShell* presShell = aPresContext->PresShell();
322 nsPlaceholderFrame* placeholderFrame =
323 presShell->FrameManager()->GetPlaceholderFrameFor(this);
324 nsContainerFrame* parent = placeholderFrame->GetParent();
326 nsIFrame* continuation = presShell->FrameConstructor()->
327 CreateContinuingFrame(aPresContext, aChild, parent, aIsFluid);
329 // The continuation will have gotten the first letter style from its
330 // prev continuation, so we need to repair the style context so it
331 // doesn't have the first letter styling.
332 nsStyleContext* parentSC = this->StyleContext()->GetParent();
333 if (parentSC) {
334 nsRefPtr<nsStyleContext> newSC;
335 newSC = presShell->StyleSet()->ResolveStyleForNonElement(parentSC);
336 continuation->SetStyleContext(newSC);
337 nsLayoutUtils::MarkDescendantsDirty(continuation);
340 //XXX Bidi may not be involved but we have to use the list name
341 // kNoReflowPrincipalList because this is just like creating a continuation
342 // except we have to insert it in a different place and we don't want a
343 // reflow command to try to be issued.
344 nsFrameList temp(continuation, continuation);
345 parent->InsertFrames(kNoReflowPrincipalList, placeholderFrame, temp);
347 *aContinuation = continuation;
348 return NS_OK;
351 void
352 nsFirstLetterFrame::DrainOverflowFrames(nsPresContext* aPresContext)
354 // Check for an overflow list with our prev-in-flow
355 nsFirstLetterFrame* prevInFlow = (nsFirstLetterFrame*)GetPrevInFlow();
356 if (prevInFlow) {
357 AutoFrameListPtr overflowFrames(aPresContext,
358 prevInFlow->StealOverflowFrames());
359 if (overflowFrames) {
360 NS_ASSERTION(mFrames.IsEmpty(), "bad overflow list");
362 // When pushing and pulling frames we need to check for whether any
363 // views need to be reparented.
364 nsContainerFrame::ReparentFrameViewList(*overflowFrames, prevInFlow,
365 this);
366 mFrames.InsertFrames(this, nullptr, *overflowFrames);
370 // It's also possible that we have an overflow list for ourselves
371 AutoFrameListPtr overflowFrames(aPresContext, StealOverflowFrames());
372 if (overflowFrames) {
373 NS_ASSERTION(mFrames.NotEmpty(), "overflow list w/o frames");
374 mFrames.AppendFrames(nullptr, *overflowFrames);
377 // Now repair our first frames style context (since we only reflow
378 // one frame there is no point in doing any other ones until they
379 // are reflowed)
380 nsIFrame* kid = mFrames.FirstChild();
381 if (kid) {
382 nsRefPtr<nsStyleContext> sc;
383 nsIContent* kidContent = kid->GetContent();
384 if (kidContent) {
385 NS_ASSERTION(kidContent->IsNodeOfType(nsINode::eTEXT),
386 "should contain only text nodes");
387 nsStyleContext* parentSC = prevInFlow ? mStyleContext->GetParent() :
388 mStyleContext;
389 sc = aPresContext->StyleSet()->ResolveStyleForNonElement(parentSC);
390 kid->SetStyleContext(sc);
391 nsLayoutUtils::MarkDescendantsDirty(kid);
396 nscoord
397 nsFirstLetterFrame::GetLogicalBaseline(WritingMode aWritingMode) const
399 return mBaseline;
402 nsIFrame::LogicalSides
403 nsFirstLetterFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const
405 if (GetPrevContinuation()) {
406 // We shouldn't get calls to GetSkipSides for later continuations since
407 // they have separate style contexts with initial values for all the
408 // properties that could trigger a call to GetSkipSides. Then again,
409 // it's not really an error to call GetSkipSides on any frame, so
410 // that's why we handle it properly.
411 return LogicalSides(eLogicalSideBitsAll);
413 return LogicalSides(); // first continuation displays all sides