Merge m-c to fx-team.
[gecko.git] / layout / generic / nsFirstLetterFrame.cpp
blob576152dc72fd7eb94b671e838552b5af775cc7bb
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 nsIFrame*
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
37 NS_IMETHODIMP
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 nsIFrame* 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 NS_IMETHODIMP
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());
90 mFrames.SetFrames(aChildList);
91 return NS_OK;
94 NS_IMETHODIMP
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::AddInlineMinWidth(nsRenderingContext *aRenderingContext,
113 nsIFrame::InlineMinWidthData *aData)
115 DoInlineIntrinsicWidth(aRenderingContext, aData, nsLayoutUtils::MIN_WIDTH);
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::AddInlinePrefWidth(nsRenderingContext *aRenderingContext,
122 nsIFrame::InlinePrefWidthData *aData)
124 DoInlineIntrinsicWidth(aRenderingContext, aData, nsLayoutUtils::PREF_WIDTH);
127 // Needed for floating first-letter frames.
128 /* virtual */ nscoord
129 nsFirstLetterFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
131 return nsLayoutUtils::MinWidthFromInline(this, aRenderingContext);
134 // Needed for floating first-letter frames.
135 /* virtual */ nscoord
136 nsFirstLetterFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
138 return nsLayoutUtils::PrefWidthFromInline(this, aRenderingContext);
141 /* virtual */ nsSize
142 nsFirstLetterFrame::ComputeSize(nsRenderingContext *aRenderingContext,
143 nsSize aCBSize, nscoord aAvailableWidth,
144 nsSize aMargin, nsSize aBorder, nsSize aPadding,
145 uint32_t aFlags)
147 if (GetPrevInFlow()) {
148 // We're wrapping the text *after* the first letter, so behave like an
149 // inline frame.
150 return nsSize(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
152 return nsContainerFrame::ComputeSize(aRenderingContext,
153 aCBSize, aAvailableWidth, aMargin, aBorder, aPadding, aFlags);
156 NS_IMETHODIMP
157 nsFirstLetterFrame::Reflow(nsPresContext* aPresContext,
158 nsHTMLReflowMetrics& aMetrics,
159 const nsHTMLReflowState& aReflowState,
160 nsReflowStatus& aReflowStatus)
162 DO_GLOBAL_REFLOW_COUNT("nsFirstLetterFrame");
163 DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aReflowStatus);
164 nsresult rv = NS_OK;
166 // Grab overflow list
167 DrainOverflowFrames(aPresContext);
169 nsIFrame* kid = mFrames.FirstChild();
171 // Setup reflow state for our child
172 nsSize availSize(aReflowState.availableWidth, aReflowState.availableHeight);
173 const nsMargin& bp = aReflowState.mComputedBorderPadding;
174 nscoord lr = bp.left + bp.right;
175 nscoord tb = bp.top + bp.bottom;
176 NS_ASSERTION(availSize.width != NS_UNCONSTRAINEDSIZE,
177 "should no longer use unconstrained widths");
178 availSize.width -= lr;
179 if (NS_UNCONSTRAINEDSIZE != availSize.height) {
180 availSize.height -= tb;
183 // Reflow the child
184 if (!aReflowState.mLineLayout) {
185 // When there is no lineLayout provided, we provide our own. The
186 // only time that the first-letter-frame is not reflowing in a
187 // line context is when its floating.
188 nsHTMLReflowState rs(aPresContext, aReflowState, kid, availSize);
189 nsLineLayout ll(aPresContext, nullptr, &aReflowState, nullptr);
191 // For unicode-bidi: plaintext, we need to get the direction of the line
192 // from the resolved paragraph level of the child, not the block frame,
193 // because the block frame could be split by hard line breaks into
194 // multiple paragraphs with different base direction
195 uint8_t direction;
196 nsIFrame* containerFrame = ll.LineContainerFrame();
197 if (containerFrame->StyleTextReset()->mUnicodeBidi &
198 NS_STYLE_UNICODE_BIDI_PLAINTEXT) {
199 FramePropertyTable *propTable = aPresContext->PropertyTable();
200 direction = NS_PTR_TO_INT32(propTable->Get(kid, BaseLevelProperty())) & 1;
201 } else {
202 direction = containerFrame->StyleVisibility()->mDirection;
204 ll.BeginLineReflow(bp.left, bp.top, availSize.width, NS_UNCONSTRAINEDSIZE,
205 false, true, direction);
206 rs.mLineLayout = &ll;
207 ll.SetInFirstLetter(true);
208 ll.SetFirstLetterStyleOK(true);
210 kid->WillReflow(aPresContext);
211 kid->Reflow(aPresContext, aMetrics, rs, aReflowStatus);
213 ll.EndLineReflow();
214 ll.SetInFirstLetter(false);
216 // In the floating first-letter case, we need to set this ourselves;
217 // nsLineLayout::BeginSpan will set it in the other case
218 mBaseline = aMetrics.ascent;
220 else {
221 // Pretend we are a span and reflow the child frame
222 nsLineLayout* ll = aReflowState.mLineLayout;
223 bool pushedFrame;
225 ll->SetInFirstLetter(
226 mStyleContext->GetPseudo() == nsCSSPseudoElements::firstLetter);
227 ll->BeginSpan(this, &aReflowState, bp.left, availSize.width, &mBaseline);
228 ll->ReflowFrame(kid, aReflowStatus, &aMetrics, pushedFrame);
229 ll->EndSpan(this);
230 ll->SetInFirstLetter(false);
233 // Place and size the child and update the output metrics
234 kid->SetRect(nsRect(bp.left, bp.top, aMetrics.width, aMetrics.height));
235 kid->FinishAndStoreOverflow(&aMetrics);
236 kid->DidReflow(aPresContext, nullptr, nsDidReflowStatus::FINISHED);
238 aMetrics.width += lr;
239 aMetrics.height += tb;
240 aMetrics.ascent += bp.top;
242 // Ensure that the overflow rect contains the child textframe's overflow rect.
243 // Note that if this is floating, the overline/underline drawable area is in
244 // the overflow rect of the child textframe.
245 aMetrics.UnionOverflowAreasWithDesiredBounds();
246 ConsiderChildOverflow(aMetrics.mOverflowAreas, kid);
248 if (!NS_INLINE_IS_BREAK_BEFORE(aReflowStatus)) {
249 // Create a continuation or remove existing continuations based on
250 // the reflow completion status.
251 if (NS_FRAME_IS_COMPLETE(aReflowStatus)) {
252 if (aReflowState.mLineLayout) {
253 aReflowState.mLineLayout->SetFirstLetterStyleOK(false);
255 nsIFrame* kidNextInFlow = kid->GetNextInFlow();
256 if (kidNextInFlow) {
257 // Remove all of the childs next-in-flows
258 static_cast<nsContainerFrame*>(kidNextInFlow->GetParent())
259 ->DeleteNextInFlowChild(aPresContext, kidNextInFlow, true);
262 else {
263 // Create a continuation for the child frame if it doesn't already
264 // have one.
265 if (!IsFloating()) {
266 nsIFrame* nextInFlow;
267 rv = CreateNextInFlow(aPresContext, kid, nextInFlow);
268 if (NS_FAILED(rv)) {
269 return rv;
272 // And then push it to our overflow list
273 const nsFrameList& overflow = mFrames.RemoveFramesAfter(kid);
274 if (overflow.NotEmpty()) {
275 SetOverflowFrames(aPresContext, overflow);
277 } else if (!kid->GetNextInFlow()) {
278 // For floating first letter frames (if a continuation wasn't already
279 // created for us) we need to put the continuation with the rest of the
280 // text that the first letter frame was made out of.
281 nsIFrame* continuation;
282 rv = CreateContinuationForFloatingParent(aPresContext, kid,
283 &continuation, true);
288 FinishAndStoreOverflow(&aMetrics);
290 NS_FRAME_SET_TRUNCATION(aReflowStatus, aReflowState, aMetrics);
291 return rv;
294 /* virtual */ bool
295 nsFirstLetterFrame::CanContinueTextRun() const
297 // We can continue a text run through a first-letter frame.
298 return true;
301 nsresult
302 nsFirstLetterFrame::CreateContinuationForFloatingParent(nsPresContext* aPresContext,
303 nsIFrame* aChild,
304 nsIFrame** aContinuation,
305 bool aIsFluid)
307 NS_ASSERTION(IsFloating(),
308 "can only call this on floating first letter frames");
309 NS_PRECONDITION(aContinuation, "bad args");
311 *aContinuation = nullptr;
312 nsresult rv = NS_OK;
314 nsIPresShell* presShell = aPresContext->PresShell();
315 nsPlaceholderFrame* placeholderFrame =
316 presShell->FrameManager()->GetPlaceholderFrameFor(this);
317 nsIFrame* parent = placeholderFrame->GetParent();
319 nsIFrame* continuation = presShell->FrameConstructor()->
320 CreateContinuingFrame(aPresContext, aChild, parent, aIsFluid);
322 // The continuation will have gotten the first letter style from it's
323 // prev continuation, so we need to repair the style context so it
324 // doesn't have the first letter styling.
325 nsStyleContext* parentSC = this->StyleContext()->GetParent();
326 if (parentSC) {
327 nsRefPtr<nsStyleContext> newSC;
328 newSC = presShell->StyleSet()->ResolveStyleForNonElement(parentSC);
329 continuation->SetStyleContext(newSC);
332 //XXX Bidi may not be involved but we have to use the list name
333 // kNoReflowPrincipalList because this is just like creating a continuation
334 // except we have to insert it in a different place and we don't want a
335 // reflow command to try to be issued.
336 nsFrameList temp(continuation, continuation);
337 rv = parent->InsertFrames(kNoReflowPrincipalList, placeholderFrame, temp);
339 *aContinuation = continuation;
340 return rv;
343 void
344 nsFirstLetterFrame::DrainOverflowFrames(nsPresContext* aPresContext)
346 // Check for an overflow list with our prev-in-flow
347 nsFirstLetterFrame* prevInFlow = (nsFirstLetterFrame*)GetPrevInFlow();
348 if (prevInFlow) {
349 AutoFrameListPtr overflowFrames(aPresContext,
350 prevInFlow->StealOverflowFrames());
351 if (overflowFrames) {
352 NS_ASSERTION(mFrames.IsEmpty(), "bad overflow list");
354 // When pushing and pulling frames we need to check for whether any
355 // views need to be reparented.
356 nsContainerFrame::ReparentFrameViewList(aPresContext, *overflowFrames,
357 prevInFlow, this);
358 mFrames.InsertFrames(this, nullptr, *overflowFrames);
362 // It's also possible that we have an overflow list for ourselves
363 AutoFrameListPtr overflowFrames(aPresContext, StealOverflowFrames());
364 if (overflowFrames) {
365 NS_ASSERTION(mFrames.NotEmpty(), "overflow list w/o frames");
366 mFrames.AppendFrames(nullptr, *overflowFrames);
369 // Now repair our first frames style context (since we only reflow
370 // one frame there is no point in doing any other ones until they
371 // are reflowed)
372 nsIFrame* kid = mFrames.FirstChild();
373 if (kid) {
374 nsRefPtr<nsStyleContext> sc;
375 nsIContent* kidContent = kid->GetContent();
376 if (kidContent) {
377 NS_ASSERTION(kidContent->IsNodeOfType(nsINode::eTEXT),
378 "should contain only text nodes");
379 sc = aPresContext->StyleSet()->ResolveStyleForNonElement(mStyleContext);
380 kid->SetStyleContext(sc);
385 nscoord
386 nsFirstLetterFrame::GetBaseline() const
388 return mBaseline;