1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/generic/nsFirstLetterFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,397 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/* rendering object for CSS :first-letter pseudo-element */ 1.10 + 1.11 +#include "nsFirstLetterFrame.h" 1.12 +#include "nsPresContext.h" 1.13 +#include "nsStyleContext.h" 1.14 +#include "nsIContent.h" 1.15 +#include "nsLineLayout.h" 1.16 +#include "nsGkAtoms.h" 1.17 +#include "nsAutoPtr.h" 1.18 +#include "nsStyleSet.h" 1.19 +#include "nsFrameManager.h" 1.20 +#include "RestyleManager.h" 1.21 +#include "nsPlaceholderFrame.h" 1.22 +#include "nsCSSFrameConstructor.h" 1.23 + 1.24 +using namespace mozilla; 1.25 +using namespace mozilla::layout; 1.26 + 1.27 +nsIFrame* 1.28 +NS_NewFirstLetterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) 1.29 +{ 1.30 + return new (aPresShell) nsFirstLetterFrame(aContext); 1.31 +} 1.32 + 1.33 +NS_IMPL_FRAMEARENA_HELPERS(nsFirstLetterFrame) 1.34 + 1.35 +NS_QUERYFRAME_HEAD(nsFirstLetterFrame) 1.36 + NS_QUERYFRAME_ENTRY(nsFirstLetterFrame) 1.37 +NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) 1.38 + 1.39 +#ifdef DEBUG_FRAME_DUMP 1.40 +nsresult 1.41 +nsFirstLetterFrame::GetFrameName(nsAString& aResult) const 1.42 +{ 1.43 + return MakeFrameName(NS_LITERAL_STRING("Letter"), aResult); 1.44 +} 1.45 +#endif 1.46 + 1.47 +nsIAtom* 1.48 +nsFirstLetterFrame::GetType() const 1.49 +{ 1.50 + return nsGkAtoms::letterFrame; 1.51 +} 1.52 + 1.53 +void 1.54 +nsFirstLetterFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 1.55 + const nsRect& aDirtyRect, 1.56 + const nsDisplayListSet& aLists) 1.57 +{ 1.58 + BuildDisplayListForInline(aBuilder, aDirtyRect, aLists); 1.59 +} 1.60 + 1.61 +void 1.62 +nsFirstLetterFrame::Init(nsIContent* aContent, 1.63 + nsIFrame* aParent, 1.64 + nsIFrame* aPrevInFlow) 1.65 +{ 1.66 + nsRefPtr<nsStyleContext> newSC; 1.67 + if (aPrevInFlow) { 1.68 + // Get proper style context for ourselves. We're creating the frame 1.69 + // that represents everything *except* the first letter, so just create 1.70 + // a style context like we would for a text node. 1.71 + nsStyleContext* parentStyleContext = mStyleContext->GetParent(); 1.72 + if (parentStyleContext) { 1.73 + newSC = PresContext()->StyleSet()-> 1.74 + ResolveStyleForNonElement(parentStyleContext); 1.75 + SetStyleContextWithoutNotification(newSC); 1.76 + } 1.77 + } 1.78 + 1.79 + nsContainerFrame::Init(aContent, aParent, aPrevInFlow); 1.80 +} 1.81 + 1.82 +nsresult 1.83 +nsFirstLetterFrame::SetInitialChildList(ChildListID aListID, 1.84 + nsFrameList& aChildList) 1.85 +{ 1.86 + RestyleManager* restyleManager = PresContext()->RestyleManager(); 1.87 + 1.88 + for (nsFrameList::Enumerator e(aChildList); !e.AtEnd(); e.Next()) { 1.89 + NS_ASSERTION(e.get()->GetParent() == this, "Unexpected parent"); 1.90 + restyleManager->ReparentStyleContext(e.get()); 1.91 + nsLayoutUtils::MarkDescendantsDirty(e.get()); 1.92 + } 1.93 + 1.94 + mFrames.SetFrames(aChildList); 1.95 + return NS_OK; 1.96 +} 1.97 + 1.98 +nsresult 1.99 +nsFirstLetterFrame::GetChildFrameContainingOffset(int32_t inContentOffset, 1.100 + bool inHint, 1.101 + int32_t* outFrameContentOffset, 1.102 + nsIFrame **outChildFrame) 1.103 +{ 1.104 + nsIFrame *kid = mFrames.FirstChild(); 1.105 + if (kid) 1.106 + { 1.107 + return kid->GetChildFrameContainingOffset(inContentOffset, inHint, outFrameContentOffset, outChildFrame); 1.108 + } 1.109 + else 1.110 + return nsFrame::GetChildFrameContainingOffset(inContentOffset, inHint, outFrameContentOffset, outChildFrame); 1.111 +} 1.112 + 1.113 +// Needed for non-floating first-letter frames and for the continuations 1.114 +// following the first-letter that we also use nsFirstLetterFrame for. 1.115 +/* virtual */ void 1.116 +nsFirstLetterFrame::AddInlineMinWidth(nsRenderingContext *aRenderingContext, 1.117 + nsIFrame::InlineMinWidthData *aData) 1.118 +{ 1.119 + DoInlineIntrinsicWidth(aRenderingContext, aData, nsLayoutUtils::MIN_WIDTH); 1.120 +} 1.121 + 1.122 +// Needed for non-floating first-letter frames and for the continuations 1.123 +// following the first-letter that we also use nsFirstLetterFrame for. 1.124 +/* virtual */ void 1.125 +nsFirstLetterFrame::AddInlinePrefWidth(nsRenderingContext *aRenderingContext, 1.126 + nsIFrame::InlinePrefWidthData *aData) 1.127 +{ 1.128 + DoInlineIntrinsicWidth(aRenderingContext, aData, nsLayoutUtils::PREF_WIDTH); 1.129 +} 1.130 + 1.131 +// Needed for floating first-letter frames. 1.132 +/* virtual */ nscoord 1.133 +nsFirstLetterFrame::GetMinWidth(nsRenderingContext *aRenderingContext) 1.134 +{ 1.135 + return nsLayoutUtils::MinWidthFromInline(this, aRenderingContext); 1.136 +} 1.137 + 1.138 +// Needed for floating first-letter frames. 1.139 +/* virtual */ nscoord 1.140 +nsFirstLetterFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) 1.141 +{ 1.142 + return nsLayoutUtils::PrefWidthFromInline(this, aRenderingContext); 1.143 +} 1.144 + 1.145 +/* virtual */ nsSize 1.146 +nsFirstLetterFrame::ComputeSize(nsRenderingContext *aRenderingContext, 1.147 + nsSize aCBSize, nscoord aAvailableWidth, 1.148 + nsSize aMargin, nsSize aBorder, nsSize aPadding, 1.149 + uint32_t aFlags) 1.150 +{ 1.151 + if (GetPrevInFlow()) { 1.152 + // We're wrapping the text *after* the first letter, so behave like an 1.153 + // inline frame. 1.154 + return nsSize(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); 1.155 + } 1.156 + return nsContainerFrame::ComputeSize(aRenderingContext, 1.157 + aCBSize, aAvailableWidth, aMargin, aBorder, aPadding, aFlags); 1.158 +} 1.159 + 1.160 +nsresult 1.161 +nsFirstLetterFrame::Reflow(nsPresContext* aPresContext, 1.162 + nsHTMLReflowMetrics& aMetrics, 1.163 + const nsHTMLReflowState& aReflowState, 1.164 + nsReflowStatus& aReflowStatus) 1.165 +{ 1.166 + DO_GLOBAL_REFLOW_COUNT("nsFirstLetterFrame"); 1.167 + DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aReflowStatus); 1.168 + nsresult rv = NS_OK; 1.169 + 1.170 + // Grab overflow list 1.171 + DrainOverflowFrames(aPresContext); 1.172 + 1.173 + nsIFrame* kid = mFrames.FirstChild(); 1.174 + 1.175 + // Setup reflow state for our child 1.176 + nsSize availSize(aReflowState.AvailableWidth(), aReflowState.AvailableHeight()); 1.177 + const nsMargin& bp = aReflowState.ComputedPhysicalBorderPadding(); 1.178 + nscoord lr = bp.left + bp.right; 1.179 + nscoord tb = bp.top + bp.bottom; 1.180 + NS_ASSERTION(availSize.width != NS_UNCONSTRAINEDSIZE, 1.181 + "should no longer use unconstrained widths"); 1.182 + availSize.width -= lr; 1.183 + if (NS_UNCONSTRAINEDSIZE != availSize.height) { 1.184 + availSize.height -= tb; 1.185 + } 1.186 + 1.187 + // Reflow the child 1.188 + if (!aReflowState.mLineLayout) { 1.189 + // When there is no lineLayout provided, we provide our own. The 1.190 + // only time that the first-letter-frame is not reflowing in a 1.191 + // line context is when its floating. 1.192 + nsHTMLReflowState rs(aPresContext, aReflowState, kid, availSize); 1.193 + nsLineLayout ll(aPresContext, nullptr, &aReflowState, nullptr); 1.194 + 1.195 + ll.BeginLineReflow(bp.left, bp.top, availSize.width, NS_UNCONSTRAINEDSIZE, 1.196 + false, true, 1.197 + ll.LineContainerFrame()->GetWritingMode(kid), 1.198 + aReflowState.AvailableWidth()); 1.199 + rs.mLineLayout = ≪ 1.200 + ll.SetInFirstLetter(true); 1.201 + ll.SetFirstLetterStyleOK(true); 1.202 + 1.203 + kid->WillReflow(aPresContext); 1.204 + kid->Reflow(aPresContext, aMetrics, rs, aReflowStatus); 1.205 + 1.206 + ll.EndLineReflow(); 1.207 + ll.SetInFirstLetter(false); 1.208 + 1.209 + // In the floating first-letter case, we need to set this ourselves; 1.210 + // nsLineLayout::BeginSpan will set it in the other case 1.211 + mBaseline = aMetrics.TopAscent(); 1.212 + } 1.213 + else { 1.214 + // Pretend we are a span and reflow the child frame 1.215 + nsLineLayout* ll = aReflowState.mLineLayout; 1.216 + bool pushedFrame; 1.217 + 1.218 + ll->SetInFirstLetter( 1.219 + mStyleContext->GetPseudo() == nsCSSPseudoElements::firstLetter); 1.220 + ll->BeginSpan(this, &aReflowState, bp.left, availSize.width, &mBaseline); 1.221 + ll->ReflowFrame(kid, aReflowStatus, &aMetrics, pushedFrame); 1.222 + ll->EndSpan(this); 1.223 + ll->SetInFirstLetter(false); 1.224 + } 1.225 + 1.226 + // Place and size the child and update the output metrics 1.227 + kid->SetRect(nsRect(bp.left, bp.top, aMetrics.Width(), aMetrics.Height())); 1.228 + kid->FinishAndStoreOverflow(&aMetrics); 1.229 + kid->DidReflow(aPresContext, nullptr, nsDidReflowStatus::FINISHED); 1.230 + 1.231 + aMetrics.Width() += lr; 1.232 + aMetrics.Height() += tb; 1.233 + aMetrics.SetTopAscent(aMetrics.TopAscent() + bp.top); 1.234 + 1.235 + // Ensure that the overflow rect contains the child textframe's overflow rect. 1.236 + // Note that if this is floating, the overline/underline drawable area is in 1.237 + // the overflow rect of the child textframe. 1.238 + aMetrics.UnionOverflowAreasWithDesiredBounds(); 1.239 + ConsiderChildOverflow(aMetrics.mOverflowAreas, kid); 1.240 + 1.241 + if (!NS_INLINE_IS_BREAK_BEFORE(aReflowStatus)) { 1.242 + // Create a continuation or remove existing continuations based on 1.243 + // the reflow completion status. 1.244 + if (NS_FRAME_IS_COMPLETE(aReflowStatus)) { 1.245 + if (aReflowState.mLineLayout) { 1.246 + aReflowState.mLineLayout->SetFirstLetterStyleOK(false); 1.247 + } 1.248 + nsIFrame* kidNextInFlow = kid->GetNextInFlow(); 1.249 + if (kidNextInFlow) { 1.250 + // Remove all of the childs next-in-flows 1.251 + static_cast<nsContainerFrame*>(kidNextInFlow->GetParent()) 1.252 + ->DeleteNextInFlowChild(kidNextInFlow, true); 1.253 + } 1.254 + } 1.255 + else { 1.256 + // Create a continuation for the child frame if it doesn't already 1.257 + // have one. 1.258 + if (!IsFloating()) { 1.259 + nsIFrame* nextInFlow; 1.260 + rv = CreateNextInFlow(kid, nextInFlow); 1.261 + if (NS_FAILED(rv)) { 1.262 + return rv; 1.263 + } 1.264 + 1.265 + // And then push it to our overflow list 1.266 + const nsFrameList& overflow = mFrames.RemoveFramesAfter(kid); 1.267 + if (overflow.NotEmpty()) { 1.268 + SetOverflowFrames(overflow); 1.269 + } 1.270 + } else if (!kid->GetNextInFlow()) { 1.271 + // For floating first letter frames (if a continuation wasn't already 1.272 + // created for us) we need to put the continuation with the rest of the 1.273 + // text that the first letter frame was made out of. 1.274 + nsIFrame* continuation; 1.275 + rv = CreateContinuationForFloatingParent(aPresContext, kid, 1.276 + &continuation, true); 1.277 + } 1.278 + } 1.279 + } 1.280 + 1.281 + FinishAndStoreOverflow(&aMetrics); 1.282 + 1.283 + NS_FRAME_SET_TRUNCATION(aReflowStatus, aReflowState, aMetrics); 1.284 + return rv; 1.285 +} 1.286 + 1.287 +/* virtual */ bool 1.288 +nsFirstLetterFrame::CanContinueTextRun() const 1.289 +{ 1.290 + // We can continue a text run through a first-letter frame. 1.291 + return true; 1.292 +} 1.293 + 1.294 +nsresult 1.295 +nsFirstLetterFrame::CreateContinuationForFloatingParent(nsPresContext* aPresContext, 1.296 + nsIFrame* aChild, 1.297 + nsIFrame** aContinuation, 1.298 + bool aIsFluid) 1.299 +{ 1.300 + NS_ASSERTION(IsFloating(), 1.301 + "can only call this on floating first letter frames"); 1.302 + NS_PRECONDITION(aContinuation, "bad args"); 1.303 + 1.304 + *aContinuation = nullptr; 1.305 + nsresult rv = NS_OK; 1.306 + 1.307 + nsIPresShell* presShell = aPresContext->PresShell(); 1.308 + nsPlaceholderFrame* placeholderFrame = 1.309 + presShell->FrameManager()->GetPlaceholderFrameFor(this); 1.310 + nsIFrame* parent = placeholderFrame->GetParent(); 1.311 + 1.312 + nsIFrame* continuation = presShell->FrameConstructor()-> 1.313 + CreateContinuingFrame(aPresContext, aChild, parent, aIsFluid); 1.314 + 1.315 + // The continuation will have gotten the first letter style from its 1.316 + // prev continuation, so we need to repair the style context so it 1.317 + // doesn't have the first letter styling. 1.318 + nsStyleContext* parentSC = this->StyleContext()->GetParent(); 1.319 + if (parentSC) { 1.320 + nsRefPtr<nsStyleContext> newSC; 1.321 + newSC = presShell->StyleSet()->ResolveStyleForNonElement(parentSC); 1.322 + continuation->SetStyleContext(newSC); 1.323 + nsLayoutUtils::MarkDescendantsDirty(continuation); 1.324 + } 1.325 + 1.326 + //XXX Bidi may not be involved but we have to use the list name 1.327 + // kNoReflowPrincipalList because this is just like creating a continuation 1.328 + // except we have to insert it in a different place and we don't want a 1.329 + // reflow command to try to be issued. 1.330 + nsFrameList temp(continuation, continuation); 1.331 + rv = parent->InsertFrames(kNoReflowPrincipalList, placeholderFrame, temp); 1.332 + 1.333 + *aContinuation = continuation; 1.334 + return rv; 1.335 +} 1.336 + 1.337 +void 1.338 +nsFirstLetterFrame::DrainOverflowFrames(nsPresContext* aPresContext) 1.339 +{ 1.340 + // Check for an overflow list with our prev-in-flow 1.341 + nsFirstLetterFrame* prevInFlow = (nsFirstLetterFrame*)GetPrevInFlow(); 1.342 + if (prevInFlow) { 1.343 + AutoFrameListPtr overflowFrames(aPresContext, 1.344 + prevInFlow->StealOverflowFrames()); 1.345 + if (overflowFrames) { 1.346 + NS_ASSERTION(mFrames.IsEmpty(), "bad overflow list"); 1.347 + 1.348 + // When pushing and pulling frames we need to check for whether any 1.349 + // views need to be reparented. 1.350 + nsContainerFrame::ReparentFrameViewList(*overflowFrames, prevInFlow, 1.351 + this); 1.352 + mFrames.InsertFrames(this, nullptr, *overflowFrames); 1.353 + } 1.354 + } 1.355 + 1.356 + // It's also possible that we have an overflow list for ourselves 1.357 + AutoFrameListPtr overflowFrames(aPresContext, StealOverflowFrames()); 1.358 + if (overflowFrames) { 1.359 + NS_ASSERTION(mFrames.NotEmpty(), "overflow list w/o frames"); 1.360 + mFrames.AppendFrames(nullptr, *overflowFrames); 1.361 + } 1.362 + 1.363 + // Now repair our first frames style context (since we only reflow 1.364 + // one frame there is no point in doing any other ones until they 1.365 + // are reflowed) 1.366 + nsIFrame* kid = mFrames.FirstChild(); 1.367 + if (kid) { 1.368 + nsRefPtr<nsStyleContext> sc; 1.369 + nsIContent* kidContent = kid->GetContent(); 1.370 + if (kidContent) { 1.371 + NS_ASSERTION(kidContent->IsNodeOfType(nsINode::eTEXT), 1.372 + "should contain only text nodes"); 1.373 + nsStyleContext* parentSC = prevInFlow ? mStyleContext->GetParent() : 1.374 + mStyleContext; 1.375 + sc = aPresContext->StyleSet()->ResolveStyleForNonElement(parentSC); 1.376 + kid->SetStyleContext(sc); 1.377 + nsLayoutUtils::MarkDescendantsDirty(kid); 1.378 + } 1.379 + } 1.380 +} 1.381 + 1.382 +nscoord 1.383 +nsFirstLetterFrame::GetBaseline() const 1.384 +{ 1.385 + return mBaseline; 1.386 +} 1.387 + 1.388 +int 1.389 +nsFirstLetterFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const 1.390 +{ 1.391 + if (GetPrevContinuation()) { 1.392 + // We shouldn't get calls to GetSkipSides for later continuations since 1.393 + // they have separate style contexts with initial values for all the 1.394 + // properties that could trigger a call to GetSkipSides. Then again, 1.395 + // it's not really an error to call GetSkipSides on any frame, so 1.396 + // that's why we handle it properly. 1.397 + return LOGICAL_SIDES_ALL; 1.398 + } 1.399 + return 0; // first continuation displays all sides 1.400 +}