Line data Source code
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/. */
5 :
6 : /* rendering object for CSS display:inline objects */
7 :
8 : #include "gfxContext.h"
9 : #include "nsInlineFrame.h"
10 : #include "nsLineLayout.h"
11 : #include "nsBlockFrame.h"
12 : #include "nsPlaceholderFrame.h"
13 : #include "nsGkAtoms.h"
14 : #include "nsStyleContext.h"
15 : #include "nsPresContext.h"
16 : #include "nsCSSAnonBoxes.h"
17 : #include "mozilla/RestyleManager.h"
18 : #include "mozilla/RestyleManagerInlines.h"
19 : #include "nsDisplayList.h"
20 : #include "mozilla/Likely.h"
21 : #include "SVGTextFrame.h"
22 : #include "nsStyleChangeList.h"
23 : #include "mozilla/StyleSetHandle.h"
24 : #include "mozilla/StyleSetHandleInlines.h"
25 : #include "mozilla/ServoStyleSet.h"
26 :
27 : #ifdef DEBUG
28 : #undef NOISY_PUSHING
29 : #endif
30 :
31 : using namespace mozilla;
32 : using namespace mozilla::layout;
33 :
34 :
35 : //////////////////////////////////////////////////////////////////////
36 :
37 : // Basic nsInlineFrame methods
38 :
39 : nsInlineFrame*
40 0 : NS_NewInlineFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
41 : {
42 0 : return new (aPresShell) nsInlineFrame(aContext);
43 : }
44 :
45 0 : NS_IMPL_FRAMEARENA_HELPERS(nsInlineFrame)
46 :
47 0 : NS_QUERYFRAME_HEAD(nsInlineFrame)
48 0 : NS_QUERYFRAME_ENTRY(nsInlineFrame)
49 0 : NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
50 :
51 : #ifdef DEBUG_FRAME_DUMP
52 : nsresult
53 0 : nsInlineFrame::GetFrameName(nsAString& aResult) const
54 : {
55 0 : return MakeFrameName(NS_LITERAL_STRING("Inline"), aResult);
56 : }
57 : #endif
58 :
59 : void
60 0 : nsInlineFrame::InvalidateFrame(uint32_t aDisplayItemKey)
61 : {
62 0 : if (nsSVGUtils::IsInSVGTextSubtree(this)) {
63 : nsIFrame* svgTextFrame = nsLayoutUtils::GetClosestFrameOfType(
64 0 : GetParent(), LayoutFrameType::SVGText);
65 0 : svgTextFrame->InvalidateFrame();
66 0 : return;
67 : }
68 0 : nsContainerFrame::InvalidateFrame(aDisplayItemKey);
69 : }
70 :
71 : void
72 0 : nsInlineFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey)
73 : {
74 0 : if (nsSVGUtils::IsInSVGTextSubtree(this)) {
75 : nsIFrame* svgTextFrame = nsLayoutUtils::GetClosestFrameOfType(
76 0 : GetParent(), LayoutFrameType::SVGText);
77 0 : svgTextFrame->InvalidateFrame();
78 0 : return;
79 : }
80 0 : nsContainerFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey);
81 : }
82 :
83 : static inline bool
84 0 : IsMarginZero(const nsStyleCoord &aCoord)
85 : {
86 0 : return aCoord.GetUnit() == eStyleUnit_Auto ||
87 0 : nsLayoutUtils::IsMarginZero(aCoord);
88 : }
89 :
90 : /* virtual */ bool
91 0 : nsInlineFrame::IsSelfEmpty()
92 : {
93 : #if 0
94 : // I used to think inline frames worked this way, but it seems they
95 : // don't. At least not in our codebase.
96 : if (GetPresContext()->CompatibilityMode() == eCompatibility_FullStandards) {
97 : return false;
98 : }
99 : #endif
100 0 : const nsStyleMargin* margin = StyleMargin();
101 0 : const nsStyleBorder* border = StyleBorder();
102 0 : const nsStylePadding* padding = StylePadding();
103 : // Block-start and -end ignored, since they shouldn't affect things, but this
104 : // doesn't really match with nsLineLayout.cpp's setting of
105 : // ZeroEffectiveSpanBox, anymore, so what should this really be?
106 0 : WritingMode wm = GetWritingMode();
107 : bool haveStart, haveEnd;
108 : // Initially set up haveStart and haveEnd in terms of visual (LTR/TTB)
109 : // coordinates; we'll exchange them later if bidi-RTL is in effect to
110 : // get logical start and end flags.
111 0 : if (wm.IsVertical()) {
112 0 : haveStart =
113 0 : border->GetComputedBorderWidth(eSideTop) != 0 ||
114 0 : !nsLayoutUtils::IsPaddingZero(padding->mPadding.GetTop()) ||
115 0 : !IsMarginZero(margin->mMargin.GetTop());
116 0 : haveEnd =
117 0 : border->GetComputedBorderWidth(eSideBottom) != 0 ||
118 0 : !nsLayoutUtils::IsPaddingZero(padding->mPadding.GetBottom()) ||
119 0 : !IsMarginZero(margin->mMargin.GetBottom());
120 : } else {
121 0 : haveStart =
122 0 : border->GetComputedBorderWidth(eSideLeft) != 0 ||
123 0 : !nsLayoutUtils::IsPaddingZero(padding->mPadding.GetLeft()) ||
124 0 : !IsMarginZero(margin->mMargin.GetLeft());
125 0 : haveEnd =
126 0 : border->GetComputedBorderWidth(eSideRight) != 0 ||
127 0 : !nsLayoutUtils::IsPaddingZero(padding->mPadding.GetRight()) ||
128 0 : !IsMarginZero(margin->mMargin.GetRight());
129 : }
130 0 : if (haveStart || haveEnd) {
131 : // We skip this block and return false for box-decoration-break:clone since
132 : // in that case all the continuations will have the border/padding/margin.
133 0 : if ((GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) &&
134 0 : StyleBorder()->mBoxDecorationBreak == StyleBoxDecorationBreak::Slice) {
135 : // When direction=rtl, we need to consider logical rather than visual
136 : // start and end, so swap the flags.
137 0 : if (!wm.IsBidiLTR()) {
138 0 : Swap(haveStart, haveEnd);
139 : }
140 : // For ib-split frames, ignore things we know we'll skip in GetSkipSides.
141 : // XXXbz should we be doing this for non-ib-split frames too, in a more
142 : // general way?
143 :
144 : // Get the first continuation eagerly, as a performance optimization, to
145 : // avoid having to get it twice..
146 0 : nsIFrame* firstCont = FirstContinuation();
147 : return
148 0 : (!haveStart || firstCont->FrameIsNonFirstInIBSplit()) &&
149 0 : (!haveEnd || firstCont->FrameIsNonLastInIBSplit());
150 : }
151 0 : return false;
152 : }
153 0 : return true;
154 : }
155 :
156 : bool
157 0 : nsInlineFrame::IsEmpty()
158 : {
159 0 : if (!IsSelfEmpty()) {
160 0 : return false;
161 : }
162 :
163 0 : for (nsIFrame* kid : mFrames) {
164 0 : if (!kid->IsEmpty())
165 0 : return false;
166 : }
167 :
168 0 : return true;
169 : }
170 :
171 : nsIFrame::FrameSearchResult
172 0 : nsInlineFrame::PeekOffsetCharacter(bool aForward, int32_t* aOffset,
173 : PeekOffsetCharacterOptions aOptions)
174 : {
175 : // Override the implementation in nsFrame, to skip empty inline frames
176 0 : NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range");
177 0 : int32_t startOffset = *aOffset;
178 0 : if (startOffset < 0)
179 0 : startOffset = 1;
180 0 : if (aForward == (startOffset == 0)) {
181 : // We're before the frame and moving forward, or after it and moving backwards:
182 : // skip to the other side, but keep going.
183 0 : *aOffset = 1 - startOffset;
184 : }
185 0 : return CONTINUE;
186 : }
187 :
188 : void
189 0 : nsInlineFrame::DestroyFrom(nsIFrame* aDestructRoot)
190 : {
191 0 : nsFrameList* overflowFrames = GetOverflowFrames();
192 0 : if (overflowFrames) {
193 : // Fixup the parent pointers for any child frames on the OverflowList.
194 : // nsIFrame::DestroyFrom depends on that to find the sticky scroll
195 : // container (an ancestor).
196 0 : nsIFrame* lineContainer = nsLayoutUtils::FindNearestBlockAncestor(this);
197 0 : DrainSelfOverflowListInternal(eForDestroy, lineContainer);
198 : }
199 0 : nsContainerFrame::DestroyFrom(aDestructRoot);
200 0 : }
201 :
202 : nsresult
203 0 : nsInlineFrame::StealFrame(nsIFrame* aChild)
204 : {
205 0 : if (MaybeStealOverflowContainerFrame(aChild)) {
206 0 : return NS_OK;
207 : }
208 :
209 0 : nsInlineFrame* parent = this;
210 0 : bool removed = false;
211 0 : do {
212 0 : removed = parent->mFrames.StartRemoveFrame(aChild);
213 0 : if (removed) {
214 0 : break;
215 : }
216 :
217 : // We didn't find the child in our principal child list.
218 : // Maybe it's on the overflow list?
219 0 : nsFrameList* frameList = parent->GetOverflowFrames();
220 0 : if (frameList) {
221 0 : removed = frameList->ContinueRemoveFrame(aChild);
222 0 : if (frameList->IsEmpty()) {
223 0 : parent->DestroyOverflowList();
224 : }
225 0 : if (removed) {
226 0 : break;
227 : }
228 : }
229 :
230 : // Due to our "lazy reparenting" optimization 'aChild' might not actually
231 : // be on any of our child lists, but instead in one of our next-in-flows.
232 0 : parent = static_cast<nsInlineFrame*>(parent->GetNextInFlow());
233 0 : } while (parent);
234 :
235 0 : MOZ_ASSERT(removed, "nsInlineFrame::StealFrame: can't find aChild");
236 0 : return removed ? NS_OK : NS_ERROR_UNEXPECTED;
237 : }
238 :
239 : void
240 0 : nsInlineFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
241 : const nsRect& aDirtyRect,
242 : const nsDisplayListSet& aLists)
243 : {
244 0 : BuildDisplayListForInline(aBuilder, aDirtyRect, aLists);
245 :
246 : // The sole purpose of this is to trigger display of the selection
247 : // window for Named Anchors, which don't have any children and
248 : // normally don't have any size, but in Editor we use CSS to display
249 : // an image to represent this "hidden" element.
250 0 : if (!mFrames.FirstChild()) {
251 0 : DisplaySelectionOverlay(aBuilder, aLists.Content());
252 : }
253 0 : }
254 :
255 : //////////////////////////////////////////////////////////////////////
256 : // Reflow methods
257 :
258 : /* virtual */ void
259 0 : nsInlineFrame::AddInlineMinISize(gfxContext *aRenderingContext,
260 : nsIFrame::InlineMinISizeData *aData)
261 : {
262 0 : DoInlineIntrinsicISize(aRenderingContext, aData, nsLayoutUtils::MIN_ISIZE);
263 0 : }
264 :
265 : /* virtual */ void
266 0 : nsInlineFrame::AddInlinePrefISize(gfxContext *aRenderingContext,
267 : nsIFrame::InlinePrefISizeData *aData)
268 : {
269 0 : DoInlineIntrinsicISize(aRenderingContext, aData, nsLayoutUtils::PREF_ISIZE);
270 0 : aData->mLineIsEmpty = false;
271 0 : }
272 :
273 : /* virtual */
274 : LogicalSize
275 0 : nsInlineFrame::ComputeSize(gfxContext *aRenderingContext,
276 : WritingMode aWM,
277 : const LogicalSize& aCBSize,
278 : nscoord aAvailableISize,
279 : const LogicalSize& aMargin,
280 : const LogicalSize& aBorder,
281 : const LogicalSize& aPadding,
282 : ComputeSizeFlags aFlags)
283 : {
284 : // Inlines and text don't compute size before reflow.
285 0 : return LogicalSize(aWM, NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
286 : }
287 :
288 : nsRect
289 0 : nsInlineFrame::ComputeTightBounds(DrawTarget* aDrawTarget) const
290 : {
291 : // be conservative
292 0 : if (StyleContext()->HasTextDecorationLines()) {
293 0 : return GetVisualOverflowRect();
294 : }
295 0 : return ComputeSimpleTightBounds(aDrawTarget);
296 : }
297 :
298 : void
299 0 : nsInlineFrame::ReparentFloatsForInlineChild(nsIFrame* aOurLineContainer,
300 : nsIFrame* aFrame,
301 : bool aReparentSiblings)
302 : {
303 : // XXXbz this would be better if it took a nsFrameList or a frame
304 : // list slice....
305 0 : NS_ASSERTION(aOurLineContainer->GetNextContinuation() ||
306 : aOurLineContainer->GetPrevContinuation(),
307 : "Don't call this when we have no continuation, it's a waste");
308 0 : if (!aFrame) {
309 0 : NS_ASSERTION(aReparentSiblings, "Why did we get called?");
310 0 : return;
311 : }
312 :
313 0 : nsBlockFrame* frameBlock = nsLayoutUtils::GetFloatContainingBlock(aFrame);
314 0 : if (!frameBlock || frameBlock == aOurLineContainer) {
315 0 : return;
316 : }
317 :
318 0 : nsBlockFrame* ourBlock = nsLayoutUtils::GetAsBlock(aOurLineContainer);
319 0 : NS_ASSERTION(ourBlock, "Not a block, but broke vertically?");
320 :
321 : while (true) {
322 0 : ourBlock->ReparentFloats(aFrame, frameBlock, false);
323 :
324 0 : if (!aReparentSiblings)
325 0 : return;
326 0 : nsIFrame* next = aFrame->GetNextSibling();
327 0 : if (!next)
328 0 : return;
329 0 : if (next->GetParent() == aFrame->GetParent()) {
330 0 : aFrame = next;
331 0 : continue;
332 : }
333 : // This is paranoid and will hardly ever get hit ... but we can't actually
334 : // trust that the frames in the sibling chain all have the same parent,
335 : // because lazy reparenting may be going on. If we find a different
336 : // parent we need to redo our analysis.
337 0 : ReparentFloatsForInlineChild(aOurLineContainer, next, aReparentSiblings);
338 0 : return;
339 0 : }
340 : }
341 :
342 : static void
343 0 : ReparentChildListStyle(nsPresContext* aPresContext,
344 : const nsFrameList::Slice& aFrames,
345 : nsIFrame* aParentFrame)
346 : {
347 0 : RestyleManager* restyleManager = aPresContext->RestyleManager();
348 :
349 0 : for (nsFrameList::Enumerator e(aFrames); !e.AtEnd(); e.Next()) {
350 0 : NS_ASSERTION(e.get()->GetParent() == aParentFrame, "Bogus parentage");
351 0 : restyleManager->ReparentStyleContext(e.get());
352 0 : nsLayoutUtils::MarkDescendantsDirty(e.get());
353 : }
354 0 : }
355 :
356 : void
357 0 : nsInlineFrame::Reflow(nsPresContext* aPresContext,
358 : ReflowOutput& aMetrics,
359 : const ReflowInput& aReflowInput,
360 : nsReflowStatus& aStatus)
361 : {
362 0 : MarkInReflow();
363 0 : DO_GLOBAL_REFLOW_COUNT("nsInlineFrame");
364 0 : DISPLAY_REFLOW(aPresContext, this, aReflowInput, aMetrics, aStatus);
365 0 : if (nullptr == aReflowInput.mLineLayout) {
366 0 : NS_ERROR("must have non-null aReflowInput.mLineLayout");
367 0 : return;
368 : }
369 0 : if (IsFrameTreeTooDeep(aReflowInput, aMetrics, aStatus)) {
370 0 : return;
371 : }
372 :
373 0 : bool lazilySetParentPointer = false;
374 :
375 0 : nsIFrame* lineContainer = aReflowInput.mLineLayout->LineContainerFrame();
376 :
377 : // Check for an overflow list with our prev-in-flow
378 0 : nsInlineFrame* prevInFlow = (nsInlineFrame*)GetPrevInFlow();
379 0 : if (prevInFlow) {
380 : AutoFrameListPtr prevOverflowFrames(aPresContext,
381 0 : prevInFlow->StealOverflowFrames());
382 0 : if (prevOverflowFrames) {
383 : // When pushing and pulling frames we need to check for whether any
384 : // views need to be reparented.
385 0 : nsContainerFrame::ReparentFrameViewList(*prevOverflowFrames, prevInFlow,
386 0 : this);
387 :
388 : // Check if we should do the lazilySetParentPointer optimization.
389 : // Only do it in simple cases where we're being reflowed for the
390 : // first time, nothing (e.g. bidi resolution) has already given
391 : // us children, and there's no next-in-flow, so all our frames
392 : // will be taken from prevOverflowFrames.
393 0 : if ((GetStateBits() & NS_FRAME_FIRST_REFLOW) && mFrames.IsEmpty() &&
394 0 : !GetNextInFlow()) {
395 : // If our child list is empty, just put the new frames into it.
396 : // Note that we don't set the parent pointer for the new frames. Instead wait
397 : // to do this until we actually reflow the frame. If the overflow list contains
398 : // thousands of frames this is a big performance issue (see bug #5588)
399 0 : mFrames.SetFrames(*prevOverflowFrames);
400 0 : lazilySetParentPointer = true;
401 : } else {
402 : // Assign all floats to our block if necessary
403 0 : if (lineContainer && lineContainer->GetPrevContinuation()) {
404 0 : ReparentFloatsForInlineChild(lineContainer,
405 : prevOverflowFrames->FirstChild(),
406 0 : true);
407 : }
408 : // Insert the new frames at the beginning of the child list
409 : // and set their parent pointer
410 : const nsFrameList::Slice& newFrames =
411 0 : mFrames.InsertFrames(this, nullptr, *prevOverflowFrames);
412 : // If our prev in flow was under the first continuation of a first-line
413 : // frame then we need to reparent the style contexts to remove the
414 : // the special first-line styling. In the lazilySetParentPointer case
415 : // we reparent the style contexts when we set their parents in
416 : // nsInlineFrame::ReflowFrames and nsInlineFrame::ReflowInlineFrame.
417 0 : if (aReflowInput.mLineLayout->GetInFirstLine()) {
418 0 : ReparentChildListStyle(aPresContext, newFrames, this);
419 : }
420 : }
421 : }
422 : }
423 :
424 : // It's also possible that we have an overflow list for ourselves
425 : #ifdef DEBUG
426 0 : if (GetStateBits() & NS_FRAME_FIRST_REFLOW) {
427 : // If it's our initial reflow, then we should not have an overflow list.
428 : // However, add an assertion in case we get reflowed more than once with
429 : // the initial reflow reason
430 0 : nsFrameList* overflowFrames = GetOverflowFrames();
431 0 : NS_ASSERTION(!overflowFrames || overflowFrames->IsEmpty(),
432 : "overflow list is not empty for initial reflow");
433 : }
434 : #endif
435 0 : if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
436 : DrainFlags flags =
437 0 : lazilySetParentPointer ? eDontReparentFrames : DrainFlags(0);
438 0 : if (aReflowInput.mLineLayout->GetInFirstLine()) {
439 0 : flags = DrainFlags(flags | eInFirstLine);
440 : }
441 0 : DrainSelfOverflowListInternal(flags, lineContainer);
442 : }
443 :
444 : // Set our own reflow state (additional state above and beyond
445 : // aReflowInput)
446 0 : InlineReflowInput irs;
447 0 : irs.mPrevFrame = nullptr;
448 0 : irs.mLineContainer = lineContainer;
449 0 : irs.mLineLayout = aReflowInput.mLineLayout;
450 0 : irs.mNextInFlow = (nsInlineFrame*) GetNextInFlow();
451 0 : irs.mSetParentPointer = lazilySetParentPointer;
452 :
453 0 : if (mFrames.IsEmpty()) {
454 : // Try to pull over one frame before starting so that we know
455 : // whether we have an anonymous block or not.
456 : bool complete;
457 0 : (void) PullOneFrame(aPresContext, irs, &complete);
458 : }
459 :
460 0 : ReflowFrames(aPresContext, aReflowInput, irs, aMetrics, aStatus);
461 :
462 0 : ReflowAbsoluteFrames(aPresContext, aMetrics, aReflowInput, aStatus);
463 :
464 : // Note: the line layout code will properly compute our
465 : // overflow-rect state for us.
466 :
467 0 : NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aMetrics);
468 : }
469 :
470 : nsresult
471 0 : nsInlineFrame::AttributeChanged(int32_t aNameSpaceID,
472 : nsIAtom* aAttribute,
473 : int32_t aModType)
474 : {
475 : nsresult rv =
476 0 : nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
477 :
478 0 : if (NS_FAILED(rv)) {
479 0 : return rv;
480 : }
481 :
482 0 : if (nsSVGUtils::IsInSVGTextSubtree(this)) {
483 : SVGTextFrame* f = static_cast<SVGTextFrame*>(
484 0 : nsLayoutUtils::GetClosestFrameOfType(this, LayoutFrameType::SVGText));
485 0 : f->HandleAttributeChangeInDescendant(mContent->AsElement(),
486 0 : aNameSpaceID, aAttribute);
487 : }
488 :
489 0 : return NS_OK;
490 : }
491 :
492 : bool
493 0 : nsInlineFrame::DrainSelfOverflowListInternal(DrainFlags aFlags,
494 : nsIFrame* aLineContainer)
495 : {
496 0 : AutoFrameListPtr overflowFrames(PresContext(), StealOverflowFrames());
497 0 : if (overflowFrames) {
498 : // The frames on our own overflowlist may have been pushed by a
499 : // previous lazilySetParentPointer Reflow so we need to ensure the
500 : // correct parent pointer. This is sometimes skipped by Reflow.
501 0 : if (!(aFlags & eDontReparentFrames)) {
502 0 : nsIFrame* firstChild = overflowFrames->FirstChild();
503 0 : if (aLineContainer && aLineContainer->GetPrevContinuation()) {
504 0 : ReparentFloatsForInlineChild(aLineContainer, firstChild, true);
505 : }
506 : const bool doReparentSC =
507 0 : (aFlags & eInFirstLine) && !(aFlags & eForDestroy);
508 0 : RestyleManager* restyleManager = PresContext()->RestyleManager();
509 0 : for (nsIFrame* f = firstChild; f; f = f->GetNextSibling()) {
510 0 : f->SetParent(this);
511 0 : if (doReparentSC) {
512 0 : restyleManager->ReparentStyleContext(f);
513 0 : nsLayoutUtils::MarkDescendantsDirty(f);
514 : }
515 : }
516 : }
517 0 : bool result = !overflowFrames->IsEmpty();
518 0 : mFrames.AppendFrames(nullptr, *overflowFrames);
519 0 : return result;
520 : }
521 0 : return false;
522 : }
523 :
524 : /* virtual */ bool
525 0 : nsInlineFrame::DrainSelfOverflowList()
526 : {
527 0 : nsIFrame* lineContainer = nsLayoutUtils::FindNearestBlockAncestor(this);
528 : // Add the eInFirstLine flag if we have a ::first-line ancestor frame.
529 : // No need to look further than the nearest line container though.
530 0 : DrainFlags flags = DrainFlags(0);
531 0 : for (nsIFrame* p = GetParent(); p != lineContainer; p = p->GetParent()) {
532 0 : if (p->IsLineFrame()) {
533 0 : flags = DrainFlags(flags | eInFirstLine);
534 0 : break;
535 : }
536 : }
537 0 : return DrainSelfOverflowListInternal(flags, lineContainer);
538 : }
539 :
540 : /* virtual */ bool
541 0 : nsInlineFrame::CanContinueTextRun() const
542 : {
543 : // We can continue a text run through an inline frame
544 0 : return true;
545 : }
546 :
547 : /* virtual */ void
548 0 : nsInlineFrame::PullOverflowsFromPrevInFlow()
549 : {
550 0 : nsInlineFrame* prevInFlow = static_cast<nsInlineFrame*>(GetPrevInFlow());
551 0 : if (prevInFlow) {
552 0 : nsPresContext* presContext = PresContext();
553 : AutoFrameListPtr prevOverflowFrames(presContext,
554 0 : prevInFlow->StealOverflowFrames());
555 0 : if (prevOverflowFrames) {
556 : // Assume that our prev-in-flow has the same line container that we do.
557 0 : nsContainerFrame::ReparentFrameViewList(*prevOverflowFrames, prevInFlow,
558 0 : this);
559 0 : mFrames.InsertFrames(this, nullptr, *prevOverflowFrames);
560 : }
561 : }
562 0 : }
563 :
564 : void
565 0 : nsInlineFrame::ReflowFrames(nsPresContext* aPresContext,
566 : const ReflowInput& aReflowInput,
567 : InlineReflowInput& irs,
568 : ReflowOutput& aMetrics,
569 : nsReflowStatus& aStatus)
570 : {
571 0 : aStatus.Reset();
572 :
573 0 : nsLineLayout* lineLayout = aReflowInput.mLineLayout;
574 0 : bool inFirstLine = aReflowInput.mLineLayout->GetInFirstLine();
575 0 : RestyleManager* restyleManager = aPresContext->RestyleManager();
576 0 : WritingMode frameWM = aReflowInput.GetWritingMode();
577 0 : WritingMode lineWM = aReflowInput.mLineLayout->mRootSpan->mWritingMode;
578 0 : LogicalMargin framePadding = aReflowInput.ComputedLogicalBorderPadding();
579 0 : nscoord startEdge = 0;
580 : const bool boxDecorationBreakClone =
581 0 : MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
582 : StyleBoxDecorationBreak::Clone);
583 : // Don't offset by our start borderpadding if we have a prev continuation or
584 : // if we're in a part of an {ib} split other than the first one. For
585 : // box-decoration-break:clone we always offset our start since all
586 : // continuations have border/padding.
587 0 : if ((!GetPrevContinuation() && !FrameIsNonFirstInIBSplit()) ||
588 : boxDecorationBreakClone) {
589 0 : startEdge = framePadding.IStart(frameWM);
590 : }
591 0 : nscoord availableISize = aReflowInput.AvailableISize();
592 0 : NS_ASSERTION(availableISize != NS_UNCONSTRAINEDSIZE,
593 : "should no longer use available widths");
594 : // Subtract off inline axis border+padding from availableISize
595 0 : availableISize -= startEdge;
596 0 : availableISize -= framePadding.IEnd(frameWM);
597 0 : lineLayout->BeginSpan(this, &aReflowInput, startEdge,
598 0 : startEdge + availableISize, &mBaseline);
599 :
600 : // First reflow our principal children.
601 0 : nsIFrame* frame = mFrames.FirstChild();
602 0 : bool done = false;
603 0 : while (frame) {
604 : // Check if we should lazily set the child frame's parent pointer.
605 0 : if (irs.mSetParentPointer) {
606 : bool havePrevBlock =
607 0 : irs.mLineContainer && irs.mLineContainer->GetPrevContinuation();
608 0 : nsIFrame* child = frame;
609 0 : do {
610 : // If our block is the first in flow, then any floats under the pulled
611 : // frame must already belong to our block.
612 0 : if (havePrevBlock) {
613 : // This has to happen before we update frame's parent; we need to
614 : // know frame's ancestry under its old block.
615 : // The blockChildren.ContainsFrame check performed by
616 : // ReparentFloatsForInlineChild here may be slow, but we can't
617 : // easily avoid it because we don't know where 'frame' originally
618 : // came from. If we really really have to optimize this we could
619 : // cache whether frame->GetParent() is under its containing blocks
620 : // overflowList or not.
621 0 : ReparentFloatsForInlineChild(irs.mLineContainer, child, false);
622 : }
623 0 : child->SetParent(this);
624 0 : if (inFirstLine) {
625 0 : restyleManager->ReparentStyleContext(child);
626 0 : nsLayoutUtils::MarkDescendantsDirty(child);
627 : }
628 : // We also need to do the same for |frame|'s next-in-flows that are in
629 : // the sibling list. Otherwise, if we reflow |frame| and it's complete
630 : // we'll crash when trying to delete its next-in-flow.
631 : // This scenario doesn't happen often, but it can happen.
632 0 : nsIFrame* nextSibling = child->GetNextSibling();
633 0 : child = child->GetNextInFlow();
634 0 : if (MOZ_UNLIKELY(child)) {
635 0 : while (child != nextSibling && nextSibling) {
636 0 : nextSibling = nextSibling->GetNextSibling();
637 : }
638 0 : if (!nextSibling) {
639 0 : child = nullptr;
640 : }
641 : }
642 0 : MOZ_ASSERT(!child || mFrames.ContainsFrame(child));
643 0 : } while (child);
644 :
645 : // Fix the parent pointer for ::first-letter child frame next-in-flows,
646 : // so nsFirstLetterFrame::Reflow can destroy them safely (bug 401042).
647 0 : nsIFrame* realFrame = nsPlaceholderFrame::GetRealFrameFor(frame);
648 0 : if (realFrame->IsLetterFrame()) {
649 0 : nsIFrame* child = realFrame->PrincipalChildList().FirstChild();
650 0 : if (child) {
651 0 : NS_ASSERTION(child->IsTextFrame(), "unexpected frame type");
652 0 : nsIFrame* nextInFlow = child->GetNextInFlow();
653 0 : for ( ; nextInFlow; nextInFlow = nextInFlow->GetNextInFlow()) {
654 0 : NS_ASSERTION(nextInFlow->IsTextFrame(), "unexpected frame type");
655 0 : if (mFrames.ContainsFrame(nextInFlow)) {
656 0 : nextInFlow->SetParent(this);
657 0 : if (inFirstLine) {
658 0 : restyleManager->ReparentStyleContext(nextInFlow);
659 0 : nsLayoutUtils::MarkDescendantsDirty(nextInFlow);
660 : }
661 : }
662 : else {
663 : #ifdef DEBUG
664 : // Once we find a next-in-flow that isn't ours none of the
665 : // remaining next-in-flows should be either.
666 0 : for ( ; nextInFlow; nextInFlow = nextInFlow->GetNextInFlow()) {
667 0 : NS_ASSERTION(!mFrames.ContainsFrame(nextInFlow),
668 : "unexpected letter frame flow");
669 : }
670 : #endif
671 0 : break;
672 : }
673 : }
674 : }
675 : }
676 : }
677 0 : MOZ_ASSERT(frame->GetParent() == this);
678 :
679 0 : if (!done) {
680 0 : bool reflowingFirstLetter = lineLayout->GetFirstLetterStyleOK();
681 0 : ReflowInlineFrame(aPresContext, aReflowInput, irs, frame, aStatus);
682 0 : done = aStatus.IsInlineBreak() ||
683 0 : (!reflowingFirstLetter && aStatus.IsIncomplete());
684 0 : if (done) {
685 0 : if (!irs.mSetParentPointer) {
686 0 : break;
687 : }
688 : // Keep reparenting the remaining siblings, but don't reflow them.
689 0 : nsFrameList* pushedFrames = GetOverflowFrames();
690 0 : if (pushedFrames && pushedFrames->FirstChild() == frame) {
691 : // Don't bother if |frame| was pushed to our overflow list.
692 0 : break;
693 : }
694 : } else {
695 0 : irs.mPrevFrame = frame;
696 : }
697 : }
698 0 : frame = frame->GetNextSibling();
699 : }
700 :
701 : // Attempt to pull frames from our next-in-flow until we can't
702 0 : if (!done && GetNextInFlow()) {
703 : while (true) {
704 0 : bool reflowingFirstLetter = lineLayout->GetFirstLetterStyleOK();
705 : bool isComplete;
706 0 : if (!frame) { // Could be non-null if we pulled a first-letter frame and
707 : // it created a continuation, since we don't push those.
708 0 : frame = PullOneFrame(aPresContext, irs, &isComplete);
709 : }
710 : #ifdef NOISY_PUSHING
711 : printf("%p pulled up %p\n", this, frame);
712 : #endif
713 0 : if (nullptr == frame) {
714 0 : if (!isComplete) {
715 0 : aStatus.Reset();
716 0 : aStatus.SetIncomplete();
717 : }
718 0 : break;
719 : }
720 0 : ReflowInlineFrame(aPresContext, aReflowInput, irs, frame, aStatus);
721 0 : if (aStatus.IsInlineBreak() ||
722 0 : (!reflowingFirstLetter && aStatus.IsIncomplete())) {
723 0 : break;
724 : }
725 0 : irs.mPrevFrame = frame;
726 0 : frame = frame->GetNextSibling();
727 0 : }
728 : }
729 :
730 0 : NS_ASSERTION(!aStatus.IsComplete() || !GetOverflowFrames(),
731 : "We can't be complete AND have overflow frames!");
732 :
733 : // If after reflowing our children they take up no area then make
734 : // sure that we don't either.
735 : //
736 : // Note: CSS demands that empty inline elements still affect the
737 : // line-height calculations. However, continuations of an inline
738 : // that are empty we force to empty so that things like collapsed
739 : // whitespace in an inline element don't affect the line-height.
740 0 : aMetrics.ISize(lineWM) = lineLayout->EndSpan(this);
741 :
742 : // Compute final width.
743 :
744 : // XXX Note that that the padding start and end are in the frame's
745 : // writing mode, but the metrics' inline-size is in the line's
746 : // writing mode. This makes sense if the line and frame are both
747 : // vertical or both horizontal, but what should happen with
748 : // orthogonal inlines?
749 :
750 : // Make sure to not include our start border and padding if we have a prev
751 : // continuation or if we're in a part of an {ib} split other than the first
752 : // one. For box-decoration-break:clone we always include our start border
753 : // and padding since all continuations have them.
754 0 : if ((!GetPrevContinuation() && !FrameIsNonFirstInIBSplit()) ||
755 : boxDecorationBreakClone) {
756 0 : aMetrics.ISize(lineWM) += framePadding.IStart(frameWM);
757 : }
758 :
759 : /*
760 : * We want to only apply the end border and padding if we're the last
761 : * continuation and either not in an {ib} split or the last part of it. To
762 : * be the last continuation we have to be complete (so that we won't get a
763 : * next-in-flow) and have no non-fluid continuations on our continuation
764 : * chain. For box-decoration-break:clone we always apply the end border and
765 : * padding since all continuations have them.
766 : */
767 0 : if ((aStatus.IsComplete() &&
768 0 : !LastInFlow()->GetNextContinuation() &&
769 0 : !FrameIsNonLastInIBSplit()) ||
770 : boxDecorationBreakClone) {
771 0 : aMetrics.ISize(lineWM) += framePadding.IEnd(frameWM);
772 : }
773 :
774 0 : nsLayoutUtils::SetBSizeFromFontMetrics(this, aMetrics,
775 0 : framePadding, lineWM, frameWM);
776 :
777 : // For now our overflow area is zero. The real value will be
778 : // computed in |nsLineLayout::RelativePositionFrames|.
779 0 : aMetrics.mOverflowAreas.Clear();
780 :
781 : #ifdef NOISY_FINAL_SIZE
782 : ListTag(stdout);
783 : printf(": metrics=%d,%d ascent=%d\n",
784 : aMetrics.Width(), aMetrics.Height(), aMetrics.TopAscent());
785 : #endif
786 0 : }
787 :
788 : void
789 0 : nsInlineFrame::ReflowInlineFrame(nsPresContext* aPresContext,
790 : const ReflowInput& aReflowInput,
791 : InlineReflowInput& irs,
792 : nsIFrame* aFrame,
793 : nsReflowStatus& aStatus)
794 : {
795 0 : nsLineLayout* lineLayout = aReflowInput.mLineLayout;
796 0 : bool reflowingFirstLetter = lineLayout->GetFirstLetterStyleOK();
797 : bool pushedFrame;
798 0 : lineLayout->ReflowFrame(aFrame, aStatus, nullptr, pushedFrame);
799 :
800 0 : if (aStatus.IsInlineBreakBefore()) {
801 0 : if (aFrame != mFrames.FirstChild()) {
802 : // Change break-before status into break-after since we have
803 : // already placed at least one child frame. This preserves the
804 : // break-type so that it can be propagated upward.
805 0 : StyleClear oldBreakType = aStatus.BreakType();
806 0 : aStatus.Reset();
807 0 : aStatus.SetIncomplete();
808 0 : aStatus.SetInlineLineBreakAfter(oldBreakType);
809 0 : PushFrames(aPresContext, aFrame, irs.mPrevFrame, irs);
810 : }
811 : else {
812 : // Preserve reflow status when breaking-before our first child
813 : // and propagate it upward without modification.
814 : }
815 0 : return;
816 : }
817 :
818 : // Create a next-in-flow if needed.
819 0 : if (!aStatus.IsFullyComplete()) {
820 0 : CreateNextInFlow(aFrame);
821 : }
822 :
823 0 : if (aStatus.IsInlineBreakAfter()) {
824 0 : nsIFrame* nextFrame = aFrame->GetNextSibling();
825 0 : if (nextFrame) {
826 0 : aStatus.SetIncomplete();
827 0 : PushFrames(aPresContext, nextFrame, aFrame, irs);
828 : }
829 : else {
830 : // We must return an incomplete status if there are more child
831 : // frames remaining in a next-in-flow that follows this frame.
832 0 : nsInlineFrame* nextInFlow = static_cast<nsInlineFrame*>(GetNextInFlow());
833 0 : while (nextInFlow) {
834 0 : if (nextInFlow->mFrames.NotEmpty()) {
835 0 : aStatus.SetIncomplete();
836 0 : break;
837 : }
838 0 : nextInFlow = static_cast<nsInlineFrame*>(nextInFlow->GetNextInFlow());
839 : }
840 : }
841 0 : return;
842 : }
843 :
844 0 : if (!aStatus.IsFullyComplete() && !reflowingFirstLetter) {
845 0 : nsIFrame* nextFrame = aFrame->GetNextSibling();
846 0 : if (nextFrame) {
847 0 : PushFrames(aPresContext, nextFrame, aFrame, irs);
848 : }
849 : }
850 : }
851 :
852 : nsIFrame*
853 0 : nsInlineFrame::PullOneFrame(nsPresContext* aPresContext,
854 : InlineReflowInput& irs,
855 : bool* aIsComplete)
856 : {
857 0 : bool isComplete = true;
858 :
859 0 : nsIFrame* frame = nullptr;
860 0 : nsInlineFrame* nextInFlow = irs.mNextInFlow;
861 0 : while (nextInFlow) {
862 0 : frame = nextInFlow->mFrames.FirstChild();
863 0 : if (!frame) {
864 : // The nextInFlow's principal list has no frames, try its overflow list.
865 0 : nsFrameList* overflowFrames = nextInFlow->GetOverflowFrames();
866 0 : if (overflowFrames) {
867 0 : frame = overflowFrames->RemoveFirstChild();
868 0 : if (overflowFrames->IsEmpty()) {
869 : // We're stealing the only frame - delete the overflow list.
870 0 : nextInFlow->DestroyOverflowList();
871 : } else {
872 : // We leave the remaining frames on the overflow list (rather than
873 : // putting them on nextInFlow's principal list) so we don't have to
874 : // set up the parent for them.
875 : }
876 : // ReparentFloatsForInlineChild needs it to be on a child list -
877 : // we remove it again below.
878 0 : nextInFlow->mFrames.SetFrames(frame);
879 : }
880 : }
881 :
882 0 : if (frame) {
883 : // If our block has no next continuation, then any floats belonging to
884 : // the pulled frame must belong to our block already. This check ensures
885 : // we do no extra work in the common non-vertical-breaking case.
886 0 : if (irs.mLineContainer && irs.mLineContainer->GetNextContinuation()) {
887 : // The blockChildren.ContainsFrame check performed by
888 : // ReparentFloatsForInlineChild will be fast because frame's ancestor
889 : // will be the first child of its containing block.
890 0 : ReparentFloatsForInlineChild(irs.mLineContainer, frame, false);
891 : }
892 0 : nextInFlow->mFrames.RemoveFirstChild();
893 : // nsFirstLineFrame::PullOneFrame calls ReparentStyleContext.
894 :
895 0 : mFrames.InsertFrame(this, irs.mPrevFrame, frame);
896 0 : isComplete = false;
897 0 : if (irs.mLineLayout) {
898 0 : irs.mLineLayout->SetDirtyNextLine();
899 : }
900 0 : nsContainerFrame::ReparentFrameView(frame, nextInFlow, this);
901 0 : break;
902 : }
903 0 : nextInFlow = static_cast<nsInlineFrame*>(nextInFlow->GetNextInFlow());
904 0 : irs.mNextInFlow = nextInFlow;
905 : }
906 :
907 0 : *aIsComplete = isComplete;
908 0 : return frame;
909 : }
910 :
911 : void
912 0 : nsInlineFrame::PushFrames(nsPresContext* aPresContext,
913 : nsIFrame* aFromChild,
914 : nsIFrame* aPrevSibling,
915 : InlineReflowInput& aState)
916 : {
917 0 : NS_PRECONDITION(aFromChild, "null pointer");
918 0 : NS_PRECONDITION(aPrevSibling, "pushing first child");
919 0 : NS_PRECONDITION(aPrevSibling->GetNextSibling() == aFromChild, "bad prev sibling");
920 :
921 : #ifdef NOISY_PUSHING
922 : printf("%p pushing aFromChild %p, disconnecting from prev sib %p\n",
923 : this, aFromChild, aPrevSibling);
924 : #endif
925 :
926 : // Add the frames to our overflow list (let our next in flow drain
927 : // our overflow list when it is ready)
928 0 : SetOverflowFrames(mFrames.RemoveFramesAfter(aPrevSibling));
929 0 : if (aState.mLineLayout) {
930 0 : aState.mLineLayout->SetDirtyNextLine();
931 : }
932 0 : }
933 :
934 :
935 : //////////////////////////////////////////////////////////////////////
936 :
937 : nsIFrame::LogicalSides
938 0 : nsInlineFrame::GetLogicalSkipSides(const ReflowInput* aReflowInput) const
939 : {
940 0 : if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
941 : StyleBoxDecorationBreak::Clone)) {
942 0 : return LogicalSides();
943 : }
944 :
945 0 : LogicalSides skip;
946 0 : if (!IsFirst()) {
947 0 : nsInlineFrame* prev = (nsInlineFrame*) GetPrevContinuation();
948 0 : if ((GetStateBits() & NS_INLINE_FRAME_BIDI_VISUAL_STATE_IS_SET) ||
949 0 : (prev && (prev->mRect.height || prev->mRect.width))) {
950 : // Prev continuation is not empty therefore we don't render our start
951 : // border edge.
952 0 : skip |= eLogicalSideBitsIStart;
953 : }
954 : else {
955 : // If the prev continuation is empty, then go ahead and let our start
956 : // edge border render.
957 : }
958 : }
959 0 : if (!IsLast()) {
960 0 : nsInlineFrame* next = (nsInlineFrame*) GetNextContinuation();
961 0 : if ((GetStateBits() & NS_INLINE_FRAME_BIDI_VISUAL_STATE_IS_SET) ||
962 0 : (next && (next->mRect.height || next->mRect.width))) {
963 : // Next continuation is not empty therefore we don't render our end
964 : // border edge.
965 0 : skip |= eLogicalSideBitsIEnd;
966 : }
967 : else {
968 : // If the next continuation is empty, then go ahead and let our end
969 : // edge border render.
970 : }
971 : }
972 :
973 0 : if (GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) {
974 : // All but the last part of an {ib} split should skip the "end" side (as
975 : // determined by this frame's direction) and all but the first part of such
976 : // a split should skip the "start" side. But figuring out which part of
977 : // the split we are involves getting our first continuation, which might be
978 : // expensive. So don't bother if we already have the relevant bits set.
979 0 : if (skip != LogicalSides(eLogicalSideBitsIBoth)) {
980 : // We're missing one of the skip bits, so check whether we need to set it.
981 : // Only get the first continuation once, as an optimization.
982 0 : nsIFrame* firstContinuation = FirstContinuation();
983 0 : if (firstContinuation->FrameIsNonLastInIBSplit()) {
984 0 : skip |= eLogicalSideBitsIEnd;
985 : }
986 0 : if (firstContinuation->FrameIsNonFirstInIBSplit()) {
987 0 : skip |= eLogicalSideBitsIStart;
988 : }
989 : }
990 : }
991 :
992 0 : return skip;
993 : }
994 :
995 : nscoord
996 0 : nsInlineFrame::GetLogicalBaseline(mozilla::WritingMode aWritingMode) const
997 : {
998 0 : return mBaseline;
999 : }
1000 :
1001 : #ifdef ACCESSIBILITY
1002 : a11y::AccType
1003 0 : nsInlineFrame::AccessibleType()
1004 : {
1005 : // Broken image accessibles are created here, because layout
1006 : // replaces the image or image control frame with an inline frame
1007 0 : if (mContent->IsHTMLElement(nsGkAtoms::input)) // Broken <input type=image ... />
1008 0 : return a11y::eHTMLButtonType;
1009 0 : if (mContent->IsHTMLElement(nsGkAtoms::img)) // Create accessible for broken <img>
1010 0 : return a11y::eHyperTextType;
1011 :
1012 0 : return a11y::eNoType;
1013 : }
1014 : #endif
1015 :
1016 : void
1017 0 : nsInlineFrame::UpdateStyleOfOwnedAnonBoxesForIBSplit(
1018 : ServoRestyleState& aRestyleState)
1019 : {
1020 0 : MOZ_ASSERT(GetStateBits() & NS_FRAME_OWNS_ANON_BOXES,
1021 : "Why did we get called?");
1022 0 : MOZ_ASSERT(GetStateBits() & NS_FRAME_PART_OF_IBSPLIT,
1023 : "Why did we have the NS_FRAME_OWNS_ANON_BOXES bit set?");
1024 : // Note: this assert _looks_ expensive, but it's cheap in all the cases when
1025 : // it passes!
1026 0 : MOZ_ASSERT(nsLayoutUtils::FirstContinuationOrIBSplitSibling(this) == this,
1027 : "Only the primary frame of the inline in a block-inside-inline "
1028 : "split should have NS_FRAME_OWNS_ANON_BOXES");
1029 0 : MOZ_ASSERT(mContent->GetPrimaryFrame() == this,
1030 : "We should be the primary frame for our element");
1031 :
1032 0 : nsIFrame* blockFrame = GetProperty(nsIFrame::IBSplitSibling());
1033 0 : MOZ_ASSERT(blockFrame, "Why did we have an IB split?");
1034 :
1035 : // The later inlines need to get our style.
1036 0 : nsStyleContext* ourStyle = StyleContext();
1037 :
1038 : // The anonymous block's style inherits from ours, and we already have our new
1039 : // style context.
1040 : RefPtr<nsStyleContext> newContext =
1041 0 : aRestyleState.StyleSet().ResolveInheritingAnonymousBoxStyle(
1042 0 : nsCSSAnonBoxes::mozBlockInsideInlineWrapper, ourStyle);
1043 :
1044 : // We're guaranteed that newContext only differs from the old style context on
1045 : // the block in things they might inherit from us. And changehint processing
1046 : // guarantees walking the continuation and ib-sibling chains, so our existing
1047 : // changehint being in aChangeList is good enough. So we don't need to touch
1048 : // aChangeList at all here.
1049 :
1050 0 : while (blockFrame) {
1051 0 : MOZ_ASSERT(!blockFrame->GetPrevContinuation(),
1052 : "Must be first continuation");
1053 :
1054 0 : MOZ_ASSERT(blockFrame->StyleContext()->GetPseudo() ==
1055 : nsCSSAnonBoxes::mozBlockInsideInlineWrapper,
1056 : "Unexpected kind of style context");
1057 :
1058 : // We don't want to just walk through using GetNextContinuationWithSameStyle
1059 : // here, because we want to set updated style contexts on both our
1060 : // ib-sibling blocks and inlines.
1061 0 : for (nsIFrame* cont = blockFrame; cont; cont = cont->GetNextContinuation()) {
1062 0 : cont->SetStyleContext(newContext);
1063 : }
1064 :
1065 0 : nsIFrame* nextInline = blockFrame->GetProperty(nsIFrame::IBSplitSibling());
1066 0 : MOZ_ASSERT(nextInline, "There is always a trailing inline in an IB split");
1067 :
1068 0 : for (nsIFrame* cont = nextInline; cont; cont = cont->GetNextContinuation()) {
1069 0 : cont->SetStyleContext(ourStyle);
1070 : }
1071 0 : blockFrame = nextInline->GetProperty(nsIFrame::IBSplitSibling());
1072 : }
1073 0 : }
1074 :
1075 : //////////////////////////////////////////////////////////////////////
1076 :
1077 : // nsLineFrame implementation
1078 :
1079 : nsFirstLineFrame*
1080 0 : NS_NewFirstLineFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
1081 : {
1082 0 : return new (aPresShell) nsFirstLineFrame(aContext);
1083 : }
1084 :
1085 0 : NS_IMPL_FRAMEARENA_HELPERS(nsFirstLineFrame)
1086 :
1087 : void
1088 0 : nsFirstLineFrame::Init(nsIContent* aContent,
1089 : nsContainerFrame* aParent,
1090 : nsIFrame* aPrevInFlow)
1091 : {
1092 0 : nsInlineFrame::Init(aContent, aParent, aPrevInFlow);
1093 0 : if (!aPrevInFlow) {
1094 0 : MOZ_ASSERT(StyleContext()->GetPseudo() == nsCSSPseudoElements::firstLine);
1095 0 : return;
1096 : }
1097 :
1098 : // This frame is a continuation - fixup the style context if aPrevInFlow
1099 : // is the first-in-flow (the only one with a ::first-line pseudo).
1100 0 : if (aPrevInFlow->StyleContext()->GetPseudo() == nsCSSPseudoElements::firstLine) {
1101 0 : MOZ_ASSERT(FirstInFlow() == aPrevInFlow);
1102 : // Create a new style context that is a child of the parent
1103 : // style context thus removing the ::first-line style. This way
1104 : // we behave as if an anonymous (unstyled) span was the child
1105 : // of the parent frame.
1106 0 : nsStyleContext* parentContext = aParent->StyleContext();
1107 0 : RefPtr<nsStyleContext> newSC = PresContext()->StyleSet()->
1108 0 : ResolveInheritingAnonymousBoxStyle(nsCSSAnonBoxes::mozLineFrame,
1109 0 : parentContext);
1110 0 : SetStyleContext(newSC);
1111 : } else {
1112 0 : MOZ_ASSERT(FirstInFlow() != aPrevInFlow);
1113 0 : MOZ_ASSERT(aPrevInFlow->StyleContext()->GetPseudo() ==
1114 : nsCSSAnonBoxes::mozLineFrame);
1115 : }
1116 : }
1117 :
1118 : #ifdef DEBUG_FRAME_DUMP
1119 : nsresult
1120 0 : nsFirstLineFrame::GetFrameName(nsAString& aResult) const
1121 : {
1122 0 : return MakeFrameName(NS_LITERAL_STRING("Line"), aResult);
1123 : }
1124 : #endif
1125 :
1126 : nsIFrame*
1127 0 : nsFirstLineFrame::PullOneFrame(nsPresContext* aPresContext, InlineReflowInput& irs,
1128 : bool* aIsComplete)
1129 : {
1130 0 : nsIFrame* frame = nsInlineFrame::PullOneFrame(aPresContext, irs, aIsComplete);
1131 0 : if (frame && !GetPrevInFlow()) {
1132 : // We are a first-line frame. Fixup the child frames
1133 : // style-context that we just pulled.
1134 0 : NS_ASSERTION(frame->GetParent() == this, "Incorrect parent?");
1135 0 : aPresContext->RestyleManager()->ReparentStyleContext(frame);
1136 0 : nsLayoutUtils::MarkDescendantsDirty(frame);
1137 : }
1138 0 : return frame;
1139 : }
1140 :
1141 : void
1142 0 : nsFirstLineFrame::Reflow(nsPresContext* aPresContext,
1143 : ReflowOutput& aMetrics,
1144 : const ReflowInput& aReflowInput,
1145 : nsReflowStatus& aStatus)
1146 : {
1147 0 : MarkInReflow();
1148 0 : if (nullptr == aReflowInput.mLineLayout) {
1149 0 : return; // XXX does this happen? why?
1150 : }
1151 :
1152 0 : nsIFrame* lineContainer = aReflowInput.mLineLayout->LineContainerFrame();
1153 :
1154 : // Check for an overflow list with our prev-in-flow
1155 0 : nsFirstLineFrame* prevInFlow = (nsFirstLineFrame*)GetPrevInFlow();
1156 0 : if (prevInFlow) {
1157 : AutoFrameListPtr prevOverflowFrames(aPresContext,
1158 0 : prevInFlow->StealOverflowFrames());
1159 0 : if (prevOverflowFrames) {
1160 : // Assign all floats to our block if necessary
1161 0 : if (lineContainer && lineContainer->GetPrevContinuation()) {
1162 0 : ReparentFloatsForInlineChild(lineContainer,
1163 : prevOverflowFrames->FirstChild(),
1164 0 : true);
1165 : }
1166 : const nsFrameList::Slice& newFrames =
1167 0 : mFrames.InsertFrames(this, nullptr, *prevOverflowFrames);
1168 0 : ReparentChildListStyle(aPresContext, newFrames, this);
1169 : }
1170 : }
1171 :
1172 : // It's also possible that we have an overflow list for ourselves.
1173 0 : DrainSelfOverflowList();
1174 :
1175 : // Set our own reflow state (additional state above and beyond
1176 : // aReflowInput)
1177 0 : InlineReflowInput irs;
1178 0 : irs.mPrevFrame = nullptr;
1179 0 : irs.mLineContainer = lineContainer;
1180 0 : irs.mLineLayout = aReflowInput.mLineLayout;
1181 0 : irs.mNextInFlow = (nsInlineFrame*) GetNextInFlow();
1182 :
1183 0 : bool wasEmpty = mFrames.IsEmpty();
1184 0 : if (wasEmpty) {
1185 : // Try to pull over one frame before starting so that we know
1186 : // whether we have an anonymous block or not.
1187 : bool complete;
1188 0 : PullOneFrame(aPresContext, irs, &complete);
1189 : }
1190 :
1191 0 : if (nullptr == GetPrevInFlow()) {
1192 : // XXX This is pretty sick, but what we do here is to pull-up, in
1193 : // advance, all of the next-in-flows children. We re-resolve their
1194 : // style while we are at at it so that when we reflow they have
1195 : // the right style.
1196 : //
1197 : // All of this is so that text-runs reflow properly.
1198 0 : irs.mPrevFrame = mFrames.LastChild();
1199 : for (;;) {
1200 : bool complete;
1201 0 : nsIFrame* frame = PullOneFrame(aPresContext, irs, &complete);
1202 0 : if (!frame) {
1203 0 : break;
1204 : }
1205 0 : irs.mPrevFrame = frame;
1206 0 : }
1207 0 : irs.mPrevFrame = nullptr;
1208 : }
1209 :
1210 0 : NS_ASSERTION(!aReflowInput.mLineLayout->GetInFirstLine(),
1211 : "Nested first-line frames? BOGUS");
1212 0 : aReflowInput.mLineLayout->SetInFirstLine(true);
1213 0 : ReflowFrames(aPresContext, aReflowInput, irs, aMetrics, aStatus);
1214 0 : aReflowInput.mLineLayout->SetInFirstLine(false);
1215 :
1216 0 : ReflowAbsoluteFrames(aPresContext, aMetrics, aReflowInput, aStatus);
1217 :
1218 : // Note: the line layout code will properly compute our overflow state for us
1219 : }
1220 :
1221 : /* virtual */ void
1222 0 : nsFirstLineFrame::PullOverflowsFromPrevInFlow()
1223 : {
1224 0 : nsFirstLineFrame* prevInFlow = static_cast<nsFirstLineFrame*>(GetPrevInFlow());
1225 0 : if (prevInFlow) {
1226 0 : nsPresContext* presContext = PresContext();
1227 : AutoFrameListPtr prevOverflowFrames(presContext,
1228 0 : prevInFlow->StealOverflowFrames());
1229 0 : if (prevOverflowFrames) {
1230 : // Assume that our prev-in-flow has the same line container that we do.
1231 : const nsFrameList::Slice& newFrames =
1232 0 : mFrames.InsertFrames(this, nullptr, *prevOverflowFrames);
1233 0 : ReparentChildListStyle(presContext, newFrames, this);
1234 : }
1235 : }
1236 0 : }
1237 :
1238 : /* virtual */ bool
1239 0 : nsFirstLineFrame::DrainSelfOverflowList()
1240 : {
1241 0 : AutoFrameListPtr overflowFrames(PresContext(), StealOverflowFrames());
1242 0 : if (overflowFrames) {
1243 0 : bool result = !overflowFrames->IsEmpty();
1244 : const nsFrameList::Slice& newFrames =
1245 0 : mFrames.AppendFrames(nullptr, *overflowFrames);
1246 0 : ReparentChildListStyle(PresContext(), newFrames, this);
1247 0 : return result;
1248 : }
1249 0 : return false;
1250 : }
|