Bug 54488 - "[Mac] Non-draggable widgets in background windows should look disabled...
[mozilla-central.git] / layout / generic / nsFirstLetterFrame.cpp
blob2de8fdadcd3c6160a233664c1c0f0ffcd3470481
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
13 * License.
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.
22 * Contributor(s):
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 */
40 #include "nsCOMPtr.h"
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"
51 nsIFrame*
52 NS_NewFirstLetterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
54 return new (aPresShell) nsFirstLetterFrame(aContext);
57 #ifdef NS_DEBUG
58 NS_IMETHODIMP
59 nsFirstLetterFrame::GetFrameName(nsAString& aResult) const
61 return MakeFrameName(NS_LITERAL_STRING("Letter"), aResult);
63 #endif
65 nsIAtom*
66 nsFirstLetterFrame::GetType() const
68 return nsGkAtoms::letterFrame;
71 PRIntn
72 nsFirstLetterFrame::GetSkipSides() const
74 return 0;
77 NS_IMETHODIMP
78 nsFirstLetterFrame::Init(nsIContent* aContent,
79 nsIFrame* aParent,
80 nsIFrame* aPrevInFlow)
82 nsRefPtr<nsStyleContext> newSC;
83 if (aPrevInFlow) {
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);
91 if (newSC)
92 SetStyleContextWithoutNotification(newSC);
96 return nsFirstLetterFrameSuper::Init(aContent, aParent, aPrevInFlow);
99 NS_IMETHODIMP
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);
110 return NS_OK;
113 NS_IMETHODIMP
114 nsFirstLetterFrame::SetSelected(nsPresContext* aPresContext, nsIDOMRange *aRange,PRBool aSelected, nsSpread aSpread, SelectionType aType)
116 if (aSelected && ParentDisablesSelection())
117 return NS_OK;
118 nsIFrame *child = GetFirstChild(nsnull);
119 while (child)
121 child->SetSelected(aPresContext, aRange, aSelected, aSpread, aType);
122 // don't worry about result. there are more frames to come
123 child = child->GetNextSibling();
125 return NS_OK;
128 NS_IMETHODIMP
129 nsFirstLetterFrame::GetChildFrameContainingOffset(PRInt32 inContentOffset,
130 PRBool inHint,
131 PRInt32* outFrameContentOffset,
132 nsIFrame **outChildFrame)
134 nsIFrame *kid = mFrames.FirstChild();
135 if (kid)
137 return kid->GetChildFrameContainingOffset(inContentOffset, inHint, outFrameContentOffset, outChildFrame);
139 else
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.
145 /* virtual */ void
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.
154 /* virtual */ void
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);
175 /* virtual */ nsSize
176 nsFirstLetterFrame::ComputeSize(nsIRenderingContext *aRenderingContext,
177 nsSize aCBSize, nscoord aAvailableWidth,
178 nsSize aMargin, nsSize aBorder, nsSize aPadding,
179 PRBool aShrinkWrap)
181 if (GetPrevInFlow()) {
182 // We're wrapping the text *after* the first letter, so behave like an
183 // inline frame.
184 return nsSize(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
186 return nsFirstLetterFrameSuper::ComputeSize(aRenderingContext,
187 aCBSize, aAvailableWidth, aMargin, aBorder, aPadding, aShrinkWrap);
190 NS_IMETHODIMP
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);
198 nsresult rv = NS_OK;
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;
217 // Reflow the child
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,
225 PR_FALSE, PR_TRUE);
226 rs.mLineLayout = &ll;
227 ll.SetFirstLetterStyleOK(PR_TRUE);
229 kid->WillReflow(aPresContext);
230 kid->Reflow(aPresContext, aMetrics, rs, aReflowStatus);
232 ll.EndLineReflow();
234 else {
235 // Pretend we are a span and reflow the child frame
236 nsLineLayout* ll = aReflowState.mLineLayout;
237 PRBool pushedFrame;
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);
243 ll->EndSpan(this);
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();
270 if (kidNextInFlow) {
271 // Remove all of the childs next-in-flows
272 static_cast<nsContainerFrame*>(kidNextInFlow->GetParent())
273 ->DeleteNextInFlowChild(aPresContext, kidNextInFlow);
276 else {
277 // Create a continuation for the child frame if it doesn't already
278 // have one.
279 nsIFrame* nextInFlow;
280 rv = CreateNextInFlow(aPresContext, this, kid, nextInFlow);
281 if (NS_FAILED(rv)) {
282 return rv;
285 // And then push it to our overflow list
286 if (nextInFlow) {
287 kid->SetNextSibling(nsnull);
288 SetOverflowFrames(aPresContext, nextInFlow);
290 else {
291 nsIFrame* nextSib = kid->GetNextSibling();
292 if (nextSib) {
293 kid->SetNextSibling(nsnull);
294 SetOverflowFrames(aPresContext, nextSib);
299 FinishAndStoreOverflow(&aMetrics);
301 NS_FRAME_SET_TRUNCATION(aReflowStatus, aReflowState, aMetrics);
302 return rv;
305 /* virtual */ PRBool
306 nsFirstLetterFrame::CanContinueTextRun() const
308 // We can continue a text run through a first-letter frame.
309 return PR_TRUE;
312 void
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;
327 while (f) {
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
344 // are reflowed)
345 nsIFrame* kid = mFrames.FirstChild();
346 if (kid) {
347 nsRefPtr<nsStyleContext> sc;
348 nsIContent* kidContent = kid->GetContent();
349 if (kidContent) {
350 NS_ASSERTION(kidContent->IsNodeOfType(nsINode::eTEXT),
351 "should contain only text nodes");
352 sc = aPresContext->StyleSet()->ResolveStyleForNonElement(mStyleContext);
353 if (sc) {
354 kid->SetStyleContext(sc);