1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is Mozilla Communicator client code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 /* rendering object for CSS :first-letter pseudo-element */
41 #include "nsFirstLetterFrame.h"
42 #include "nsPresContext.h"
43 #include "nsStyleContext.h"
44 #include "nsIContent.h"
45 #include "nsLineLayout.h"
46 #include "nsGkAtoms.h"
47 #include "nsAutoPtr.h"
48 #include "nsStyleSet.h"
49 #include "nsFrameManager.h"
52 NS_NewFirstLetterFrame(nsIPresShell
* aPresShell
, nsStyleContext
* aContext
)
54 return new (aPresShell
) nsFirstLetterFrame(aContext
);
59 nsFirstLetterFrame::GetFrameName(nsAString
& aResult
) const
61 return MakeFrameName(NS_LITERAL_STRING("Letter"), aResult
);
66 nsFirstLetterFrame::GetType() const
68 return nsGkAtoms::letterFrame
;
72 nsFirstLetterFrame::GetSkipSides() const
78 nsFirstLetterFrame::Init(nsIContent
* aContent
,
80 nsIFrame
* aPrevInFlow
)
82 nsRefPtr
<nsStyleContext
> newSC
;
84 // Get proper style context for ourselves. We're creating the frame
85 // that represents everything *except* the first letter, so just create
86 // a style context like we would for a text node.
87 nsStyleContext
* parentStyleContext
= mStyleContext
->GetParent();
88 if (parentStyleContext
) {
89 newSC
= mStyleContext
->GetRuleNode()->GetPresContext()->StyleSet()->
90 ResolveStyleForNonElement(parentStyleContext
);
92 SetStyleContextWithoutNotification(newSC
);
96 return nsFirstLetterFrameSuper::Init(aContent
, aParent
, aPrevInFlow
);
100 nsFirstLetterFrame::SetInitialChildList(nsIAtom
* aListName
,
101 nsIFrame
* aChildList
)
103 mFrames
.SetFrames(aChildList
);
104 nsFrameManager
*frameManager
= PresContext()->FrameManager();
106 for (nsIFrame
* frame
= aChildList
; frame
; frame
= frame
->GetNextSibling()) {
107 NS_ASSERTION(frame
->GetParent() == this, "Unexpected parent");
108 frameManager
->ReParentStyleContext(frame
);
114 nsFirstLetterFrame::SetSelected(nsPresContext
* aPresContext
, nsIDOMRange
*aRange
,PRBool aSelected
, nsSpread aSpread
, SelectionType aType
)
116 if (aSelected
&& ParentDisablesSelection())
118 nsIFrame
*child
= GetFirstChild(nsnull
);
121 child
->SetSelected(aPresContext
, aRange
, aSelected
, aSpread
, aType
);
122 // don't worry about result. there are more frames to come
123 child
= child
->GetNextSibling();
129 nsFirstLetterFrame::GetChildFrameContainingOffset(PRInt32 inContentOffset
,
131 PRInt32
* outFrameContentOffset
,
132 nsIFrame
**outChildFrame
)
134 nsIFrame
*kid
= mFrames
.FirstChild();
137 return kid
->GetChildFrameContainingOffset(inContentOffset
, inHint
, outFrameContentOffset
, outChildFrame
);
140 return nsFrame::GetChildFrameContainingOffset(inContentOffset
, inHint
, outFrameContentOffset
, outChildFrame
);
143 // Needed for non-floating first-letter frames and for the continuations
144 // following the first-letter that we also use nsFirstLetterFrame for.
146 nsFirstLetterFrame::AddInlineMinWidth(nsIRenderingContext
*aRenderingContext
,
147 nsIFrame::InlineMinWidthData
*aData
)
149 DoInlineIntrinsicWidth(aRenderingContext
, aData
, nsLayoutUtils::MIN_WIDTH
);
152 // Needed for non-floating first-letter frames and for the continuations
153 // following the first-letter that we also use nsFirstLetterFrame for.
155 nsFirstLetterFrame::AddInlinePrefWidth(nsIRenderingContext
*aRenderingContext
,
156 nsIFrame::InlinePrefWidthData
*aData
)
158 DoInlineIntrinsicWidth(aRenderingContext
, aData
, nsLayoutUtils::PREF_WIDTH
);
161 // Needed for floating first-letter frames.
162 /* virtual */ nscoord
163 nsFirstLetterFrame::GetMinWidth(nsIRenderingContext
*aRenderingContext
)
165 return nsLayoutUtils::MinWidthFromInline(this, aRenderingContext
);
168 // Needed for floating first-letter frames.
169 /* virtual */ nscoord
170 nsFirstLetterFrame::GetPrefWidth(nsIRenderingContext
*aRenderingContext
)
172 return nsLayoutUtils::PrefWidthFromInline(this, aRenderingContext
);
176 nsFirstLetterFrame::ComputeSize(nsIRenderingContext
*aRenderingContext
,
177 nsSize aCBSize
, nscoord aAvailableWidth
,
178 nsSize aMargin
, nsSize aBorder
, nsSize aPadding
,
181 if (GetPrevInFlow()) {
182 // We're wrapping the text *after* the first letter, so behave like an
184 return nsSize(NS_UNCONSTRAINEDSIZE
, NS_UNCONSTRAINEDSIZE
);
186 return nsFirstLetterFrameSuper::ComputeSize(aRenderingContext
,
187 aCBSize
, aAvailableWidth
, aMargin
, aBorder
, aPadding
, aShrinkWrap
);
191 nsFirstLetterFrame::Reflow(nsPresContext
* aPresContext
,
192 nsHTMLReflowMetrics
& aMetrics
,
193 const nsHTMLReflowState
& aReflowState
,
194 nsReflowStatus
& aReflowStatus
)
196 DO_GLOBAL_REFLOW_COUNT("nsFirstLetterFrame");
197 DISPLAY_REFLOW(aPresContext
, this, aReflowState
, aMetrics
, aReflowStatus
);
200 // Grab overflow list
201 DrainOverflowFrames(aPresContext
);
203 nsIFrame
* kid
= mFrames
.FirstChild();
205 // Setup reflow state for our child
206 nsSize
availSize(aReflowState
.availableWidth
, aReflowState
.availableHeight
);
207 const nsMargin
& bp
= aReflowState
.mComputedBorderPadding
;
208 nscoord lr
= bp
.left
+ bp
.right
;
209 nscoord tb
= bp
.top
+ bp
.bottom
;
210 NS_ASSERTION(availSize
.width
!= NS_UNCONSTRAINEDSIZE
,
211 "should no longer use unconstrained widths");
212 availSize
.width
-= lr
;
213 if (NS_UNCONSTRAINEDSIZE
!= availSize
.height
) {
214 availSize
.height
-= tb
;
218 if (!aReflowState
.mLineLayout
) {
219 // When there is no lineLayout provided, we provide our own. The
220 // only time that the first-letter-frame is not reflowing in a
221 // line context is when its floating.
222 nsHTMLReflowState
rs(aPresContext
, aReflowState
, kid
, availSize
);
223 nsLineLayout
ll(aPresContext
, nsnull
, &aReflowState
, nsnull
);
224 ll
.BeginLineReflow(bp
.left
, bp
.top
, availSize
.width
, NS_UNCONSTRAINEDSIZE
,
226 rs
.mLineLayout
= &ll
;
227 ll
.SetFirstLetterStyleOK(PR_TRUE
);
229 kid
->WillReflow(aPresContext
);
230 kid
->Reflow(aPresContext
, aMetrics
, rs
, aReflowStatus
);
235 // Pretend we are a span and reflow the child frame
236 nsLineLayout
* ll
= aReflowState
.mLineLayout
;
239 NS_ASSERTION(ll
->GetFirstLetterStyleOK() || GetPrevInFlow(),
240 "First-in-flow first-letter should have first-letter style enabled in nsLineLayout!");
241 ll
->BeginSpan(this, &aReflowState
, bp
.left
, availSize
.width
);
242 ll
->ReflowFrame(kid
, aReflowStatus
, &aMetrics
, pushedFrame
);
246 // Place and size the child and update the output metrics
247 kid
->SetRect(nsRect(bp
.left
, bp
.top
, aMetrics
.width
, aMetrics
.height
));
248 kid
->FinishAndStoreOverflow(&aMetrics
);
249 kid
->DidReflow(aPresContext
, nsnull
, NS_FRAME_REFLOW_FINISHED
);
251 aMetrics
.width
+= lr
;
252 aMetrics
.height
+= tb
;
253 aMetrics
.ascent
+= bp
.top
;
254 mBaseline
= aMetrics
.ascent
;
256 // Ensure that the overflow rect contains the child textframe's overflow rect.
257 // Note that if this is floating, the overline/underline drawable area is in
258 // the overflow rect of the child textframe.
259 aMetrics
.mOverflowArea
.UnionRect(aMetrics
.mOverflowArea
,
260 nsRect(0, 0, aMetrics
.width
, aMetrics
.height
));
261 ConsiderChildOverflow(aMetrics
.mOverflowArea
, kid
);
263 // Create a continuation or remove existing continuations based on
264 // the reflow completion status.
265 if (NS_FRAME_IS_COMPLETE(aReflowStatus
)) {
266 if (aReflowState
.mLineLayout
) {
267 aReflowState
.mLineLayout
->SetFirstLetterStyleOK(PR_FALSE
);
269 nsIFrame
* kidNextInFlow
= kid
->GetNextInFlow();
271 // Remove all of the childs next-in-flows
272 static_cast<nsContainerFrame
*>(kidNextInFlow
->GetParent())
273 ->DeleteNextInFlowChild(aPresContext
, kidNextInFlow
);
277 // Create a continuation for the child frame if it doesn't already
279 nsIFrame
* nextInFlow
;
280 rv
= CreateNextInFlow(aPresContext
, this, kid
, nextInFlow
);
285 // And then push it to our overflow list
287 kid
->SetNextSibling(nsnull
);
288 SetOverflowFrames(aPresContext
, nextInFlow
);
291 nsIFrame
* nextSib
= kid
->GetNextSibling();
293 kid
->SetNextSibling(nsnull
);
294 SetOverflowFrames(aPresContext
, nextSib
);
299 FinishAndStoreOverflow(&aMetrics
);
301 NS_FRAME_SET_TRUNCATION(aReflowStatus
, aReflowState
, aMetrics
);
306 nsFirstLetterFrame::CanContinueTextRun() const
308 // We can continue a text run through a first-letter frame.
313 nsFirstLetterFrame::DrainOverflowFrames(nsPresContext
* aPresContext
)
315 nsIFrame
* overflowFrames
;
317 // Check for an overflow list with our prev-in-flow
318 nsFirstLetterFrame
* prevInFlow
= (nsFirstLetterFrame
*)GetPrevInFlow();
319 if (nsnull
!= prevInFlow
) {
320 overflowFrames
= prevInFlow
->GetOverflowFrames(aPresContext
, PR_TRUE
);
321 if (overflowFrames
) {
322 NS_ASSERTION(mFrames
.IsEmpty(), "bad overflow list");
324 // When pushing and pulling frames we need to check for whether any
325 // views need to be reparented.
326 nsIFrame
* f
= overflowFrames
;
328 nsHTMLContainerFrame::ReparentFrameView(aPresContext
, f
, prevInFlow
, this);
329 f
= f
->GetNextSibling();
331 mFrames
.InsertFrames(this, nsnull
, overflowFrames
);
335 // It's also possible that we have an overflow list for ourselves
336 overflowFrames
= GetOverflowFrames(aPresContext
, PR_TRUE
);
337 if (overflowFrames
) {
338 NS_ASSERTION(mFrames
.NotEmpty(), "overflow list w/o frames");
339 mFrames
.AppendFrames(nsnull
, overflowFrames
);
342 // Now repair our first frames style context (since we only reflow
343 // one frame there is no point in doing any other ones until they
345 nsIFrame
* kid
= mFrames
.FirstChild();
347 nsRefPtr
<nsStyleContext
> sc
;
348 nsIContent
* kidContent
= kid
->GetContent();
350 NS_ASSERTION(kidContent
->IsNodeOfType(nsINode::eTEXT
),
351 "should contain only text nodes");
352 sc
= aPresContext
->StyleSet()->ResolveStyleForNonElement(mStyleContext
);
354 kid
->SetStyleContext(sc
);