layout/generic/nsFirstLetterFrame.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 /* rendering object for CSS :first-letter pseudo-element */
michael@0 7
michael@0 8 #include "nsFirstLetterFrame.h"
michael@0 9 #include "nsPresContext.h"
michael@0 10 #include "nsStyleContext.h"
michael@0 11 #include "nsIContent.h"
michael@0 12 #include "nsLineLayout.h"
michael@0 13 #include "nsGkAtoms.h"
michael@0 14 #include "nsAutoPtr.h"
michael@0 15 #include "nsStyleSet.h"
michael@0 16 #include "nsFrameManager.h"
michael@0 17 #include "RestyleManager.h"
michael@0 18 #include "nsPlaceholderFrame.h"
michael@0 19 #include "nsCSSFrameConstructor.h"
michael@0 20
michael@0 21 using namespace mozilla;
michael@0 22 using namespace mozilla::layout;
michael@0 23
michael@0 24 nsIFrame*
michael@0 25 NS_NewFirstLetterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
michael@0 26 {
michael@0 27 return new (aPresShell) nsFirstLetterFrame(aContext);
michael@0 28 }
michael@0 29
michael@0 30 NS_IMPL_FRAMEARENA_HELPERS(nsFirstLetterFrame)
michael@0 31
michael@0 32 NS_QUERYFRAME_HEAD(nsFirstLetterFrame)
michael@0 33 NS_QUERYFRAME_ENTRY(nsFirstLetterFrame)
michael@0 34 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
michael@0 35
michael@0 36 #ifdef DEBUG_FRAME_DUMP
michael@0 37 nsresult
michael@0 38 nsFirstLetterFrame::GetFrameName(nsAString& aResult) const
michael@0 39 {
michael@0 40 return MakeFrameName(NS_LITERAL_STRING("Letter"), aResult);
michael@0 41 }
michael@0 42 #endif
michael@0 43
michael@0 44 nsIAtom*
michael@0 45 nsFirstLetterFrame::GetType() const
michael@0 46 {
michael@0 47 return nsGkAtoms::letterFrame;
michael@0 48 }
michael@0 49
michael@0 50 void
michael@0 51 nsFirstLetterFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
michael@0 52 const nsRect& aDirtyRect,
michael@0 53 const nsDisplayListSet& aLists)
michael@0 54 {
michael@0 55 BuildDisplayListForInline(aBuilder, aDirtyRect, aLists);
michael@0 56 }
michael@0 57
michael@0 58 void
michael@0 59 nsFirstLetterFrame::Init(nsIContent* aContent,
michael@0 60 nsIFrame* aParent,
michael@0 61 nsIFrame* aPrevInFlow)
michael@0 62 {
michael@0 63 nsRefPtr<nsStyleContext> newSC;
michael@0 64 if (aPrevInFlow) {
michael@0 65 // Get proper style context for ourselves. We're creating the frame
michael@0 66 // that represents everything *except* the first letter, so just create
michael@0 67 // a style context like we would for a text node.
michael@0 68 nsStyleContext* parentStyleContext = mStyleContext->GetParent();
michael@0 69 if (parentStyleContext) {
michael@0 70 newSC = PresContext()->StyleSet()->
michael@0 71 ResolveStyleForNonElement(parentStyleContext);
michael@0 72 SetStyleContextWithoutNotification(newSC);
michael@0 73 }
michael@0 74 }
michael@0 75
michael@0 76 nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
michael@0 77 }
michael@0 78
michael@0 79 nsresult
michael@0 80 nsFirstLetterFrame::SetInitialChildList(ChildListID aListID,
michael@0 81 nsFrameList& aChildList)
michael@0 82 {
michael@0 83 RestyleManager* restyleManager = PresContext()->RestyleManager();
michael@0 84
michael@0 85 for (nsFrameList::Enumerator e(aChildList); !e.AtEnd(); e.Next()) {
michael@0 86 NS_ASSERTION(e.get()->GetParent() == this, "Unexpected parent");
michael@0 87 restyleManager->ReparentStyleContext(e.get());
michael@0 88 nsLayoutUtils::MarkDescendantsDirty(e.get());
michael@0 89 }
michael@0 90
michael@0 91 mFrames.SetFrames(aChildList);
michael@0 92 return NS_OK;
michael@0 93 }
michael@0 94
michael@0 95 nsresult
michael@0 96 nsFirstLetterFrame::GetChildFrameContainingOffset(int32_t inContentOffset,
michael@0 97 bool inHint,
michael@0 98 int32_t* outFrameContentOffset,
michael@0 99 nsIFrame **outChildFrame)
michael@0 100 {
michael@0 101 nsIFrame *kid = mFrames.FirstChild();
michael@0 102 if (kid)
michael@0 103 {
michael@0 104 return kid->GetChildFrameContainingOffset(inContentOffset, inHint, outFrameContentOffset, outChildFrame);
michael@0 105 }
michael@0 106 else
michael@0 107 return nsFrame::GetChildFrameContainingOffset(inContentOffset, inHint, outFrameContentOffset, outChildFrame);
michael@0 108 }
michael@0 109
michael@0 110 // Needed for non-floating first-letter frames and for the continuations
michael@0 111 // following the first-letter that we also use nsFirstLetterFrame for.
michael@0 112 /* virtual */ void
michael@0 113 nsFirstLetterFrame::AddInlineMinWidth(nsRenderingContext *aRenderingContext,
michael@0 114 nsIFrame::InlineMinWidthData *aData)
michael@0 115 {
michael@0 116 DoInlineIntrinsicWidth(aRenderingContext, aData, nsLayoutUtils::MIN_WIDTH);
michael@0 117 }
michael@0 118
michael@0 119 // Needed for non-floating first-letter frames and for the continuations
michael@0 120 // following the first-letter that we also use nsFirstLetterFrame for.
michael@0 121 /* virtual */ void
michael@0 122 nsFirstLetterFrame::AddInlinePrefWidth(nsRenderingContext *aRenderingContext,
michael@0 123 nsIFrame::InlinePrefWidthData *aData)
michael@0 124 {
michael@0 125 DoInlineIntrinsicWidth(aRenderingContext, aData, nsLayoutUtils::PREF_WIDTH);
michael@0 126 }
michael@0 127
michael@0 128 // Needed for floating first-letter frames.
michael@0 129 /* virtual */ nscoord
michael@0 130 nsFirstLetterFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
michael@0 131 {
michael@0 132 return nsLayoutUtils::MinWidthFromInline(this, aRenderingContext);
michael@0 133 }
michael@0 134
michael@0 135 // Needed for floating first-letter frames.
michael@0 136 /* virtual */ nscoord
michael@0 137 nsFirstLetterFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
michael@0 138 {
michael@0 139 return nsLayoutUtils::PrefWidthFromInline(this, aRenderingContext);
michael@0 140 }
michael@0 141
michael@0 142 /* virtual */ nsSize
michael@0 143 nsFirstLetterFrame::ComputeSize(nsRenderingContext *aRenderingContext,
michael@0 144 nsSize aCBSize, nscoord aAvailableWidth,
michael@0 145 nsSize aMargin, nsSize aBorder, nsSize aPadding,
michael@0 146 uint32_t aFlags)
michael@0 147 {
michael@0 148 if (GetPrevInFlow()) {
michael@0 149 // We're wrapping the text *after* the first letter, so behave like an
michael@0 150 // inline frame.
michael@0 151 return nsSize(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
michael@0 152 }
michael@0 153 return nsContainerFrame::ComputeSize(aRenderingContext,
michael@0 154 aCBSize, aAvailableWidth, aMargin, aBorder, aPadding, aFlags);
michael@0 155 }
michael@0 156
michael@0 157 nsresult
michael@0 158 nsFirstLetterFrame::Reflow(nsPresContext* aPresContext,
michael@0 159 nsHTMLReflowMetrics& aMetrics,
michael@0 160 const nsHTMLReflowState& aReflowState,
michael@0 161 nsReflowStatus& aReflowStatus)
michael@0 162 {
michael@0 163 DO_GLOBAL_REFLOW_COUNT("nsFirstLetterFrame");
michael@0 164 DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aReflowStatus);
michael@0 165 nsresult rv = NS_OK;
michael@0 166
michael@0 167 // Grab overflow list
michael@0 168 DrainOverflowFrames(aPresContext);
michael@0 169
michael@0 170 nsIFrame* kid = mFrames.FirstChild();
michael@0 171
michael@0 172 // Setup reflow state for our child
michael@0 173 nsSize availSize(aReflowState.AvailableWidth(), aReflowState.AvailableHeight());
michael@0 174 const nsMargin& bp = aReflowState.ComputedPhysicalBorderPadding();
michael@0 175 nscoord lr = bp.left + bp.right;
michael@0 176 nscoord tb = bp.top + bp.bottom;
michael@0 177 NS_ASSERTION(availSize.width != NS_UNCONSTRAINEDSIZE,
michael@0 178 "should no longer use unconstrained widths");
michael@0 179 availSize.width -= lr;
michael@0 180 if (NS_UNCONSTRAINEDSIZE != availSize.height) {
michael@0 181 availSize.height -= tb;
michael@0 182 }
michael@0 183
michael@0 184 // Reflow the child
michael@0 185 if (!aReflowState.mLineLayout) {
michael@0 186 // When there is no lineLayout provided, we provide our own. The
michael@0 187 // only time that the first-letter-frame is not reflowing in a
michael@0 188 // line context is when its floating.
michael@0 189 nsHTMLReflowState rs(aPresContext, aReflowState, kid, availSize);
michael@0 190 nsLineLayout ll(aPresContext, nullptr, &aReflowState, nullptr);
michael@0 191
michael@0 192 ll.BeginLineReflow(bp.left, bp.top, availSize.width, NS_UNCONSTRAINEDSIZE,
michael@0 193 false, true,
michael@0 194 ll.LineContainerFrame()->GetWritingMode(kid),
michael@0 195 aReflowState.AvailableWidth());
michael@0 196 rs.mLineLayout = &ll;
michael@0 197 ll.SetInFirstLetter(true);
michael@0 198 ll.SetFirstLetterStyleOK(true);
michael@0 199
michael@0 200 kid->WillReflow(aPresContext);
michael@0 201 kid->Reflow(aPresContext, aMetrics, rs, aReflowStatus);
michael@0 202
michael@0 203 ll.EndLineReflow();
michael@0 204 ll.SetInFirstLetter(false);
michael@0 205
michael@0 206 // In the floating first-letter case, we need to set this ourselves;
michael@0 207 // nsLineLayout::BeginSpan will set it in the other case
michael@0 208 mBaseline = aMetrics.TopAscent();
michael@0 209 }
michael@0 210 else {
michael@0 211 // Pretend we are a span and reflow the child frame
michael@0 212 nsLineLayout* ll = aReflowState.mLineLayout;
michael@0 213 bool pushedFrame;
michael@0 214
michael@0 215 ll->SetInFirstLetter(
michael@0 216 mStyleContext->GetPseudo() == nsCSSPseudoElements::firstLetter);
michael@0 217 ll->BeginSpan(this, &aReflowState, bp.left, availSize.width, &mBaseline);
michael@0 218 ll->ReflowFrame(kid, aReflowStatus, &aMetrics, pushedFrame);
michael@0 219 ll->EndSpan(this);
michael@0 220 ll->SetInFirstLetter(false);
michael@0 221 }
michael@0 222
michael@0 223 // Place and size the child and update the output metrics
michael@0 224 kid->SetRect(nsRect(bp.left, bp.top, aMetrics.Width(), aMetrics.Height()));
michael@0 225 kid->FinishAndStoreOverflow(&aMetrics);
michael@0 226 kid->DidReflow(aPresContext, nullptr, nsDidReflowStatus::FINISHED);
michael@0 227
michael@0 228 aMetrics.Width() += lr;
michael@0 229 aMetrics.Height() += tb;
michael@0 230 aMetrics.SetTopAscent(aMetrics.TopAscent() + bp.top);
michael@0 231
michael@0 232 // Ensure that the overflow rect contains the child textframe's overflow rect.
michael@0 233 // Note that if this is floating, the overline/underline drawable area is in
michael@0 234 // the overflow rect of the child textframe.
michael@0 235 aMetrics.UnionOverflowAreasWithDesiredBounds();
michael@0 236 ConsiderChildOverflow(aMetrics.mOverflowAreas, kid);
michael@0 237
michael@0 238 if (!NS_INLINE_IS_BREAK_BEFORE(aReflowStatus)) {
michael@0 239 // Create a continuation or remove existing continuations based on
michael@0 240 // the reflow completion status.
michael@0 241 if (NS_FRAME_IS_COMPLETE(aReflowStatus)) {
michael@0 242 if (aReflowState.mLineLayout) {
michael@0 243 aReflowState.mLineLayout->SetFirstLetterStyleOK(false);
michael@0 244 }
michael@0 245 nsIFrame* kidNextInFlow = kid->GetNextInFlow();
michael@0 246 if (kidNextInFlow) {
michael@0 247 // Remove all of the childs next-in-flows
michael@0 248 static_cast<nsContainerFrame*>(kidNextInFlow->GetParent())
michael@0 249 ->DeleteNextInFlowChild(kidNextInFlow, true);
michael@0 250 }
michael@0 251 }
michael@0 252 else {
michael@0 253 // Create a continuation for the child frame if it doesn't already
michael@0 254 // have one.
michael@0 255 if (!IsFloating()) {
michael@0 256 nsIFrame* nextInFlow;
michael@0 257 rv = CreateNextInFlow(kid, nextInFlow);
michael@0 258 if (NS_FAILED(rv)) {
michael@0 259 return rv;
michael@0 260 }
michael@0 261
michael@0 262 // And then push it to our overflow list
michael@0 263 const nsFrameList& overflow = mFrames.RemoveFramesAfter(kid);
michael@0 264 if (overflow.NotEmpty()) {
michael@0 265 SetOverflowFrames(overflow);
michael@0 266 }
michael@0 267 } else if (!kid->GetNextInFlow()) {
michael@0 268 // For floating first letter frames (if a continuation wasn't already
michael@0 269 // created for us) we need to put the continuation with the rest of the
michael@0 270 // text that the first letter frame was made out of.
michael@0 271 nsIFrame* continuation;
michael@0 272 rv = CreateContinuationForFloatingParent(aPresContext, kid,
michael@0 273 &continuation, true);
michael@0 274 }
michael@0 275 }
michael@0 276 }
michael@0 277
michael@0 278 FinishAndStoreOverflow(&aMetrics);
michael@0 279
michael@0 280 NS_FRAME_SET_TRUNCATION(aReflowStatus, aReflowState, aMetrics);
michael@0 281 return rv;
michael@0 282 }
michael@0 283
michael@0 284 /* virtual */ bool
michael@0 285 nsFirstLetterFrame::CanContinueTextRun() const
michael@0 286 {
michael@0 287 // We can continue a text run through a first-letter frame.
michael@0 288 return true;
michael@0 289 }
michael@0 290
michael@0 291 nsresult
michael@0 292 nsFirstLetterFrame::CreateContinuationForFloatingParent(nsPresContext* aPresContext,
michael@0 293 nsIFrame* aChild,
michael@0 294 nsIFrame** aContinuation,
michael@0 295 bool aIsFluid)
michael@0 296 {
michael@0 297 NS_ASSERTION(IsFloating(),
michael@0 298 "can only call this on floating first letter frames");
michael@0 299 NS_PRECONDITION(aContinuation, "bad args");
michael@0 300
michael@0 301 *aContinuation = nullptr;
michael@0 302 nsresult rv = NS_OK;
michael@0 303
michael@0 304 nsIPresShell* presShell = aPresContext->PresShell();
michael@0 305 nsPlaceholderFrame* placeholderFrame =
michael@0 306 presShell->FrameManager()->GetPlaceholderFrameFor(this);
michael@0 307 nsIFrame* parent = placeholderFrame->GetParent();
michael@0 308
michael@0 309 nsIFrame* continuation = presShell->FrameConstructor()->
michael@0 310 CreateContinuingFrame(aPresContext, aChild, parent, aIsFluid);
michael@0 311
michael@0 312 // The continuation will have gotten the first letter style from its
michael@0 313 // prev continuation, so we need to repair the style context so it
michael@0 314 // doesn't have the first letter styling.
michael@0 315 nsStyleContext* parentSC = this->StyleContext()->GetParent();
michael@0 316 if (parentSC) {
michael@0 317 nsRefPtr<nsStyleContext> newSC;
michael@0 318 newSC = presShell->StyleSet()->ResolveStyleForNonElement(parentSC);
michael@0 319 continuation->SetStyleContext(newSC);
michael@0 320 nsLayoutUtils::MarkDescendantsDirty(continuation);
michael@0 321 }
michael@0 322
michael@0 323 //XXX Bidi may not be involved but we have to use the list name
michael@0 324 // kNoReflowPrincipalList because this is just like creating a continuation
michael@0 325 // except we have to insert it in a different place and we don't want a
michael@0 326 // reflow command to try to be issued.
michael@0 327 nsFrameList temp(continuation, continuation);
michael@0 328 rv = parent->InsertFrames(kNoReflowPrincipalList, placeholderFrame, temp);
michael@0 329
michael@0 330 *aContinuation = continuation;
michael@0 331 return rv;
michael@0 332 }
michael@0 333
michael@0 334 void
michael@0 335 nsFirstLetterFrame::DrainOverflowFrames(nsPresContext* aPresContext)
michael@0 336 {
michael@0 337 // Check for an overflow list with our prev-in-flow
michael@0 338 nsFirstLetterFrame* prevInFlow = (nsFirstLetterFrame*)GetPrevInFlow();
michael@0 339 if (prevInFlow) {
michael@0 340 AutoFrameListPtr overflowFrames(aPresContext,
michael@0 341 prevInFlow->StealOverflowFrames());
michael@0 342 if (overflowFrames) {
michael@0 343 NS_ASSERTION(mFrames.IsEmpty(), "bad overflow list");
michael@0 344
michael@0 345 // When pushing and pulling frames we need to check for whether any
michael@0 346 // views need to be reparented.
michael@0 347 nsContainerFrame::ReparentFrameViewList(*overflowFrames, prevInFlow,
michael@0 348 this);
michael@0 349 mFrames.InsertFrames(this, nullptr, *overflowFrames);
michael@0 350 }
michael@0 351 }
michael@0 352
michael@0 353 // It's also possible that we have an overflow list for ourselves
michael@0 354 AutoFrameListPtr overflowFrames(aPresContext, StealOverflowFrames());
michael@0 355 if (overflowFrames) {
michael@0 356 NS_ASSERTION(mFrames.NotEmpty(), "overflow list w/o frames");
michael@0 357 mFrames.AppendFrames(nullptr, *overflowFrames);
michael@0 358 }
michael@0 359
michael@0 360 // Now repair our first frames style context (since we only reflow
michael@0 361 // one frame there is no point in doing any other ones until they
michael@0 362 // are reflowed)
michael@0 363 nsIFrame* kid = mFrames.FirstChild();
michael@0 364 if (kid) {
michael@0 365 nsRefPtr<nsStyleContext> sc;
michael@0 366 nsIContent* kidContent = kid->GetContent();
michael@0 367 if (kidContent) {
michael@0 368 NS_ASSERTION(kidContent->IsNodeOfType(nsINode::eTEXT),
michael@0 369 "should contain only text nodes");
michael@0 370 nsStyleContext* parentSC = prevInFlow ? mStyleContext->GetParent() :
michael@0 371 mStyleContext;
michael@0 372 sc = aPresContext->StyleSet()->ResolveStyleForNonElement(parentSC);
michael@0 373 kid->SetStyleContext(sc);
michael@0 374 nsLayoutUtils::MarkDescendantsDirty(kid);
michael@0 375 }
michael@0 376 }
michael@0 377 }
michael@0 378
michael@0 379 nscoord
michael@0 380 nsFirstLetterFrame::GetBaseline() const
michael@0 381 {
michael@0 382 return mBaseline;
michael@0 383 }
michael@0 384
michael@0 385 int
michael@0 386 nsFirstLetterFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const
michael@0 387 {
michael@0 388 if (GetPrevContinuation()) {
michael@0 389 // We shouldn't get calls to GetSkipSides for later continuations since
michael@0 390 // they have separate style contexts with initial values for all the
michael@0 391 // properties that could trigger a call to GetSkipSides. Then again,
michael@0 392 // it's not really an error to call GetSkipSides on any frame, so
michael@0 393 // that's why we handle it properly.
michael@0 394 return LOGICAL_SIDES_ALL;
michael@0 395 }
michael@0 396 return 0; // first continuation displays all sides
michael@0 397 }

mercurial