1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : // vim:cindent:ts=2:et:sw=2:
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla Communicator client code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1998
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Steve Clark <buster@netscape.com>
25 : * Robert O'Callahan <roc+moz@cs.cmu.edu>
26 : * L. David Baron <dbaron@dbaron.org>
27 : * IBM Corporation
28 : * Mats Palmgren <matspal@gmail.com>
29 : *
30 : * Alternatively, the contents of this file may be used under the terms of
31 : * either of the GNU General Public License Version 2 or later (the "GPL"),
32 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
33 : * in which case the provisions of the GPL or the LGPL are applicable instead
34 : * of those above. If you wish to allow use of your version of this file only
35 : * under the terms of either the GPL or the LGPL, and not to allow others to
36 : * use your version of this file under the terms of the MPL, indicate your
37 : * decision by deleting the provisions above and replace them with the notice
38 : * and other provisions required by the GPL or the LGPL. If you do not delete
39 : * the provisions above, a recipient may use your version of this file under
40 : * the terms of any one of the MPL, the GPL or the LGPL.
41 : *
42 : * ***** END LICENSE BLOCK ***** */
43 :
44 : /*
45 : * rendering object for CSS display:block, inline-block, and list-item
46 : * boxes, also used for various anonymous boxes
47 : */
48 :
49 : #include "nsCOMPtr.h"
50 : #include "nsBlockFrame.h"
51 : #include "nsBlockReflowContext.h"
52 : #include "nsBlockReflowState.h"
53 : #include "nsBulletFrame.h"
54 : #include "nsLineBox.h"
55 : #include "nsInlineFrame.h"
56 : #include "nsLineLayout.h"
57 : #include "nsPlaceholderFrame.h"
58 : #include "nsStyleConsts.h"
59 : #include "nsFrameManager.h"
60 : #include "nsPresContext.h"
61 : #include "nsIPresShell.h"
62 : #include "nsStyleContext.h"
63 : #include "nsIView.h"
64 : #include "nsHTMLParts.h"
65 : #include "nsGkAtoms.h"
66 : #include "nsIDOMEvent.h"
67 : #include "nsGenericHTMLElement.h"
68 : #include "prprf.h"
69 : #include "nsStyleChangeList.h"
70 : #include "nsFrameSelection.h"
71 : #include "nsFloatManager.h"
72 : #include "nsIntervalSet.h"
73 : #include "prenv.h"
74 : #include "plstr.h"
75 : #include "nsGUIEvent.h"
76 : #include "nsLayoutErrors.h"
77 : #include "nsAutoPtr.h"
78 : #include "nsIServiceManager.h"
79 : #include "nsIScrollableFrame.h"
80 : #ifdef ACCESSIBILITY
81 : #include "nsIDOMHTMLDocument.h"
82 : #include "nsAccessibilityService.h"
83 : #endif
84 : #include "nsLayoutUtils.h"
85 : #include "nsDisplayList.h"
86 : #include "nsContentErrors.h"
87 : #include "nsCSSAnonBoxes.h"
88 : #include "nsCSSFrameConstructor.h"
89 : #include "nsCSSRendering.h"
90 : #include "FrameLayerBuilder.h"
91 : #include "nsRenderingContext.h"
92 : #include "TextOverflow.h"
93 : #include "mozilla/Util.h" // for DebugOnly
94 :
95 : #ifdef IBMBIDI
96 : #include "nsBidiPresUtils.h"
97 : #endif // IBMBIDI
98 :
99 : #include "nsIDOMHTMLBodyElement.h"
100 : #include "nsIDOMHTMLHtmlElement.h"
101 :
102 : static const int MIN_LINES_NEEDING_CURSOR = 20;
103 :
104 : static const PRUnichar kDiscCharacter = 0x2022;
105 : static const PRUnichar kCircleCharacter = 0x25e6;
106 : static const PRUnichar kSquareCharacter = 0x25aa;
107 :
108 : #define DISABLE_FLOAT_BREAKING_IN_COLUMNS
109 :
110 : using namespace mozilla;
111 : using namespace mozilla::css;
112 :
113 : #ifdef DEBUG
114 : #include "nsBlockDebugFlags.h"
115 :
116 : bool nsBlockFrame::gLamePaintMetrics;
117 : bool nsBlockFrame::gLameReflowMetrics;
118 : bool nsBlockFrame::gNoisy;
119 : bool nsBlockFrame::gNoisyDamageRepair;
120 : bool nsBlockFrame::gNoisyIntrinsic;
121 : bool nsBlockFrame::gNoisyReflow;
122 : bool nsBlockFrame::gReallyNoisyReflow;
123 : bool nsBlockFrame::gNoisyFloatManager;
124 : bool nsBlockFrame::gVerifyLines;
125 : bool nsBlockFrame::gDisableResizeOpt;
126 :
127 : PRInt32 nsBlockFrame::gNoiseIndent;
128 :
129 : struct BlockDebugFlags {
130 : const char* name;
131 : bool* on;
132 : };
133 :
134 : static const BlockDebugFlags gFlags[] = {
135 : { "reflow", &nsBlockFrame::gNoisyReflow },
136 : { "really-noisy-reflow", &nsBlockFrame::gReallyNoisyReflow },
137 : { "intrinsic", &nsBlockFrame::gNoisyIntrinsic },
138 : { "float-manager", &nsBlockFrame::gNoisyFloatManager },
139 : { "verify-lines", &nsBlockFrame::gVerifyLines },
140 : { "damage-repair", &nsBlockFrame::gNoisyDamageRepair },
141 : { "lame-paint-metrics", &nsBlockFrame::gLamePaintMetrics },
142 : { "lame-reflow-metrics", &nsBlockFrame::gLameReflowMetrics },
143 : { "disable-resize-opt", &nsBlockFrame::gDisableResizeOpt },
144 : };
145 : #define NUM_DEBUG_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
146 :
147 : static void
148 0 : ShowDebugFlags()
149 : {
150 0 : printf("Here are the available GECKO_BLOCK_DEBUG_FLAGS:\n");
151 0 : const BlockDebugFlags* bdf = gFlags;
152 0 : const BlockDebugFlags* end = gFlags + NUM_DEBUG_FLAGS;
153 0 : for (; bdf < end; bdf++) {
154 0 : printf(" %s\n", bdf->name);
155 : }
156 0 : printf("Note: GECKO_BLOCK_DEBUG_FLAGS is a comma separated list of flag\n");
157 0 : printf("names (no whitespace)\n");
158 0 : }
159 :
160 : void
161 0 : nsBlockFrame::InitDebugFlags()
162 : {
163 : static bool firstTime = true;
164 0 : if (firstTime) {
165 0 : firstTime = false;
166 0 : char* flags = PR_GetEnv("GECKO_BLOCK_DEBUG_FLAGS");
167 0 : if (flags) {
168 0 : bool error = false;
169 0 : for (;;) {
170 0 : char* cm = PL_strchr(flags, ',');
171 0 : if (cm) *cm = '\0';
172 :
173 0 : bool found = false;
174 0 : const BlockDebugFlags* bdf = gFlags;
175 0 : const BlockDebugFlags* end = gFlags + NUM_DEBUG_FLAGS;
176 0 : for (; bdf < end; bdf++) {
177 0 : if (PL_strcasecmp(bdf->name, flags) == 0) {
178 0 : *(bdf->on) = true;
179 0 : printf("nsBlockFrame: setting %s debug flag on\n", bdf->name);
180 0 : gNoisy = true;
181 0 : found = true;
182 0 : break;
183 : }
184 : }
185 0 : if (!found) {
186 0 : error = true;
187 : }
188 :
189 0 : if (!cm) break;
190 0 : *cm = ',';
191 0 : flags = cm + 1;
192 : }
193 0 : if (error) {
194 0 : ShowDebugFlags();
195 : }
196 : }
197 : }
198 0 : }
199 :
200 : #endif
201 :
202 : // add in a sanity check for absurdly deep frame trees. See bug 42138
203 : // can't just use IsFrameTreeTooDeep() because that method has side effects we don't want
204 : #define MAX_DEPTH_FOR_LIST_RENUMBERING 200 // 200 open displayable tags is pretty unrealistic
205 :
206 : //----------------------------------------------------------------------
207 :
208 : // Debugging support code
209 :
210 : #ifdef DEBUG
211 : const char* nsBlockFrame::kReflowCommandType[] = {
212 : "ContentChanged",
213 : "StyleChanged",
214 : "ReflowDirty",
215 : "Timeout",
216 : "UserDefined",
217 : };
218 : #endif
219 :
220 : #ifdef REALLY_NOISY_FIRST_LINE
221 : static void
222 : DumpStyleGeneaology(nsIFrame* aFrame, const char* gap)
223 : {
224 : fputs(gap, stdout);
225 : nsFrame::ListTag(stdout, aFrame);
226 : printf(": ");
227 : nsStyleContext* sc = aFrame->GetStyleContext();
228 : while (nsnull != sc) {
229 : nsStyleContext* psc;
230 : printf("%p ", sc);
231 : psc = sc->GetParent();
232 : sc = psc;
233 : }
234 : printf("\n");
235 : }
236 : #endif
237 :
238 : #ifdef REFLOW_STATUS_COVERAGE
239 : static void
240 : RecordReflowStatus(bool aChildIsBlock, nsReflowStatus aFrameReflowStatus)
241 : {
242 : static PRUint32 record[2];
243 :
244 : // 0: child-is-block
245 : // 1: child-is-inline
246 : PRIntn index = 0;
247 : if (!aChildIsBlock) index |= 1;
248 :
249 : // Compute new status
250 : PRUint32 newS = record[index];
251 : if (NS_INLINE_IS_BREAK(aFrameReflowStatus)) {
252 : if (NS_INLINE_IS_BREAK_BEFORE(aFrameReflowStatus)) {
253 : newS |= 1;
254 : }
255 : else if (NS_FRAME_IS_NOT_COMPLETE(aFrameReflowStatus)) {
256 : newS |= 2;
257 : }
258 : else {
259 : newS |= 4;
260 : }
261 : }
262 : else if (NS_FRAME_IS_NOT_COMPLETE(aFrameReflowStatus)) {
263 : newS |= 8;
264 : }
265 : else {
266 : newS |= 16;
267 : }
268 :
269 : // Log updates to the status that yield different values
270 : if (record[index] != newS) {
271 : record[index] = newS;
272 : printf("record(%d): %02x %02x\n", index, record[0], record[1]);
273 : }
274 : }
275 : #endif
276 :
277 : // Destructor function for the overflowLines frame property
278 : static void
279 0 : DestroyOverflowLines(void* aPropertyValue)
280 : {
281 0 : NS_ERROR("Overflow lines should never be destroyed by the FramePropertyTable");
282 0 : }
283 :
284 0 : NS_DECLARE_FRAME_PROPERTY(OverflowLinesProperty, DestroyOverflowLines)
285 0 : NS_DECLARE_FRAME_PROPERTY(OverflowOutOfFlowsProperty,
286 : nsContainerFrame::DestroyFrameList)
287 0 : NS_DECLARE_FRAME_PROPERTY(PushedFloatProperty,
288 : nsContainerFrame::DestroyFrameList)
289 0 : NS_DECLARE_FRAME_PROPERTY(OutsideBulletProperty,
290 : nsContainerFrame::DestroyFrameList)
291 0 : NS_DECLARE_FRAME_PROPERTY(InsideBulletProperty, nsnull)
292 :
293 : //----------------------------------------------------------------------
294 :
295 : nsIFrame*
296 0 : NS_NewBlockFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, PRUint32 aFlags)
297 : {
298 0 : nsBlockFrame* it = new (aPresShell) nsBlockFrame(aContext);
299 0 : if (it) {
300 0 : it->SetFlags(aFlags);
301 : }
302 0 : return it;
303 : }
304 :
305 0 : NS_IMPL_FRAMEARENA_HELPERS(nsBlockFrame)
306 :
307 0 : nsBlockFrame::~nsBlockFrame()
308 : {
309 0 : }
310 :
311 : void
312 0 : nsBlockFrame::DestroyFrom(nsIFrame* aDestructRoot)
313 : {
314 0 : ClearLineCursor();
315 0 : DestroyAbsoluteFrames(aDestructRoot);
316 0 : mFloats.DestroyFramesFrom(aDestructRoot);
317 0 : nsPresContext* presContext = PresContext();
318 0 : nsLineBox::DeleteLineList(presContext, mLines, aDestructRoot);
319 :
320 : // Now clear mFrames, since we've destroyed all the frames in it.
321 0 : mFrames.Clear();
322 :
323 0 : nsFrameList* pushedFloats = RemovePushedFloats();
324 0 : if (pushedFloats) {
325 0 : pushedFloats->DestroyFrom(aDestructRoot);
326 : }
327 :
328 : // destroy overflow lines now
329 0 : FrameLines* overflowLines = RemoveOverflowLines();
330 0 : if (overflowLines) {
331 : nsLineBox::DeleteLineList(presContext, overflowLines->mLines,
332 0 : aDestructRoot);
333 0 : delete overflowLines;
334 : }
335 :
336 : {
337 0 : nsAutoOOFFrameList oofs(this);
338 0 : oofs.mList.DestroyFramesFrom(aDestructRoot);
339 : // oofs is now empty and will remove the frame list property
340 : }
341 :
342 0 : nsBlockFrameSuper::DestroyFrom(aDestructRoot);
343 0 : }
344 :
345 : /* virtual */ nsILineIterator*
346 0 : nsBlockFrame::GetLineIterator()
347 : {
348 0 : nsLineIterator* it = new nsLineIterator;
349 0 : if (!it)
350 0 : return nsnull;
351 :
352 0 : const nsStyleVisibility* visibility = GetStyleVisibility();
353 0 : nsresult rv = it->Init(mLines, visibility->mDirection == NS_STYLE_DIRECTION_RTL);
354 0 : if (NS_FAILED(rv)) {
355 0 : delete it;
356 0 : return nsnull;
357 : }
358 0 : return it;
359 : }
360 :
361 0 : NS_QUERYFRAME_HEAD(nsBlockFrame)
362 0 : NS_QUERYFRAME_ENTRY(nsBlockFrame)
363 0 : NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrameSuper)
364 :
365 : nsSplittableType
366 0 : nsBlockFrame::GetSplittableType() const
367 : {
368 0 : return NS_FRAME_SPLITTABLE_NON_RECTANGULAR;
369 : }
370 :
371 : #ifdef DEBUG
372 : NS_METHOD
373 0 : nsBlockFrame::List(FILE* out, PRInt32 aIndent) const
374 : {
375 0 : IndentBy(out, aIndent);
376 0 : ListTag(out);
377 : #ifdef DEBUG_waterson
378 : fprintf(out, " [parent=%p]", mParent);
379 : #endif
380 0 : if (HasView()) {
381 0 : fprintf(out, " [view=%p]", static_cast<void*>(GetView()));
382 : }
383 0 : if (GetNextSibling()) {
384 0 : fprintf(out, " next=%p", static_cast<void*>(GetNextSibling()));
385 : }
386 :
387 : // Output the flow linkage
388 0 : if (nsnull != GetPrevInFlow()) {
389 0 : fprintf(out, " prev-in-flow=%p", static_cast<void*>(GetPrevInFlow()));
390 : }
391 0 : if (nsnull != GetNextInFlow()) {
392 0 : fprintf(out, " next-in-flow=%p", static_cast<void*>(GetNextInFlow()));
393 : }
394 :
395 0 : void* IBsibling = Properties().Get(IBSplitSpecialSibling());
396 0 : if (IBsibling) {
397 0 : fprintf(out, " IBSplitSpecialSibling=%p", IBsibling);
398 : }
399 0 : void* IBprevsibling = Properties().Get(IBSplitSpecialPrevSibling());
400 0 : if (IBprevsibling) {
401 0 : fprintf(out, " IBSplitSpecialPrevSibling=%p", IBprevsibling);
402 : }
403 :
404 0 : if (nsnull != mContent) {
405 0 : fprintf(out, " [content=%p]", static_cast<void*>(mContent));
406 : }
407 :
408 : // Output the rect and state
409 0 : fprintf(out, " {%d,%d,%d,%d}", mRect.x, mRect.y, mRect.width, mRect.height);
410 0 : if (0 != mState) {
411 0 : fprintf(out, " [state=%016llx]", (unsigned long long)mState);
412 : }
413 0 : nsBlockFrame* f = const_cast<nsBlockFrame*>(this);
414 0 : if (f->HasOverflowAreas()) {
415 0 : nsRect overflowArea = f->GetVisualOverflowRect();
416 : fprintf(out, " [vis-overflow=%d,%d,%d,%d]", overflowArea.x, overflowArea.y,
417 0 : overflowArea.width, overflowArea.height);
418 0 : overflowArea = f->GetScrollableOverflowRect();
419 : fprintf(out, " [scr-overflow=%d,%d,%d,%d]", overflowArea.x, overflowArea.y,
420 0 : overflowArea.width, overflowArea.height);
421 : }
422 0 : PRInt32 numInlineLines = 0;
423 0 : PRInt32 numBlockLines = 0;
424 0 : if (!mLines.empty()) {
425 0 : const_line_iterator line = begin_lines(), line_end = end_lines();
426 0 : for ( ; line != line_end; ++line) {
427 0 : if (line->IsBlock())
428 0 : numBlockLines++;
429 : else
430 0 : numInlineLines++;
431 : }
432 : }
433 : fprintf(out, " sc=%p(i=%d,b=%d)",
434 0 : static_cast<void*>(mStyleContext), numInlineLines, numBlockLines);
435 0 : nsIAtom* pseudoTag = mStyleContext->GetPseudo();
436 0 : if (pseudoTag) {
437 0 : nsAutoString atomString;
438 0 : pseudoTag->ToString(atomString);
439 : fprintf(out, " pst=%s",
440 0 : NS_LossyConvertUTF16toASCII(atomString).get());
441 : }
442 0 : fputs("<\n", out);
443 :
444 0 : aIndent++;
445 :
446 : // Output the lines
447 0 : if (!mLines.empty()) {
448 0 : const_line_iterator line = begin_lines(), line_end = end_lines();
449 0 : for ( ; line != line_end; ++line) {
450 0 : line->List(out, aIndent);
451 : }
452 : }
453 :
454 : // Output the overflow lines.
455 0 : const FrameLines* overflowLines = GetOverflowLines();
456 0 : if (overflowLines && !overflowLines->mLines.empty()) {
457 0 : IndentBy(out, aIndent);
458 0 : fputs("Overflow-lines<\n", out);
459 0 : const_line_iterator line = overflowLines->mLines.begin(),
460 0 : line_end = overflowLines->mLines.end();
461 0 : for ( ; line != line_end; ++line) {
462 0 : line->List(out, aIndent + 1);
463 : }
464 0 : IndentBy(out, aIndent);
465 0 : fputs(">\n", out);
466 : }
467 :
468 : // skip the principal list - we printed the lines above
469 : // skip the overflow list - we printed the overflow lines above
470 0 : ChildListIterator lists(this);
471 0 : ChildListIDs skip(kPrincipalList | kOverflowList);
472 0 : for (; !lists.IsDone(); lists.Next()) {
473 0 : if (skip.Contains(lists.CurrentID())) {
474 0 : continue;
475 : }
476 0 : IndentBy(out, aIndent);
477 0 : fprintf(out, "%s<\n", mozilla::layout::ChildListName(lists.CurrentID()));
478 0 : nsFrameList::Enumerator childFrames(lists.CurrentList());
479 0 : for (; !childFrames.AtEnd(); childFrames.Next()) {
480 0 : nsIFrame* kid = childFrames.get();
481 0 : kid->List(out, aIndent + 1);
482 : }
483 0 : IndentBy(out, aIndent);
484 0 : fputs(">\n", out);
485 : }
486 :
487 0 : aIndent--;
488 0 : IndentBy(out, aIndent);
489 0 : fputs(">\n", out);
490 :
491 0 : return NS_OK;
492 : }
493 :
494 : NS_IMETHODIMP_(nsFrameState)
495 0 : nsBlockFrame::GetDebugStateBits() const
496 : {
497 : // We don't want to include our cursor flag in the bits the
498 : // regression tester looks at
499 0 : return nsBlockFrameSuper::GetDebugStateBits() & ~NS_BLOCK_HAS_LINE_CURSOR;
500 : }
501 :
502 : NS_IMETHODIMP
503 0 : nsBlockFrame::GetFrameName(nsAString& aResult) const
504 : {
505 0 : return MakeFrameName(NS_LITERAL_STRING("Block"), aResult);
506 : }
507 : #endif
508 :
509 : nsIAtom*
510 0 : nsBlockFrame::GetType() const
511 : {
512 0 : return nsGkAtoms::blockFrame;
513 : }
514 :
515 : void
516 0 : nsBlockFrame::InvalidateInternal(const nsRect& aDamageRect,
517 : nscoord aX, nscoord aY, nsIFrame* aForChild,
518 : PRUint32 aFlags)
519 : {
520 : // Optimize by suppressing invalidation of areas that are clipped out
521 : // with CSS 'clip'. Don't suppress invalidation of *this* frame directly,
522 : // because when 'clip' shrinks we need to invalidate this frame and
523 : // be able to invalidate areas outside the 'clip'.
524 0 : if (aForChild) {
525 0 : const nsStyleDisplay* disp = GetStyleDisplay();
526 0 : nsRect clipRect;
527 0 : if (GetClipPropClipRect(disp, &clipRect, GetSize())) {
528 : // Restrict the invalidated area to abs-pos clip rect
529 : // abs-pos clipping clips everything in the frame
530 0 : nsRect r;
531 0 : if (r.IntersectRect(aDamageRect, clipRect - nsPoint(aX, aY))) {
532 0 : nsBlockFrameSuper::InvalidateInternal(r, aX, aY, this, aFlags);
533 : }
534 : return;
535 : }
536 : }
537 :
538 0 : nsBlockFrameSuper::InvalidateInternal(aDamageRect, aX, aY, this, aFlags);
539 : }
540 :
541 : nscoord
542 0 : nsBlockFrame::GetBaseline() const
543 : {
544 : nscoord result;
545 0 : if (nsLayoutUtils::GetLastLineBaseline(this, &result))
546 0 : return result;
547 0 : return nsFrame::GetBaseline();
548 : }
549 :
550 : nscoord
551 0 : nsBlockFrame::GetCaretBaseline() const
552 : {
553 0 : nsRect contentRect = GetContentRect();
554 0 : nsMargin bp = GetUsedBorderAndPadding();
555 :
556 0 : if (!mLines.empty()) {
557 0 : const_line_iterator line = begin_lines();
558 0 : const nsLineBox* firstLine = line;
559 0 : if (firstLine->GetChildCount()) {
560 0 : return bp.top + firstLine->mFirstChild->GetCaretBaseline();
561 : }
562 : }
563 0 : nsRefPtr<nsFontMetrics> fm;
564 : float inflation =
565 0 : nsLayoutUtils::FontSizeInflationFor(this, nsLayoutUtils::eNotInReflow);
566 0 : nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm), inflation);
567 : return nsLayoutUtils::GetCenteredFontBaseline(fm, nsHTMLReflowState::
568 0 : CalcLineHeight(GetStyleContext(), contentRect.height, inflation)) +
569 0 : bp.top;
570 : }
571 :
572 : /////////////////////////////////////////////////////////////////////////////
573 : // Child frame enumeration
574 :
575 : const nsFrameList&
576 0 : nsBlockFrame::GetChildList(ChildListID aListID) const
577 : {
578 0 : switch (aListID) {
579 : case kPrincipalList:
580 0 : return mFrames;
581 : case kOverflowList: {
582 0 : FrameLines* overflowLines = GetOverflowLines();
583 0 : return overflowLines ? overflowLines->mFrames : nsFrameList::EmptyList();
584 : }
585 : case kFloatList:
586 0 : return mFloats;
587 : case kOverflowOutOfFlowList: {
588 0 : const nsFrameList* list = GetOverflowOutOfFlows();
589 0 : return list ? *list : nsFrameList::EmptyList();
590 : }
591 : case kPushedFloatsList: {
592 0 : const nsFrameList* list = GetPushedFloats();
593 0 : return list ? *list : nsFrameList::EmptyList();
594 : }
595 : case kBulletList: {
596 0 : const nsFrameList* list = GetOutsideBulletList();
597 0 : return list ? *list : nsFrameList::EmptyList();
598 : }
599 : default:
600 0 : return nsContainerFrame::GetChildList(aListID);
601 : }
602 : }
603 :
604 : void
605 0 : nsBlockFrame::GetChildLists(nsTArray<ChildList>* aLists) const
606 : {
607 0 : nsContainerFrame::GetChildLists(aLists);
608 0 : FrameLines* overflowLines = GetOverflowLines();
609 0 : if (overflowLines) {
610 0 : overflowLines->mFrames.AppendIfNonempty(aLists, kOverflowList);
611 : }
612 0 : const nsFrameList* list = GetOverflowOutOfFlows();
613 0 : if (list) {
614 0 : list->AppendIfNonempty(aLists, kOverflowOutOfFlowList);
615 : }
616 0 : mFloats.AppendIfNonempty(aLists, kFloatList);
617 0 : list = GetOutsideBulletList();
618 0 : if (list) {
619 0 : list->AppendIfNonempty(aLists, kBulletList);
620 : }
621 0 : list = GetPushedFloats();
622 0 : if (list) {
623 0 : list->AppendIfNonempty(aLists, kPushedFloatsList);
624 : }
625 0 : }
626 :
627 : /* virtual */ bool
628 0 : nsBlockFrame::IsFloatContainingBlock() const
629 : {
630 0 : return true;
631 : }
632 :
633 : static void
634 0 : ReparentFrame(nsIFrame* aFrame, nsIFrame* aOldParent, nsIFrame* aNewParent)
635 : {
636 0 : NS_ASSERTION(aOldParent == aFrame->GetParent(),
637 : "Parent not consistent with expectations");
638 :
639 0 : aFrame->SetParent(aNewParent);
640 :
641 : // When pushing and pulling frames we need to check for whether any
642 : // views need to be reparented
643 : nsContainerFrame::ReparentFrameView(aFrame->PresContext(), aFrame,
644 0 : aOldParent, aNewParent);
645 0 : }
646 :
647 : static void
648 0 : ReparentFrames(nsFrameList& aFrameList, nsIFrame* aOldParent,
649 : nsIFrame* aNewParent)
650 : {
651 0 : for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
652 0 : ReparentFrame(e.get(), aOldParent, aNewParent);
653 : }
654 0 : }
655 :
656 : /**
657 : * Remove the first line from aFromLines and adjust the associated frame list
658 : * aFromFrames accordingly. The removed line is assigned to *aOutLine and
659 : * a frame list with its frames is assigned to *aOutFrames, i.e. the frames
660 : * that were extracted from the head of aFromFrames.
661 : * aFromLines must contain at least one line, the line may be empty.
662 : * @return true if aFromLines becomes empty
663 : */
664 : static bool
665 0 : RemoveFirstLine(nsLineList& aFromLines, nsFrameList& aFromFrames,
666 : nsLineBox** aOutLine, nsFrameList* aOutFrames)
667 : {
668 0 : nsLineList_iterator removedLine = aFromLines.begin();
669 0 : *aOutLine = removedLine;
670 0 : nsLineList_iterator next = aFromLines.erase(removedLine);
671 0 : bool isLastLine = next == aFromLines.end();
672 : nsIFrame* lastFrame = isLastLine ? aFromFrames.LastChild()
673 0 : : next->mFirstChild->GetPrevSibling();
674 0 : nsFrameList::FrameLinkEnumerator linkToBreak(aFromFrames, lastFrame);
675 0 : *aOutFrames = aFromFrames.ExtractHead(linkToBreak);
676 0 : return isLastLine;
677 : }
678 :
679 : //////////////////////////////////////////////////////////////////////
680 : // Reflow methods
681 :
682 : /* virtual */ void
683 0 : nsBlockFrame::MarkIntrinsicWidthsDirty()
684 : {
685 0 : nsBlockFrame* dirtyBlock = static_cast<nsBlockFrame*>(GetFirstContinuation());
686 0 : dirtyBlock->mMinWidth = NS_INTRINSIC_WIDTH_UNKNOWN;
687 0 : dirtyBlock->mPrefWidth = NS_INTRINSIC_WIDTH_UNKNOWN;
688 0 : if (!(GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION)) {
689 0 : for (nsIFrame* frame = dirtyBlock; frame;
690 0 : frame = frame->GetNextContinuation()) {
691 0 : frame->AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
692 : }
693 : }
694 :
695 0 : nsBlockFrameSuper::MarkIntrinsicWidthsDirty();
696 0 : }
697 :
698 : /* virtual */ nscoord
699 0 : nsBlockFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
700 : {
701 0 : nsIFrame* firstInFlow = GetFirstContinuation();
702 0 : if (firstInFlow != this)
703 0 : return firstInFlow->GetMinWidth(aRenderingContext);
704 :
705 0 : DISPLAY_MIN_WIDTH(this, mMinWidth);
706 0 : if (mMinWidth != NS_INTRINSIC_WIDTH_UNKNOWN)
707 0 : return mMinWidth;
708 :
709 : #ifdef DEBUG
710 0 : if (gNoisyIntrinsic) {
711 0 : IndentBy(stdout, gNoiseIndent);
712 0 : ListTag(stdout);
713 0 : printf(": GetMinWidth\n");
714 : }
715 0 : AutoNoisyIndenter indenter(gNoisyIntrinsic);
716 : #endif
717 :
718 0 : if (GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION)
719 0 : ResolveBidi();
720 0 : InlineMinWidthData data;
721 0 : for (nsBlockFrame* curFrame = this; curFrame;
722 0 : curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) {
723 0 : for (line_iterator line = curFrame->begin_lines(), line_end = curFrame->end_lines();
724 : line != line_end; ++line)
725 : {
726 : #ifdef DEBUG
727 0 : if (gNoisyIntrinsic) {
728 0 : IndentBy(stdout, gNoiseIndent);
729 : printf("line (%s%s)\n",
730 0 : line->IsBlock() ? "block" : "inline",
731 0 : line->IsEmpty() ? ", empty" : "");
732 : }
733 0 : AutoNoisyIndenter lineindent(gNoisyIntrinsic);
734 : #endif
735 0 : if (line->IsBlock()) {
736 0 : data.ForceBreak(aRenderingContext);
737 : data.currentLine = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
738 0 : line->mFirstChild, nsLayoutUtils::MIN_WIDTH);
739 0 : data.ForceBreak(aRenderingContext);
740 : } else {
741 0 : if (!curFrame->GetPrevContinuation() &&
742 0 : line == curFrame->begin_lines()) {
743 : // Only add text-indent if it has no percentages; using a
744 : // percentage basis of 0 unconditionally would give strange
745 : // behavior for calc(10%-3px).
746 0 : const nsStyleCoord &indent = GetStyleText()->mTextIndent;
747 0 : if (indent.ConvertsToLength())
748 0 : data.currentLine += nsRuleNode::ComputeCoordPercentCalc(indent, 0);
749 : }
750 : // XXX Bug NNNNNN Should probably handle percentage text-indent.
751 :
752 0 : data.line = &line;
753 0 : data.lineContainer = curFrame;
754 0 : nsIFrame *kid = line->mFirstChild;
755 0 : for (PRInt32 i = 0, i_end = line->GetChildCount(); i != i_end;
756 : ++i, kid = kid->GetNextSibling()) {
757 0 : kid->AddInlineMinWidth(aRenderingContext, &data);
758 : }
759 : }
760 : #ifdef DEBUG
761 0 : if (gNoisyIntrinsic) {
762 0 : IndentBy(stdout, gNoiseIndent);
763 : printf("min: [prevLines=%d currentLine=%d]\n",
764 0 : data.prevLines, data.currentLine);
765 : }
766 : #endif
767 : }
768 : }
769 0 : data.ForceBreak(aRenderingContext);
770 :
771 0 : mMinWidth = data.prevLines;
772 0 : return mMinWidth;
773 : }
774 :
775 : /* virtual */ nscoord
776 0 : nsBlockFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
777 : {
778 0 : nsIFrame* firstInFlow = GetFirstContinuation();
779 0 : if (firstInFlow != this)
780 0 : return firstInFlow->GetPrefWidth(aRenderingContext);
781 :
782 0 : DISPLAY_PREF_WIDTH(this, mPrefWidth);
783 :
784 0 : if (mPrefWidth != NS_INTRINSIC_WIDTH_UNKNOWN)
785 0 : return mPrefWidth;
786 :
787 : #ifdef DEBUG
788 0 : if (gNoisyIntrinsic) {
789 0 : IndentBy(stdout, gNoiseIndent);
790 0 : ListTag(stdout);
791 0 : printf(": GetPrefWidth\n");
792 : }
793 0 : AutoNoisyIndenter indenter(gNoisyIntrinsic);
794 : #endif
795 :
796 0 : if (GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION)
797 0 : ResolveBidi();
798 0 : InlinePrefWidthData data;
799 0 : for (nsBlockFrame* curFrame = this; curFrame;
800 0 : curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) {
801 0 : for (line_iterator line = curFrame->begin_lines(), line_end = curFrame->end_lines();
802 : line != line_end; ++line)
803 : {
804 : #ifdef DEBUG
805 0 : if (gNoisyIntrinsic) {
806 0 : IndentBy(stdout, gNoiseIndent);
807 : printf("line (%s%s)\n",
808 0 : line->IsBlock() ? "block" : "inline",
809 0 : line->IsEmpty() ? ", empty" : "");
810 : }
811 0 : AutoNoisyIndenter lineindent(gNoisyIntrinsic);
812 : #endif
813 0 : if (line->IsBlock()) {
814 0 : data.ForceBreak(aRenderingContext);
815 : data.currentLine = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
816 0 : line->mFirstChild, nsLayoutUtils::PREF_WIDTH);
817 0 : data.ForceBreak(aRenderingContext);
818 : } else {
819 0 : if (!curFrame->GetPrevContinuation() &&
820 0 : line == curFrame->begin_lines()) {
821 : // Only add text-indent if it has no percentages; using a
822 : // percentage basis of 0 unconditionally would give strange
823 : // behavior for calc(10%-3px).
824 0 : const nsStyleCoord &indent = GetStyleText()->mTextIndent;
825 0 : if (indent.ConvertsToLength())
826 0 : data.currentLine += nsRuleNode::ComputeCoordPercentCalc(indent, 0);
827 : }
828 : // XXX Bug NNNNNN Should probably handle percentage text-indent.
829 :
830 0 : data.line = &line;
831 0 : data.lineContainer = curFrame;
832 0 : nsIFrame *kid = line->mFirstChild;
833 0 : for (PRInt32 i = 0, i_end = line->GetChildCount(); i != i_end;
834 : ++i, kid = kid->GetNextSibling()) {
835 0 : kid->AddInlinePrefWidth(aRenderingContext, &data);
836 : }
837 : }
838 : #ifdef DEBUG
839 0 : if (gNoisyIntrinsic) {
840 0 : IndentBy(stdout, gNoiseIndent);
841 : printf("pref: [prevLines=%d currentLine=%d]\n",
842 0 : data.prevLines, data.currentLine);
843 : }
844 : #endif
845 : }
846 : }
847 0 : data.ForceBreak(aRenderingContext);
848 :
849 0 : mPrefWidth = data.prevLines;
850 0 : return mPrefWidth;
851 : }
852 :
853 : nsRect
854 0 : nsBlockFrame::ComputeTightBounds(gfxContext* aContext) const
855 : {
856 : // be conservative
857 0 : if (GetStyleContext()->HasTextDecorationLines()) {
858 0 : return GetVisualOverflowRect();
859 : }
860 0 : return ComputeSimpleTightBounds(aContext);
861 : }
862 :
863 : static bool
864 0 : AvailableSpaceShrunk(const nsRect& aOldAvailableSpace,
865 : const nsRect& aNewAvailableSpace)
866 : {
867 0 : if (aNewAvailableSpace.width == 0) {
868 : // Positions are not significant if the width is zero.
869 0 : return aOldAvailableSpace.width != 0;
870 : }
871 0 : NS_ASSERTION(aOldAvailableSpace.x <= aNewAvailableSpace.x &&
872 : aOldAvailableSpace.XMost() >= aNewAvailableSpace.XMost(),
873 : "available space should never grow");
874 0 : return aOldAvailableSpace.width != aNewAvailableSpace.width;
875 : }
876 :
877 : static nsSize
878 0 : CalculateContainingBlockSizeForAbsolutes(const nsHTMLReflowState& aReflowState,
879 : nsSize aFrameSize)
880 : {
881 : // The issue here is that for a 'height' of 'auto' the reflow state
882 : // code won't know how to calculate the containing block height
883 : // because it's calculated bottom up. So we use our own computed
884 : // size as the dimensions.
885 0 : nsIFrame* frame = aReflowState.frame;
886 :
887 0 : nsSize cbSize(aFrameSize);
888 : // Containing block is relative to the padding edge
889 : const nsMargin& border =
890 0 : aReflowState.mComputedBorderPadding - aReflowState.mComputedPadding;
891 0 : cbSize.width -= border.LeftRight();
892 0 : cbSize.height -= border.TopBottom();
893 :
894 0 : if (frame->GetParent()->GetContent() == frame->GetContent() &&
895 0 : frame->GetParent()->GetType() != nsGkAtoms::canvasFrame) {
896 : // We are a wrapped frame for the content (and the wrapper is not the
897 : // canvas frame, whose size is not meaningful here).
898 : // Use the container's dimensions, if they have been precomputed.
899 : // XXX This is a hack! We really should be waiting until the outermost
900 : // frame is fully reflowed and using the resulting dimensions, even
901 : // if they're intrinsic.
902 : // In fact we should be attaching absolute children to the outermost
903 : // frame and not always sticking them in block frames.
904 :
905 : // First, find the reflow state for the outermost frame for this
906 : // content.
907 0 : const nsHTMLReflowState* aLastRS = &aReflowState;
908 0 : const nsHTMLReflowState* lastButOneRS = &aReflowState;
909 0 : while (aLastRS->parentReflowState &&
910 0 : aLastRS->parentReflowState->frame->GetContent() == frame->GetContent()) {
911 0 : lastButOneRS = aLastRS;
912 0 : aLastRS = aLastRS->parentReflowState;
913 : }
914 0 : if (aLastRS != &aReflowState) {
915 : // Scrollbars need to be specifically excluded, if present, because they are outside the
916 : // padding-edge. We need better APIs for getting the various boxes from a frame.
917 0 : nsIScrollableFrame* scrollFrame = do_QueryFrame(aLastRS->frame);
918 0 : nsMargin scrollbars(0,0,0,0);
919 0 : if (scrollFrame) {
920 : scrollbars =
921 : scrollFrame->GetDesiredScrollbarSizes(aLastRS->frame->PresContext(),
922 0 : aLastRS->rendContext);
923 0 : if (!lastButOneRS->mFlags.mAssumingHScrollbar) {
924 0 : scrollbars.top = scrollbars.bottom = 0;
925 : }
926 0 : if (!lastButOneRS->mFlags.mAssumingVScrollbar) {
927 0 : scrollbars.left = scrollbars.right = 0;
928 : }
929 : }
930 : // We found a reflow state for the outermost wrapping frame, so use
931 : // its computed metrics if available
932 0 : if (aLastRS->ComputedWidth() != NS_UNCONSTRAINEDSIZE) {
933 : cbSize.width = NS_MAX(0,
934 0 : aLastRS->ComputedWidth() + aLastRS->mComputedPadding.LeftRight() - scrollbars.LeftRight());
935 : }
936 0 : if (aLastRS->ComputedHeight() != NS_UNCONSTRAINEDSIZE) {
937 : cbSize.height = NS_MAX(0,
938 0 : aLastRS->ComputedHeight() + aLastRS->mComputedPadding.TopBottom() - scrollbars.TopBottom());
939 : }
940 : }
941 : }
942 :
943 : return cbSize;
944 : }
945 :
946 : NS_IMETHODIMP
947 0 : nsBlockFrame::Reflow(nsPresContext* aPresContext,
948 : nsHTMLReflowMetrics& aMetrics,
949 : const nsHTMLReflowState& aReflowState,
950 : nsReflowStatus& aStatus)
951 : {
952 0 : DO_GLOBAL_REFLOW_COUNT("nsBlockFrame");
953 0 : DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
954 : #ifdef DEBUG
955 0 : if (gNoisyReflow) {
956 0 : IndentBy(stdout, gNoiseIndent);
957 0 : ListTag(stdout);
958 : printf(": begin reflow availSize=%d,%d computedSize=%d,%d\n",
959 : aReflowState.availableWidth, aReflowState.availableHeight,
960 0 : aReflowState.ComputedWidth(), aReflowState.ComputedHeight());
961 : }
962 0 : AutoNoisyIndenter indent(gNoisy);
963 0 : PRTime start = LL_ZERO; // Initialize these variablies to silence the compiler.
964 0 : PRInt32 ctc = 0; // We only use these if they are set (gLameReflowMetrics).
965 0 : if (gLameReflowMetrics) {
966 0 : start = PR_Now();
967 0 : ctc = nsLineBox::GetCtorCount();
968 : }
969 : #endif
970 :
971 0 : const nsHTMLReflowState *reflowState = &aReflowState;
972 0 : nsAutoPtr<nsHTMLReflowState> mutableReflowState;
973 : // If we have non-auto height, we're clipping our kids and we fit,
974 : // make sure our kids fit too.
975 0 : if (aReflowState.availableHeight != NS_UNCONSTRAINEDSIZE &&
976 0 : aReflowState.ComputedHeight() != NS_AUTOHEIGHT &&
977 0 : ApplyOverflowClipping(this, aReflowState.mStyleDisplay)) {
978 0 : nsMargin heightExtras = aReflowState.mComputedBorderPadding;
979 0 : if (GetSkipSides() & NS_SIDE_TOP) {
980 0 : heightExtras.top = 0;
981 : } else {
982 : // Bottom margin never causes us to create continuations, so we
983 : // don't need to worry about whether it fits in its entirety.
984 0 : heightExtras.top += aReflowState.mComputedMargin.top;
985 : }
986 :
987 0 : if (GetEffectiveComputedHeight(aReflowState) + heightExtras.TopBottom() <=
988 : aReflowState.availableHeight) {
989 0 : mutableReflowState = new nsHTMLReflowState(aReflowState);
990 0 : mutableReflowState->availableHeight = NS_UNCONSTRAINEDSIZE;
991 0 : reflowState = mutableReflowState;
992 : }
993 : }
994 :
995 : // See comment below about oldSize. Use *only* for the
996 : // abs-pos-containing-block-size-change optimization!
997 0 : nsSize oldSize = GetSize();
998 :
999 : // Should we create a float manager?
1000 0 : nsAutoFloatManager autoFloatManager(const_cast<nsHTMLReflowState&>(*reflowState));
1001 :
1002 : // XXXldb If we start storing the float manager in the frame rather
1003 : // than keeping it around only during reflow then we should create it
1004 : // only when there are actually floats to manage. Otherwise things
1005 : // like tables will gain significant bloat.
1006 0 : bool needFloatManager = nsBlockFrame::BlockNeedsFloatManager(this);
1007 0 : if (needFloatManager)
1008 0 : autoFloatManager.CreateFloatManager(aPresContext);
1009 :
1010 : // OK, some lines may be reflowed. Blow away any saved line cursor
1011 : // because we may invalidate the nondecreasing
1012 : // overflowArea.VisualOverflow().y/yMost invariant, and we may even
1013 : // delete the line with the line cursor.
1014 0 : ClearLineCursor();
1015 :
1016 0 : if (IsFrameTreeTooDeep(*reflowState, aMetrics, aStatus)) {
1017 0 : return NS_OK;
1018 : }
1019 :
1020 0 : bool marginRoot = BlockIsMarginRoot(this);
1021 : nsBlockReflowState state(*reflowState, aPresContext, this, aMetrics,
1022 0 : marginRoot, marginRoot, needFloatManager);
1023 :
1024 : #ifdef IBMBIDI
1025 0 : if (GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION)
1026 0 : static_cast<nsBlockFrame*>(GetFirstContinuation())->ResolveBidi();
1027 : #endif // IBMBIDI
1028 :
1029 0 : if (RenumberLists(aPresContext)) {
1030 0 : AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
1031 : }
1032 :
1033 0 : nsresult rv = NS_OK;
1034 :
1035 : // ALWAYS drain overflow. We never want to leave the previnflow's
1036 : // overflow lines hanging around; block reflow depends on the
1037 : // overflow line lists being cleared out between reflow passes.
1038 0 : DrainOverflowLines();
1039 :
1040 : // Handle paginated overflow (see nsContainerFrame.h)
1041 0 : nsOverflowAreas ocBounds;
1042 0 : nsReflowStatus ocStatus = NS_FRAME_COMPLETE;
1043 0 : if (GetPrevInFlow()) {
1044 : ReflowOverflowContainerChildren(aPresContext, *reflowState, ocBounds, 0,
1045 0 : ocStatus);
1046 : }
1047 :
1048 : // Now that we're done cleaning up our overflow container lists, we can
1049 : // give |state| its nsOverflowContinuationTracker.
1050 0 : nsOverflowContinuationTracker tracker(aPresContext, this, false);
1051 0 : state.mOverflowTracker = &tracker;
1052 :
1053 : // Drain & handle pushed floats
1054 0 : DrainPushedFloats(state);
1055 0 : nsOverflowAreas fcBounds;
1056 0 : nsReflowStatus fcStatus = NS_FRAME_COMPLETE;
1057 0 : rv = ReflowPushedFloats(state, fcBounds, fcStatus);
1058 0 : NS_ENSURE_SUCCESS(rv, rv);
1059 :
1060 : // If we're not dirty (which means we'll mark everything dirty later)
1061 : // and our width has changed, mark the lines dirty that we need to
1062 : // mark dirty for a resize reflow.
1063 0 : if (reflowState->mFlags.mHResize)
1064 0 : PrepareResizeReflow(state);
1065 :
1066 0 : mState &= ~NS_FRAME_FIRST_REFLOW;
1067 :
1068 : // Now reflow...
1069 0 : rv = ReflowDirtyLines(state);
1070 0 : NS_ASSERTION(NS_SUCCEEDED(rv), "reflow dirty lines failed");
1071 0 : if (NS_FAILED(rv)) return rv;
1072 :
1073 0 : NS_MergeReflowStatusInto(&state.mReflowStatus, ocStatus);
1074 0 : NS_MergeReflowStatusInto(&state.mReflowStatus, fcStatus);
1075 :
1076 : // If we end in a BR with clear and affected floats continue,
1077 : // we need to continue, too.
1078 0 : if (NS_UNCONSTRAINEDSIZE != reflowState->availableHeight &&
1079 : NS_FRAME_IS_COMPLETE(state.mReflowStatus) &&
1080 0 : state.mFloatManager->ClearContinues(FindTrailingClear())) {
1081 0 : NS_FRAME_SET_INCOMPLETE(state.mReflowStatus);
1082 : }
1083 :
1084 0 : if (!NS_FRAME_IS_FULLY_COMPLETE(state.mReflowStatus)) {
1085 0 : if (HasOverflowLines() || HasPushedFloats()) {
1086 0 : state.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
1087 : }
1088 :
1089 : #ifdef DEBUG_kipp
1090 : ListTag(stdout); printf(": block is not fully complete\n");
1091 : #endif
1092 : }
1093 :
1094 0 : CheckFloats(state);
1095 :
1096 : // Place the "marker" (bullet) frame if it is placed next to a block
1097 : // child.
1098 : //
1099 : // According to the CSS2 spec, section 12.6.1, the "marker" box
1100 : // participates in the height calculation of the list-item box's
1101 : // first line box.
1102 : //
1103 : // There are exactly two places a bullet can be placed: near the
1104 : // first or second line. It's only placed on the second line in a
1105 : // rare case: an empty first line followed by a second line that
1106 : // contains a block (example: <LI>\n<P>... ). This is where
1107 : // the second case can happen.
1108 0 : if (HasOutsideBullet() && !mLines.empty() &&
1109 0 : (mLines.front()->IsBlock() ||
1110 0 : (0 == mLines.front()->mBounds.height &&
1111 0 : mLines.front() != mLines.back() &&
1112 0 : mLines.begin().next()->IsBlock()))) {
1113 : // Reflow the bullet
1114 0 : nsHTMLReflowMetrics metrics;
1115 : // XXX Use the entire line when we fix bug 25888.
1116 : nsLayoutUtils::LinePosition position;
1117 0 : bool havePosition = nsLayoutUtils::GetFirstLinePosition(this, &position);
1118 : nscoord lineTop = havePosition ? position.mTop
1119 0 : : reflowState->mComputedBorderPadding.top;
1120 0 : nsIFrame* bullet = GetOutsideBullet();
1121 0 : ReflowBullet(bullet, state, metrics, lineTop);
1122 0 : NS_ASSERTION(!BulletIsEmpty() || metrics.height == 0,
1123 : "empty bullet took up space");
1124 :
1125 0 : if (havePosition && !BulletIsEmpty()) {
1126 : // We have some lines to align the bullet with.
1127 :
1128 : // Doing the alignment using the baseline will also cater for
1129 : // bullets that are placed next to a child block (bug 92896)
1130 :
1131 : // Tall bullets won't look particularly nice here...
1132 0 : nsRect bbox = bullet->GetRect();
1133 0 : bbox.y = position.mBaseline - metrics.ascent;
1134 0 : bullet->SetRect(bbox);
1135 : }
1136 : // Otherwise just leave the bullet where it is, up against our top padding.
1137 : }
1138 :
1139 : // Compute our final size
1140 : nscoord bottomEdgeOfChildren;
1141 0 : ComputeFinalSize(*reflowState, state, aMetrics, &bottomEdgeOfChildren);
1142 0 : nsRect areaBounds = nsRect(0, 0, aMetrics.width, aMetrics.height);
1143 : ComputeOverflowAreas(areaBounds, reflowState->mStyleDisplay,
1144 0 : bottomEdgeOfChildren, aMetrics.mOverflowAreas);
1145 : // Factor overflow container child bounds into the overflow area
1146 0 : aMetrics.mOverflowAreas.UnionWith(ocBounds);
1147 : // Factor pushed float child bounds into the overflow area
1148 0 : aMetrics.mOverflowAreas.UnionWith(fcBounds);
1149 :
1150 : // Let the absolutely positioned container reflow any absolutely positioned
1151 : // child frames that need to be reflowed, e.g., elements with a percentage
1152 : // based width/height
1153 : // We want to do this under either of two conditions:
1154 : // 1. If we didn't do the incremental reflow above.
1155 : // 2. If our size changed.
1156 : // Even though it's the padding edge that's the containing block, we
1157 : // can use our rect (the border edge) since if the border style
1158 : // changed, the reflow would have been targeted at us so we'd satisfy
1159 : // condition 1.
1160 : // XXX checking oldSize is bogus, there are various reasons we might have
1161 : // reflowed but our size might not have been changed to what we
1162 : // asked for (e.g., we ended up being pushed to a new page)
1163 : // When WillReflowAgainForClearance is true, we will reflow again without
1164 : // resetting the size. Because of this, we must not reflow our abs-pos children
1165 : // in that situation --- what we think is our "new size"
1166 : // will not be our real new size. This also happens to be more efficient.
1167 0 : if (HasAbsolutelyPositionedChildren()) {
1168 0 : nsAbsoluteContainingBlock* absoluteContainer = GetAbsoluteContainingBlock();
1169 0 : bool haveInterrupt = aPresContext->HasPendingInterrupt();
1170 0 : if (reflowState->WillReflowAgainForClearance() ||
1171 : haveInterrupt) {
1172 : // Make sure that when we reflow again we'll actually reflow all the abs
1173 : // pos frames that might conceivably depend on our size (or all of them,
1174 : // if we're dirty right now and interrupted; in that case we also need
1175 : // to mark them all with NS_FRAME_IS_DIRTY). Sadly, we can't do much
1176 : // better than that, because we don't really know what our size will be,
1177 : // and it might in fact not change on the followup reflow!
1178 0 : if (haveInterrupt && (GetStateBits() & NS_FRAME_IS_DIRTY)) {
1179 0 : absoluteContainer->MarkAllFramesDirty();
1180 : } else {
1181 0 : absoluteContainer->MarkSizeDependentFramesDirty();
1182 : }
1183 : } else {
1184 : nsSize containingBlockSize =
1185 : CalculateContainingBlockSizeForAbsolutes(*reflowState,
1186 : nsSize(aMetrics.width,
1187 0 : aMetrics.height));
1188 :
1189 : // Mark frames that depend on changes we just made to this frame as dirty:
1190 : // Now we can assume that the padding edge hasn't moved.
1191 : // We need to reflow the absolutes if one of them depends on
1192 : // its placeholder position, or the containing block size in a
1193 : // direction in which the containing block size might have
1194 : // changed.
1195 0 : bool cbWidthChanged = aMetrics.width != oldSize.width;
1196 0 : bool isRoot = !GetContent()->GetParent();
1197 : // If isRoot and we have auto height, then we are the initial
1198 : // containing block and the containing block height is the
1199 : // viewport height, which can't change during incremental
1200 : // reflow.
1201 : bool cbHeightChanged =
1202 0 : !(isRoot && NS_UNCONSTRAINEDSIZE == reflowState->ComputedHeight()) &&
1203 0 : aMetrics.height != oldSize.height;
1204 :
1205 : absoluteContainer->Reflow(this, aPresContext, *reflowState,
1206 : state.mReflowStatus,
1207 : containingBlockSize.width,
1208 : containingBlockSize.height, true,
1209 : cbWidthChanged, cbHeightChanged,
1210 0 : &aMetrics.mOverflowAreas);
1211 :
1212 : //XXXfr Why isn't this rv (and others in this file) checked/returned?
1213 : }
1214 : }
1215 :
1216 : // Determine if we need to repaint our border, background or outline
1217 0 : CheckInvalidateSizeChange(aMetrics);
1218 :
1219 0 : FinishAndStoreOverflow(&aMetrics);
1220 :
1221 : // Clear the float manager pointer in the block reflow state so we
1222 : // don't waste time translating the coordinate system back on a dead
1223 : // float manager.
1224 0 : if (needFloatManager)
1225 0 : state.mFloatManager = nsnull;
1226 :
1227 0 : aStatus = state.mReflowStatus;
1228 :
1229 : #ifdef DEBUG
1230 : // Between when we drain pushed floats and when we complete reflow,
1231 : // we're allowed to have multiple continuations of the same float on
1232 : // our floats list, since a first-in-flow might get pushed to a later
1233 : // continuation of its containing block. But it's not permitted
1234 : // outside that time.
1235 0 : nsLayoutUtils::AssertNoDuplicateContinuations(this, mFloats);
1236 :
1237 0 : if (gNoisyReflow) {
1238 0 : IndentBy(stdout, gNoiseIndent);
1239 0 : ListTag(stdout);
1240 : printf(": status=%x (%scomplete) metrics=%d,%d carriedMargin=%d",
1241 : aStatus, NS_FRAME_IS_COMPLETE(aStatus) ? "" : "not ",
1242 : aMetrics.width, aMetrics.height,
1243 0 : aMetrics.mCarriedOutBottomMargin.get());
1244 0 : if (HasOverflowAreas()) {
1245 : printf(" overflow-vis={%d,%d,%d,%d}",
1246 0 : aMetrics.VisualOverflow().x,
1247 0 : aMetrics.VisualOverflow().y,
1248 0 : aMetrics.VisualOverflow().width,
1249 0 : aMetrics.VisualOverflow().height);
1250 : printf(" overflow-scr={%d,%d,%d,%d}",
1251 0 : aMetrics.ScrollableOverflow().x,
1252 0 : aMetrics.ScrollableOverflow().y,
1253 0 : aMetrics.ScrollableOverflow().width,
1254 0 : aMetrics.ScrollableOverflow().height);
1255 : }
1256 0 : printf("\n");
1257 : }
1258 :
1259 0 : if (gLameReflowMetrics) {
1260 0 : PRTime end = PR_Now();
1261 :
1262 0 : PRInt32 ectc = nsLineBox::GetCtorCount();
1263 0 : PRInt32 numLines = mLines.size();
1264 0 : if (!numLines) numLines = 1;
1265 : PRTime delta, perLineDelta, lines;
1266 0 : LL_I2L(lines, numLines);
1267 0 : LL_SUB(delta, end, start);
1268 0 : LL_DIV(perLineDelta, delta, lines);
1269 :
1270 0 : ListTag(stdout);
1271 : char buf[400];
1272 : PR_snprintf(buf, sizeof(buf),
1273 : ": %lld elapsed (%lld per line) (%d lines; %d new lines)",
1274 0 : delta, perLineDelta, numLines, ectc - ctc);
1275 0 : printf("%s\n", buf);
1276 : }
1277 : #endif
1278 :
1279 0 : NS_FRAME_SET_TRUNCATION(aStatus, (*reflowState), aMetrics);
1280 0 : return rv;
1281 : }
1282 :
1283 : bool
1284 0 : nsBlockFrame::CheckForCollapsedBottomMarginFromClearanceLine()
1285 : {
1286 0 : line_iterator begin = begin_lines();
1287 0 : line_iterator line = end_lines();
1288 :
1289 0 : while (true) {
1290 0 : if (begin == line) {
1291 0 : return false;
1292 : }
1293 0 : --line;
1294 0 : if (line->mBounds.height != 0 || !line->CachedIsEmpty()) {
1295 0 : return false;
1296 : }
1297 0 : if (line->HasClearance()) {
1298 0 : return true;
1299 : }
1300 : }
1301 : // not reached
1302 : }
1303 :
1304 : void
1305 0 : nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState,
1306 : nsBlockReflowState& aState,
1307 : nsHTMLReflowMetrics& aMetrics,
1308 : nscoord* aBottomEdgeOfChildren)
1309 : {
1310 0 : const nsMargin& borderPadding = aState.BorderPadding();
1311 : #ifdef NOISY_FINAL_SIZE
1312 : ListTag(stdout);
1313 : printf(": mY=%d mIsBottomMarginRoot=%s mPrevBottomMargin=%d bp=%d,%d\n",
1314 : aState.mY, aState.GetFlag(BRS_ISBOTTOMMARGINROOT) ? "yes" : "no",
1315 : aState.mPrevBottomMargin,
1316 : borderPadding.top, borderPadding.bottom);
1317 : #endif
1318 :
1319 : // Compute final width
1320 : aMetrics.width =
1321 : NSCoordSaturatingAdd(NSCoordSaturatingAdd(borderPadding.left,
1322 : aReflowState.ComputedWidth()),
1323 0 : borderPadding.right);
1324 :
1325 : // Return bottom margin information
1326 : // rbs says he hit this assertion occasionally (see bug 86947), so
1327 : // just set the margin to zero and we'll figure out why later
1328 : //NS_ASSERTION(aMetrics.mCarriedOutBottomMargin.IsZero(),
1329 : // "someone else set the margin");
1330 0 : nscoord nonCarriedOutVerticalMargin = 0;
1331 0 : if (!aState.GetFlag(BRS_ISBOTTOMMARGINROOT)) {
1332 : // Apply rule from CSS 2.1 section 8.3.1. If we have some empty
1333 : // line with clearance and a non-zero top margin and all
1334 : // subsequent lines are empty, then we do not allow our children's
1335 : // carried out bottom margin to be carried out of us and collapse
1336 : // with our own bottom margin.
1337 0 : if (CheckForCollapsedBottomMarginFromClearanceLine()) {
1338 : // Convert the children's carried out margin to something that
1339 : // we will include in our height
1340 0 : nonCarriedOutVerticalMargin = aState.mPrevBottomMargin.get();
1341 0 : aState.mPrevBottomMargin.Zero();
1342 : }
1343 0 : aMetrics.mCarriedOutBottomMargin = aState.mPrevBottomMargin;
1344 : } else {
1345 0 : aMetrics.mCarriedOutBottomMargin.Zero();
1346 : }
1347 :
1348 0 : nscoord bottomEdgeOfChildren = aState.mY + nonCarriedOutVerticalMargin;
1349 : // Shrink wrap our height around our contents.
1350 0 : if (aState.GetFlag(BRS_ISBOTTOMMARGINROOT) ||
1351 0 : NS_UNCONSTRAINEDSIZE != aReflowState.ComputedHeight()) {
1352 : // When we are a bottom-margin root make sure that our last
1353 : // childs bottom margin is fully applied. We also do this when
1354 : // we have a computed height, since in that case the carried out
1355 : // margin is not going to be applied anywhere, so we should note it
1356 : // here to be included in the overflow area.
1357 : // Apply the margin only if there's space for it.
1358 0 : if (bottomEdgeOfChildren < aState.mReflowState.availableHeight)
1359 : {
1360 : // Truncate bottom margin if it doesn't fit to our available height.
1361 : bottomEdgeOfChildren =
1362 0 : NS_MIN(bottomEdgeOfChildren + aState.mPrevBottomMargin.get(),
1363 0 : aState.mReflowState.availableHeight);
1364 : }
1365 : }
1366 0 : if (aState.GetFlag(BRS_FLOAT_MGR)) {
1367 : // Include the float manager's state to properly account for the
1368 : // bottom margin of any floated elements; e.g., inside a table cell.
1369 : nscoord floatHeight =
1370 : aState.ClearFloats(bottomEdgeOfChildren, NS_STYLE_CLEAR_LEFT_AND_RIGHT,
1371 0 : nsnull, nsFloatManager::DONT_CLEAR_PUSHED_FLOATS);
1372 0 : bottomEdgeOfChildren = NS_MAX(bottomEdgeOfChildren, floatHeight);
1373 : }
1374 :
1375 : // Compute final height
1376 0 : if (NS_UNCONSTRAINEDSIZE != aReflowState.ComputedHeight()) {
1377 : // Figure out how much of the computed height should be
1378 : // applied to this frame.
1379 0 : nscoord computedHeightLeftOver = GetEffectiveComputedHeight(aReflowState);
1380 0 : NS_ASSERTION(!( IS_TRUE_OVERFLOW_CONTAINER(this)
1381 : && computedHeightLeftOver ),
1382 : "overflow container must not have computedHeightLeftOver");
1383 :
1384 : aMetrics.height =
1385 : NSCoordSaturatingAdd(NSCoordSaturatingAdd(borderPadding.top,
1386 : computedHeightLeftOver),
1387 0 : borderPadding.bottom);
1388 :
1389 0 : if (NS_FRAME_IS_NOT_COMPLETE(aState.mReflowStatus)
1390 : && aMetrics.height < aReflowState.availableHeight) {
1391 : // We ran out of height on this page but we're incomplete
1392 : // Set status to complete except for overflow
1393 0 : NS_FRAME_SET_OVERFLOW_INCOMPLETE(aState.mReflowStatus);
1394 : }
1395 :
1396 0 : if (NS_FRAME_IS_COMPLETE(aState.mReflowStatus)) {
1397 0 : if (computedHeightLeftOver > 0 &&
1398 : NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight &&
1399 : aMetrics.height > aReflowState.availableHeight) {
1400 : // We don't fit and we consumed some of the computed height,
1401 : // so we should consume all the available height and then
1402 : // break. If our bottom border/padding straddles the break
1403 : // point, then this will increase our height and push the
1404 : // border/padding to the next page/column.
1405 : aMetrics.height = NS_MAX(aReflowState.availableHeight,
1406 0 : aState.mY + nonCarriedOutVerticalMargin);
1407 0 : NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
1408 0 : if (!GetNextInFlow())
1409 0 : aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
1410 : }
1411 : }
1412 : else {
1413 : // Use the current height; continuations will take up the rest.
1414 : // Do extend the height to at least consume the available
1415 : // height, otherwise our left/right borders (for example) won't
1416 : // extend all the way to the break.
1417 : aMetrics.height = NS_MAX(aReflowState.availableHeight,
1418 0 : aState.mY + nonCarriedOutVerticalMargin);
1419 : // ... but don't take up more height than is available
1420 : aMetrics.height = NS_MIN(aMetrics.height,
1421 0 : borderPadding.top + computedHeightLeftOver);
1422 : // XXX It's pretty wrong that our bottom border still gets drawn on
1423 : // on its own on the last-in-flow, even if we ran out of height
1424 : // here. We need GetSkipSides to check whether we ran out of content
1425 : // height in the current frame, not whether it's last-in-flow.
1426 : }
1427 :
1428 : // Don't carry out a bottom margin when our height is fixed.
1429 0 : aMetrics.mCarriedOutBottomMargin.Zero();
1430 : }
1431 0 : else if (NS_FRAME_IS_COMPLETE(aState.mReflowStatus)) {
1432 0 : nscoord autoHeight = bottomEdgeOfChildren;
1433 0 : autoHeight -= borderPadding.top;
1434 0 : nscoord oldAutoHeight = autoHeight;
1435 0 : aReflowState.ApplyMinMaxConstraints(nsnull, &autoHeight);
1436 0 : if (autoHeight != oldAutoHeight) {
1437 : // Our min-height or max-height made our height change. Don't carry out
1438 : // our kids' bottom margins.
1439 0 : aMetrics.mCarriedOutBottomMargin.Zero();
1440 : }
1441 0 : autoHeight += borderPadding.top + borderPadding.bottom;
1442 0 : aMetrics.height = autoHeight;
1443 : }
1444 : else {
1445 0 : NS_ASSERTION(aReflowState.availableHeight != NS_UNCONSTRAINEDSIZE,
1446 : "Shouldn't be incomplete if availableHeight is UNCONSTRAINED.");
1447 0 : aMetrics.height = NS_MAX(aState.mY, aReflowState.availableHeight);
1448 0 : if (aReflowState.availableHeight == NS_UNCONSTRAINEDSIZE)
1449 : // This should never happen, but it does. See bug 414255
1450 0 : aMetrics.height = aState.mY;
1451 : }
1452 :
1453 0 : if (IS_TRUE_OVERFLOW_CONTAINER(this) &&
1454 : NS_FRAME_IS_NOT_COMPLETE(aState.mReflowStatus)) {
1455 : // Overflow containers can only be overflow complete.
1456 : // Note that auto height overflow containers have no normal children
1457 0 : NS_ASSERTION(aMetrics.height == 0, "overflow containers must be zero-height");
1458 0 : NS_FRAME_SET_OVERFLOW_INCOMPLETE(aState.mReflowStatus);
1459 : }
1460 :
1461 : // Screen out negative heights --- can happen due to integer overflows :-(
1462 0 : aMetrics.height = NS_MAX(0, aMetrics.height);
1463 0 : *aBottomEdgeOfChildren = bottomEdgeOfChildren;
1464 :
1465 : #ifdef DEBUG_blocks
1466 : if (CRAZY_WIDTH(aMetrics.width) || CRAZY_HEIGHT(aMetrics.height)) {
1467 : ListTag(stdout);
1468 : printf(": WARNING: desired:%d,%d\n", aMetrics.width, aMetrics.height);
1469 : }
1470 : #endif
1471 0 : }
1472 :
1473 : void
1474 0 : nsBlockFrame::ComputeOverflowAreas(const nsRect& aBounds,
1475 : const nsStyleDisplay* aDisplay,
1476 : nscoord aBottomEdgeOfChildren,
1477 : nsOverflowAreas& aOverflowAreas)
1478 : {
1479 : // Compute the overflow areas of our children
1480 : // XXX_perf: This can be done incrementally. It is currently one of
1481 : // the things that makes incremental reflow O(N^2).
1482 0 : nsOverflowAreas areas(aBounds, aBounds);
1483 0 : if (!ApplyOverflowClipping(this, aDisplay)) {
1484 0 : for (line_iterator line = begin_lines(), line_end = end_lines();
1485 : line != line_end;
1486 : ++line) {
1487 0 : areas.UnionWith(line->GetOverflowAreas());
1488 : }
1489 :
1490 : // Factor an outside bullet in; normally the bullet will be factored into
1491 : // the line-box's overflow areas. However, if the line is a block
1492 : // line then it won't; if there are no lines, it won't. So just
1493 : // factor it in anyway (it can't hurt if it was already done).
1494 : // XXXldb Can we just fix GetOverflowArea instead?
1495 0 : nsIFrame* outsideBullet = GetOutsideBullet();
1496 0 : if (outsideBullet) {
1497 0 : areas.UnionAllWith(outsideBullet->GetRect());
1498 : }
1499 :
1500 : // Factor in the bottom edge of the children. Child frames will be added
1501 : // to the overflow area as we iterate through the lines, but their margins
1502 : // won't, so we need to account for bottom margins here.
1503 : // REVIEW: For now, we do this for both visual and scrollable area,
1504 : // although when we make scrollable overflow area not be a subset of
1505 : // visual, we can change this.
1506 0 : NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
1507 0 : nsRect& o = areas.Overflow(otype);
1508 0 : o.height = NS_MAX(o.YMost(), aBottomEdgeOfChildren) - o.y;
1509 : }
1510 : }
1511 : #ifdef NOISY_COMBINED_AREA
1512 : ListTag(stdout);
1513 : printf(": ca=%d,%d,%d,%d\n", area.x, area.y, area.width, area.height);
1514 : #endif
1515 :
1516 0 : aOverflowAreas = areas;
1517 0 : }
1518 :
1519 : bool
1520 0 : nsBlockFrame::UpdateOverflow()
1521 : {
1522 : // We need to update the overflow areas of lines manually, as they
1523 : // get cached and re-used otherwise. Lines aren't exposed as normal
1524 : // frame children, so calling UnionChildOverflow alone will end up
1525 : // using the old cached values.
1526 0 : for (line_iterator line = begin_lines(), line_end = end_lines();
1527 : line != line_end;
1528 : ++line) {
1529 0 : nsOverflowAreas lineAreas;
1530 :
1531 0 : PRInt32 n = line->GetChildCount();
1532 0 : for (nsIFrame* lineFrame = line->mFirstChild;
1533 : n > 0; lineFrame = lineFrame->GetNextSibling(), --n) {
1534 0 : ConsiderChildOverflow(lineAreas, lineFrame);
1535 : }
1536 :
1537 0 : line->SetOverflowAreas(lineAreas);
1538 : }
1539 :
1540 0 : return nsBlockFrameSuper::UpdateOverflow();
1541 : }
1542 :
1543 : nsresult
1544 0 : nsBlockFrame::MarkLineDirty(line_iterator aLine, const nsLineList* aLineList)
1545 : {
1546 : // Mark aLine dirty
1547 0 : aLine->MarkDirty();
1548 0 : aLine->SetInvalidateTextRuns(true);
1549 : #ifdef DEBUG
1550 0 : if (gNoisyReflow) {
1551 0 : IndentBy(stdout, gNoiseIndent);
1552 0 : ListTag(stdout);
1553 0 : printf(": mark line %p dirty\n", static_cast<void*>(aLine.get()));
1554 : }
1555 : #endif
1556 :
1557 : // Mark previous line dirty if it's an inline line so that it can
1558 : // maybe pullup something from the line just affected.
1559 : // XXX We don't need to do this if aPrevLine ends in a break-after...
1560 0 : if (aLine != (aLineList ? aLineList : &mLines)->front() &&
1561 0 : aLine->IsInline() &&
1562 0 : aLine.prev()->IsInline()) {
1563 0 : aLine.prev()->MarkDirty();
1564 0 : aLine.prev()->SetInvalidateTextRuns(true);
1565 : #ifdef DEBUG
1566 0 : if (gNoisyReflow) {
1567 0 : IndentBy(stdout, gNoiseIndent);
1568 0 : ListTag(stdout);
1569 : printf(": mark prev-line %p dirty\n",
1570 0 : static_cast<void*>(aLine.prev().get()));
1571 : }
1572 : #endif
1573 : }
1574 :
1575 0 : return NS_OK;
1576 : }
1577 :
1578 : /**
1579 : * Test whether lines are certain to be aligned left so that we can make
1580 : * resizing optimizations
1581 : */
1582 0 : bool static inline IsAlignedLeft(const PRUint8 aAlignment,
1583 : const PRUint8 aDirection,
1584 : const PRUint8 aUnicodeBidi)
1585 : {
1586 : return (NS_STYLE_TEXT_ALIGN_LEFT == aAlignment ||
1587 : ((NS_STYLE_TEXT_ALIGN_DEFAULT == aAlignment &&
1588 : NS_STYLE_DIRECTION_LTR == aDirection) ||
1589 : (NS_STYLE_TEXT_ALIGN_END == aAlignment &&
1590 : NS_STYLE_DIRECTION_RTL == aDirection)) &&
1591 0 : !(NS_STYLE_UNICODE_BIDI_PLAINTEXT & aUnicodeBidi));
1592 : }
1593 :
1594 : nsresult
1595 0 : nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState)
1596 : {
1597 0 : const nsStyleText* styleText = GetStyleText();
1598 0 : const nsStyleTextReset* styleTextReset = GetStyleTextReset();
1599 : // See if we can try and avoid marking all the lines as dirty
1600 : bool tryAndSkipLines =
1601 : // The text must be left-aligned.
1602 : IsAlignedLeft(styleText->mTextAlign,
1603 : aState.mReflowState.mStyleVisibility->mDirection,
1604 0 : styleTextReset->mUnicodeBidi) &&
1605 : // The left content-edge must be a constant distance from the left
1606 : // border-edge.
1607 0 : !GetStylePadding()->mPadding.GetLeft().HasPercent();
1608 :
1609 : #ifdef DEBUG
1610 0 : if (gDisableResizeOpt) {
1611 0 : tryAndSkipLines = false;
1612 : }
1613 0 : if (gNoisyReflow) {
1614 0 : if (!tryAndSkipLines) {
1615 0 : IndentBy(stdout, gNoiseIndent);
1616 0 : ListTag(stdout);
1617 : printf(": marking all lines dirty: availWidth=%d textAlign=%d\n",
1618 : aState.mReflowState.availableWidth,
1619 0 : styleText->mTextAlign);
1620 : }
1621 : }
1622 : #endif
1623 :
1624 0 : if (tryAndSkipLines) {
1625 : nscoord newAvailWidth = aState.mReflowState.mComputedBorderPadding.left +
1626 0 : aState.mReflowState.ComputedWidth();
1627 0 : NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mReflowState.mComputedBorderPadding.left &&
1628 : NS_UNCONSTRAINEDSIZE != aState.mReflowState.ComputedWidth(),
1629 : "math on NS_UNCONSTRAINEDSIZE");
1630 :
1631 : #ifdef DEBUG
1632 0 : if (gNoisyReflow) {
1633 0 : IndentBy(stdout, gNoiseIndent);
1634 0 : ListTag(stdout);
1635 0 : printf(": trying to avoid marking all lines dirty\n");
1636 : }
1637 : #endif
1638 :
1639 : // The last line might not be aligned left even if the rest of the block is
1640 : bool skipLastLine = NS_STYLE_TEXT_ALIGN_AUTO == styleText->mTextAlignLast ||
1641 : IsAlignedLeft(styleText->mTextAlignLast,
1642 : aState.mReflowState.mStyleVisibility->mDirection,
1643 0 : styleTextReset->mUnicodeBidi);
1644 :
1645 0 : for (line_iterator line = begin_lines(), line_end = end_lines();
1646 : line != line_end;
1647 : ++line)
1648 : {
1649 : // We let child blocks make their own decisions the same
1650 : // way we are here.
1651 0 : bool isLastLine = line == mLines.back() && !GetNextInFlow();
1652 0 : if (line->IsBlock() ||
1653 0 : line->HasFloats() ||
1654 0 : (!isLastLine && !line->HasBreakAfter()) ||
1655 0 : ((isLastLine || !line->IsLineWrapped()) && !skipLastLine) ||
1656 0 : line->ResizeReflowOptimizationDisabled() ||
1657 0 : line->IsImpactedByFloat() ||
1658 0 : (line->mBounds.XMost() > newAvailWidth)) {
1659 0 : line->MarkDirty();
1660 : }
1661 :
1662 : #ifdef REALLY_NOISY_REFLOW
1663 : if (!line->IsBlock()) {
1664 : printf("PrepareResizeReflow thinks line %p is %simpacted by floats\n",
1665 : line.get(), line->IsImpactedByFloat() ? "" : "not ");
1666 : }
1667 : #endif
1668 : #ifdef DEBUG
1669 0 : if (gNoisyReflow && !line->IsDirty()) {
1670 0 : IndentBy(stdout, gNoiseIndent + 1);
1671 : printf("skipped: line=%p next=%p %s %s%s%s%s breakTypeBefore/After=%d/%d xmost=%d\n",
1672 0 : static_cast<void*>(line.get()),
1673 0 : static_cast<void*>((line.next() != end_lines() ? line.next().get() : nsnull)),
1674 0 : line->IsBlock() ? "block" : "inline",
1675 0 : line->HasBreakAfter() ? "has-break-after " : "",
1676 0 : line->HasFloats() ? "has-floats " : "",
1677 0 : line->IsImpactedByFloat() ? "impacted " : "",
1678 : skipLastLine ? "last-line-left-aligned " : "",
1679 0 : line->GetBreakTypeBefore(), line->GetBreakTypeAfter(),
1680 0 : line->mBounds.XMost());
1681 : }
1682 : #endif
1683 : }
1684 : }
1685 : else {
1686 : // Mark everything dirty
1687 0 : for (line_iterator line = begin_lines(), line_end = end_lines();
1688 : line != line_end;
1689 : ++line)
1690 : {
1691 0 : line->MarkDirty();
1692 : }
1693 : }
1694 0 : return NS_OK;
1695 : }
1696 :
1697 : //----------------------------------------
1698 :
1699 : /**
1700 : * Propagate reflow "damage" from from earlier lines to the current
1701 : * line. The reflow damage comes from the following sources:
1702 : * 1. The regions of float damage remembered during reflow.
1703 : * 2. The combination of nonzero |aDeltaY| and any impact by a float,
1704 : * either the previous reflow or now.
1705 : *
1706 : * When entering this function, |aLine| is still at its old position and
1707 : * |aDeltaY| indicates how much it will later be slid (assuming it
1708 : * doesn't get marked dirty and reflowed entirely).
1709 : */
1710 : void
1711 0 : nsBlockFrame::PropagateFloatDamage(nsBlockReflowState& aState,
1712 : nsLineBox* aLine,
1713 : nscoord aDeltaY)
1714 : {
1715 0 : nsFloatManager *floatManager = aState.mReflowState.mFloatManager;
1716 0 : NS_ASSERTION((aState.mReflowState.parentReflowState &&
1717 : aState.mReflowState.parentReflowState->mFloatManager == floatManager) ||
1718 : aState.mReflowState.mBlockDelta == 0, "Bad block delta passed in");
1719 :
1720 : // Check to see if there are any floats; if there aren't, there can't
1721 : // be any float damage
1722 0 : if (!floatManager->HasAnyFloats())
1723 0 : return;
1724 :
1725 : // Check the damage region recorded in the float damage.
1726 0 : if (floatManager->HasFloatDamage()) {
1727 : // Need to check mBounds *and* mCombinedArea to find intersections
1728 : // with aLine's floats
1729 0 : nscoord lineYA = aLine->mBounds.y + aDeltaY;
1730 0 : nscoord lineYB = lineYA + aLine->mBounds.height;
1731 : // Scrollable overflow should be sufficient for things that affect
1732 : // layout.
1733 0 : nsRect overflow = aLine->GetOverflowArea(eScrollableOverflow);
1734 0 : nscoord lineYCombinedA = overflow.y + aDeltaY;
1735 0 : nscoord lineYCombinedB = lineYCombinedA + overflow.height;
1736 0 : if (floatManager->IntersectsDamage(lineYA, lineYB) ||
1737 0 : floatManager->IntersectsDamage(lineYCombinedA, lineYCombinedB)) {
1738 0 : aLine->MarkDirty();
1739 : return;
1740 : }
1741 : }
1742 :
1743 : // Check if the line is moving relative to the float manager
1744 0 : if (aDeltaY + aState.mReflowState.mBlockDelta != 0) {
1745 0 : if (aLine->IsBlock()) {
1746 : // Unconditionally reflow sliding blocks; we only really need to reflow
1747 : // if there's a float impacting this block, but the current float manager
1748 : // makes it difficult to check that. Therefore, we let the child block
1749 : // decide what it needs to reflow.
1750 0 : aLine->MarkDirty();
1751 : } else {
1752 0 : bool wasImpactedByFloat = aLine->IsImpactedByFloat();
1753 : nsFlowAreaRect floatAvailableSpace =
1754 : aState.GetFloatAvailableSpaceForHeight(aLine->mBounds.y + aDeltaY,
1755 : aLine->mBounds.height,
1756 0 : nsnull);
1757 :
1758 : #ifdef REALLY_NOISY_REFLOW
1759 : printf("nsBlockFrame::PropagateFloatDamage %p was = %d, is=%d\n",
1760 : this, wasImpactedByFloat, floatAvailableSpace.mHasFloats);
1761 : #endif
1762 :
1763 : // Mark the line dirty if it was or is affected by a float
1764 : // We actually only really need to reflow if the amount of impact
1765 : // changes, but that's not straightforward to check
1766 0 : if (wasImpactedByFloat || floatAvailableSpace.mHasFloats) {
1767 0 : aLine->MarkDirty();
1768 : }
1769 : }
1770 : }
1771 : }
1772 :
1773 : static void PlaceFrameView(nsIFrame* aFrame);
1774 :
1775 0 : static bool LineHasClear(nsLineBox* aLine) {
1776 0 : return aLine->IsBlock()
1777 0 : ? (aLine->GetBreakTypeBefore() ||
1778 0 : (aLine->mFirstChild->GetStateBits() & NS_BLOCK_HAS_CLEAR_CHILDREN) ||
1779 0 : !nsBlockFrame::BlockCanIntersectFloats(aLine->mFirstChild))
1780 0 : : aLine->HasFloatBreakAfter();
1781 : }
1782 :
1783 :
1784 : /**
1785 : * Reparent a whole list of floats from aOldParent to this block. The
1786 : * floats might be taken from aOldParent's overflow list. They will be
1787 : * removed from the list. They end up appended to our mFloats list.
1788 : */
1789 : void
1790 0 : nsBlockFrame::ReparentFloats(nsIFrame* aFirstFrame,
1791 : nsBlockFrame* aOldParent, bool aFromOverflow,
1792 : bool aReparentSiblings) {
1793 0 : nsFrameList list;
1794 0 : aOldParent->CollectFloats(aFirstFrame, list, aFromOverflow, aReparentSiblings);
1795 0 : if (list.NotEmpty()) {
1796 0 : for (nsIFrame* f = list.FirstChild(); f; f = f->GetNextSibling()) {
1797 0 : ReparentFrame(f, aOldParent, this);
1798 : }
1799 0 : mFloats.AppendFrames(nsnull, list);
1800 : }
1801 0 : }
1802 :
1803 0 : static void DumpLine(const nsBlockReflowState& aState, nsLineBox* aLine,
1804 : nscoord aDeltaY, PRInt32 aDeltaIndent) {
1805 : #ifdef DEBUG
1806 0 : if (nsBlockFrame::gNoisyReflow) {
1807 0 : nsRect ovis(aLine->GetVisualOverflowArea());
1808 0 : nsRect oscr(aLine->GetScrollableOverflowArea());
1809 0 : nsBlockFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent + aDeltaIndent);
1810 : printf("line=%p mY=%d dirty=%s oldBounds={%d,%d,%d,%d} oldoverflow-vis={%d,%d,%d,%d} oldoverflow-scr={%d,%d,%d,%d} deltaY=%d mPrevBottomMargin=%d childCount=%d\n",
1811 : static_cast<void*>(aLine), aState.mY,
1812 0 : aLine->IsDirty() ? "yes" : "no",
1813 : aLine->mBounds.x, aLine->mBounds.y,
1814 : aLine->mBounds.width, aLine->mBounds.height,
1815 : ovis.x, ovis.y, ovis.width, ovis.height,
1816 : oscr.x, oscr.y, oscr.width, oscr.height,
1817 0 : aDeltaY, aState.mPrevBottomMargin.get(), aLine->GetChildCount());
1818 : }
1819 : #endif
1820 0 : }
1821 :
1822 : /**
1823 : * Reflow the dirty lines
1824 : */
1825 : nsresult
1826 0 : nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
1827 : {
1828 0 : nsresult rv = NS_OK;
1829 0 : bool keepGoing = true;
1830 0 : bool repositionViews = false; // should we really need this?
1831 0 : bool foundAnyClears = aState.mFloatBreakType != NS_STYLE_CLEAR_NONE;
1832 0 : bool willReflowAgain = false;
1833 :
1834 : #ifdef DEBUG
1835 0 : if (gNoisyReflow) {
1836 0 : IndentBy(stdout, gNoiseIndent);
1837 0 : ListTag(stdout);
1838 0 : printf(": reflowing dirty lines");
1839 0 : printf(" computedWidth=%d\n", aState.mReflowState.ComputedWidth());
1840 : }
1841 0 : AutoNoisyIndenter indent(gNoisyReflow);
1842 : #endif
1843 :
1844 0 : bool selfDirty = (GetStateBits() & NS_FRAME_IS_DIRTY) ||
1845 : (aState.mReflowState.mFlags.mVResize &&
1846 0 : (GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT));
1847 :
1848 : // Reflow our last line if our availableHeight has increased
1849 : // so that we (and our last child) pull up content as necessary
1850 0 : if (aState.mReflowState.availableHeight != NS_UNCONSTRAINEDSIZE
1851 0 : && GetNextInFlow() && aState.mReflowState.availableHeight > mRect.height) {
1852 0 : line_iterator lastLine = end_lines();
1853 0 : if (lastLine != begin_lines()) {
1854 0 : --lastLine;
1855 0 : lastLine->MarkDirty();
1856 : }
1857 : }
1858 : // the amount by which we will slide the current line if it is not
1859 : // dirty
1860 0 : nscoord deltaY = 0;
1861 :
1862 : // whether we did NOT reflow the previous line and thus we need to
1863 : // recompute the carried out margin before the line if we want to
1864 : // reflow it or if its previous margin is dirty
1865 0 : bool needToRecoverState = false;
1866 : // Float continuations were reflowed in ReflowPushedFloats
1867 0 : bool reflowedFloat = mFloats.NotEmpty() &&
1868 0 : (mFloats.FirstChild()->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT);
1869 0 : bool lastLineMovedUp = false;
1870 : // We save up information about BR-clearance here
1871 0 : PRUint8 inlineFloatBreakType = aState.mFloatBreakType;
1872 :
1873 0 : line_iterator line = begin_lines(), line_end = end_lines();
1874 :
1875 : // Reflow the lines that are already ours
1876 0 : for ( ; line != line_end; ++line, aState.AdvanceToNextLine()) {
1877 0 : DumpLine(aState, line, deltaY, 0);
1878 : #ifdef DEBUG
1879 0 : AutoNoisyIndenter indent2(gNoisyReflow);
1880 : #endif
1881 :
1882 0 : if (selfDirty)
1883 0 : line->MarkDirty();
1884 :
1885 : // This really sucks, but we have to look inside any blocks that have clear
1886 : // elements inside them.
1887 : // XXX what can we do smarter here?
1888 0 : if (!line->IsDirty() && line->IsBlock() &&
1889 0 : (line->mFirstChild->GetStateBits() & NS_BLOCK_HAS_CLEAR_CHILDREN)) {
1890 0 : line->MarkDirty();
1891 : }
1892 :
1893 0 : nsIFrame *replacedBlock = nsnull;
1894 0 : if (line->IsBlock() &&
1895 0 : !nsBlockFrame::BlockCanIntersectFloats(line->mFirstChild)) {
1896 0 : replacedBlock = line->mFirstChild;
1897 : }
1898 :
1899 : // We have to reflow the line if it's a block whose clearance
1900 : // might have changed, so detect that.
1901 0 : if (!line->IsDirty() &&
1902 0 : (line->GetBreakTypeBefore() != NS_STYLE_CLEAR_NONE ||
1903 : replacedBlock)) {
1904 0 : nscoord curY = aState.mY;
1905 : // See where we would be after applying any clearance due to
1906 : // BRs.
1907 0 : if (inlineFloatBreakType != NS_STYLE_CLEAR_NONE) {
1908 0 : curY = aState.ClearFloats(curY, inlineFloatBreakType);
1909 : }
1910 :
1911 : nscoord newY =
1912 0 : aState.ClearFloats(curY, line->GetBreakTypeBefore(), replacedBlock);
1913 :
1914 0 : if (line->HasClearance()) {
1915 : // Reflow the line if it might not have clearance anymore.
1916 0 : if (newY == curY
1917 : // aState.mY is the clearance point which should be the
1918 : // top border-edge of the block frame. If sliding the
1919 : // block by deltaY isn't going to put it in the predicted
1920 : // position, then we'd better reflow the line.
1921 0 : || newY != line->mBounds.y + deltaY) {
1922 0 : line->MarkDirty();
1923 : }
1924 : } else {
1925 : // Reflow the line if the line might have clearance now.
1926 0 : if (curY != newY) {
1927 0 : line->MarkDirty();
1928 : }
1929 : }
1930 : }
1931 :
1932 : // We might have to reflow a line that is after a clearing BR.
1933 0 : if (inlineFloatBreakType != NS_STYLE_CLEAR_NONE) {
1934 0 : aState.mY = aState.ClearFloats(aState.mY, inlineFloatBreakType);
1935 0 : if (aState.mY != line->mBounds.y + deltaY) {
1936 : // SlideLine is not going to put the line where the clearance
1937 : // put it. Reflow the line to be sure.
1938 0 : line->MarkDirty();
1939 : }
1940 0 : inlineFloatBreakType = NS_STYLE_CLEAR_NONE;
1941 : }
1942 :
1943 0 : bool previousMarginWasDirty = line->IsPreviousMarginDirty();
1944 0 : if (previousMarginWasDirty) {
1945 : // If the previous margin is dirty, reflow the current line
1946 0 : line->MarkDirty();
1947 0 : line->ClearPreviousMarginDirty();
1948 0 : } else if (line->mBounds.YMost() + deltaY > aState.mBottomEdge) {
1949 : // Lines that aren't dirty but get slid past our height constraint must
1950 : // be reflowed.
1951 0 : line->MarkDirty();
1952 : }
1953 :
1954 : // If we have a constrained height (i.e., breaking columns/pages),
1955 : // and the distance to the bottom might have changed, then we need
1956 : // to reflow any line that might have floats in it, both because the
1957 : // breakpoints within those floats may have changed and because we
1958 : // might have to push/pull the floats in their entirety.
1959 : // FIXME: What about a deltaY or height change that forces us to
1960 : // push lines? Why does that work?
1961 0 : if (!line->IsDirty() &&
1962 : aState.mReflowState.availableHeight != NS_UNCONSTRAINEDSIZE &&
1963 : (deltaY != 0 || aState.mReflowState.mFlags.mVResize) &&
1964 0 : (line->IsBlock() || line->HasFloats() || line->HadFloatPushed())) {
1965 0 : line->MarkDirty();
1966 : }
1967 :
1968 0 : if (!line->IsDirty()) {
1969 : // See if there's any reflow damage that requires that we mark the
1970 : // line dirty.
1971 0 : PropagateFloatDamage(aState, line, deltaY);
1972 : }
1973 :
1974 0 : if (needToRecoverState && line->IsDirty()) {
1975 : // We need to reconstruct the bottom margin only if we didn't
1976 : // reflow the previous line and we do need to reflow (or repair
1977 : // the top position of) the next line.
1978 0 : aState.ReconstructMarginAbove(line);
1979 : }
1980 :
1981 0 : bool reflowedPrevLine = !needToRecoverState;
1982 0 : if (needToRecoverState) {
1983 0 : needToRecoverState = false;
1984 :
1985 : // Update aState.mPrevChild as if we had reflowed all of the frames in
1986 : // this line.
1987 0 : if (line->IsDirty())
1988 0 : NS_ASSERTION(line->mFirstChild->GetPrevSibling() ==
1989 : line.prev()->LastChild(), "unexpected line frames");
1990 0 : aState.mPrevChild = line->mFirstChild->GetPrevSibling();
1991 : }
1992 :
1993 : // Now repair the line and update |aState.mY| by calling
1994 : // |ReflowLine| or |SlideLine|.
1995 : // If we're going to reflow everything again, then no need to reflow
1996 : // the dirty line ... unless the line has floats, in which case we'd
1997 : // better reflow it now to refresh its float cache, which may contain
1998 : // dangling frame pointers! Ugh! This reflow of the line may be
1999 : // incorrect because we skipped reflowing previous lines (e.g., floats
2000 : // may be placed incorrectly), but that's OK because we'll mark the
2001 : // line dirty below under "if (aState.mReflowState.mDiscoveredClearance..."
2002 0 : if (line->IsDirty() && (line->HasFloats() || !willReflowAgain)) {
2003 0 : lastLineMovedUp = true;
2004 :
2005 : bool maybeReflowingForFirstTime =
2006 0 : line->mBounds.x == 0 && line->mBounds.y == 0 &&
2007 0 : line->mBounds.width == 0 && line->mBounds.height == 0;
2008 :
2009 : // Compute the dirty lines "before" YMost, after factoring in
2010 : // the running deltaY value - the running value is implicit in
2011 : // aState.mY.
2012 0 : nscoord oldY = line->mBounds.y;
2013 0 : nscoord oldYMost = line->mBounds.YMost();
2014 :
2015 0 : NS_ASSERTION(!willReflowAgain || !line->IsBlock(),
2016 : "Don't reflow blocks while willReflowAgain is true, reflow of block abs-pos children depends on this");
2017 :
2018 : // Reflow the dirty line. If it's an incremental reflow, then force
2019 : // it to invalidate the dirty area if necessary
2020 0 : rv = ReflowLine(aState, line, &keepGoing);
2021 0 : NS_ENSURE_SUCCESS(rv, rv);
2022 :
2023 0 : if (aState.mReflowState.WillReflowAgainForClearance()) {
2024 0 : line->MarkDirty();
2025 0 : willReflowAgain = true;
2026 : // Note that once we've entered this state, every line that gets here
2027 : // (e.g. because it has floats) gets marked dirty and reflowed again.
2028 : // in the next pass. This is important, see above.
2029 : }
2030 :
2031 0 : if (line->HasFloats()) {
2032 0 : reflowedFloat = true;
2033 : }
2034 :
2035 0 : if (!keepGoing) {
2036 0 : DumpLine(aState, line, deltaY, -1);
2037 0 : if (0 == line->GetChildCount()) {
2038 0 : DeleteLine(aState, line, line_end);
2039 : }
2040 : break;
2041 : }
2042 :
2043 : // Test to see whether the margin that should be carried out
2044 : // to the next line (NL) might have changed. In ReflowBlockFrame
2045 : // we call nextLine->MarkPreviousMarginDirty if the block's
2046 : // actual carried-out bottom margin changed. So here we only
2047 : // need to worry about the following effects:
2048 : // 1) the line was just created, and it might now be blocking
2049 : // a carried-out bottom margin from previous lines that
2050 : // used to reach NL from reaching NL
2051 : // 2) the line used to be empty, and is now not empty,
2052 : // thus blocking a carried-out bottom margin from previous lines
2053 : // that used to reach NL from reaching NL
2054 : // 3) the line wasn't empty, but now is, so a carried-out
2055 : // bottom margin from previous lines that didn't used to reach NL
2056 : // now does
2057 : // 4) the line might have changed in a way that affects NL's
2058 : // ShouldApplyTopMargin decision. The three things that matter
2059 : // are the line's emptiness, its adjacency to the top of the block,
2060 : // and whether it has clearance (the latter only matters if the block
2061 : // was and is adjacent to the top and empty).
2062 : //
2063 : // If the line is empty now, we can't reliably tell if the line was empty
2064 : // before, so we just assume it was and do nextLine->MarkPreviousMarginDirty.
2065 : // This means the checks in 4) are redundant; if the line is empty now
2066 : // we don't need to check 4), but if the line is not empty now and we're sure
2067 : // it wasn't empty before, any adjacency and clearance changes are irrelevant
2068 : // to the result of nextLine->ShouldApplyTopMargin.
2069 0 : if (line.next() != end_lines()) {
2070 0 : bool maybeWasEmpty = oldY == line.next()->mBounds.y;
2071 0 : bool isEmpty = line->CachedIsEmpty();
2072 0 : if (maybeReflowingForFirstTime /*1*/ ||
2073 : (isEmpty || maybeWasEmpty) /*2/3/4*/) {
2074 0 : line.next()->MarkPreviousMarginDirty();
2075 : // since it's marked dirty, nobody will care about |deltaY|
2076 : }
2077 : }
2078 :
2079 : // If the line was just reflowed for the first time, then its
2080 : // old mBounds cannot be trusted so this deltaY computation is
2081 : // bogus. But that's OK because we just did
2082 : // MarkPreviousMarginDirty on the next line which will force it
2083 : // to be reflowed, so this computation of deltaY will not be
2084 : // used.
2085 0 : deltaY = line->mBounds.YMost() - oldYMost;
2086 :
2087 : // Now do an interrupt check. We want to do this only in the case when we
2088 : // actually reflow the line, so that if we get back in here we'll get
2089 : // further on the reflow before interrupting.
2090 0 : aState.mPresContext->CheckForInterrupt(this);
2091 : } else {
2092 0 : aState.mOverflowTracker->Skip(line->mFirstChild, aState.mReflowStatus);
2093 : // Nop except for blocks (we don't create overflow container
2094 : // continuations for any inlines atm), so only checking mFirstChild
2095 : // is enough
2096 :
2097 0 : lastLineMovedUp = deltaY < 0;
2098 :
2099 0 : if (deltaY != 0)
2100 0 : SlideLine(aState, line, deltaY);
2101 : else
2102 0 : repositionViews = true;
2103 :
2104 0 : NS_ASSERTION(!line->IsDirty() || !line->HasFloats(),
2105 : "Possibly stale float cache here!");
2106 0 : if (willReflowAgain && line->IsBlock()) {
2107 : // If we're going to reflow everything again, and this line is a block,
2108 : // then there is no need to recover float state. The line may contain
2109 : // other lines with floats, but in that case RecoverStateFrom would only
2110 : // add floats to the float manager. We don't need to do that because
2111 : // everything's going to get reflowed again "for real". Calling
2112 : // RecoverStateFrom in this situation could be lethal because the
2113 : // block's descendant lines may have float caches containing dangling
2114 : // frame pointers. Ugh!
2115 : // If this line is inline, then we need to recover its state now
2116 : // to make sure that we don't forget to move its floats by deltaY.
2117 : } else {
2118 : // XXX EVIL O(N^2) EVIL
2119 0 : aState.RecoverStateFrom(line, deltaY);
2120 : }
2121 :
2122 : // Keep mY up to date in case we're propagating reflow damage
2123 : // and also because our final height may depend on it. If the
2124 : // line is inlines, then only update mY if the line is not
2125 : // empty, because that's what PlaceLine does. (Empty blocks may
2126 : // want to update mY, e.g. if they have clearance.)
2127 0 : if (line->IsBlock() || !line->CachedIsEmpty()) {
2128 0 : aState.mY = line->mBounds.YMost();
2129 : }
2130 :
2131 0 : needToRecoverState = true;
2132 :
2133 0 : if (reflowedPrevLine && !line->IsBlock() &&
2134 0 : aState.mPresContext->HasPendingInterrupt()) {
2135 : // Need to make sure to pull overflows from any prev-in-flows
2136 0 : for (nsIFrame* inlineKid = line->mFirstChild; inlineKid;
2137 : inlineKid = inlineKid->GetFirstPrincipalChild()) {
2138 0 : inlineKid->PullOverflowsFromPrevInFlow();
2139 : }
2140 : }
2141 : }
2142 :
2143 : // Record if we need to clear floats before reflowing the next
2144 : // line. Note that inlineFloatBreakType will be handled and
2145 : // cleared before the next line is processed, so there is no
2146 : // need to combine break types here.
2147 0 : if (line->HasFloatBreakAfter()) {
2148 0 : inlineFloatBreakType = line->GetBreakTypeAfter();
2149 : }
2150 :
2151 0 : if (LineHasClear(line.get())) {
2152 0 : foundAnyClears = true;
2153 : }
2154 :
2155 0 : DumpLine(aState, line, deltaY, -1);
2156 :
2157 0 : if (aState.mPresContext->HasPendingInterrupt()) {
2158 0 : willReflowAgain = true;
2159 : // Another option here might be to leave |line| clean if
2160 : // !HasPendingInterrupt() before the CheckForInterrupt() call, since in
2161 : // that case the line really did reflow as it should have. Not sure
2162 : // whether that would be safe, so doing this for now instead. Also not
2163 : // sure whether we really want to mark all lines dirty after an
2164 : // interrupt, but until we get better at propagating float damage we
2165 : // really do need to do it this way; see comments inside MarkLineDirty.
2166 0 : MarkLineDirtyForInterrupt(line);
2167 : }
2168 : }
2169 :
2170 : // Handle BR-clearance from the last line of the block
2171 0 : if (inlineFloatBreakType != NS_STYLE_CLEAR_NONE) {
2172 0 : aState.mY = aState.ClearFloats(aState.mY, inlineFloatBreakType);
2173 : }
2174 :
2175 0 : if (needToRecoverState) {
2176 : // Is this expensive?
2177 0 : aState.ReconstructMarginAbove(line);
2178 :
2179 : // Update aState.mPrevChild as if we had reflowed all of the frames in
2180 : // the last line.
2181 0 : NS_ASSERTION(line == line_end || line->mFirstChild->GetPrevSibling() ==
2182 : line.prev()->LastChild(), "unexpected line frames");
2183 : aState.mPrevChild =
2184 0 : line == line_end ? mFrames.LastChild() : line->mFirstChild->GetPrevSibling();
2185 : }
2186 :
2187 : // Should we really have to do this?
2188 0 : if (repositionViews)
2189 0 : ::PlaceFrameView(this);
2190 :
2191 : // We can skip trying to pull up the next line if our height is constrained
2192 : // (so we can report being incomplete) and there is no next in flow or we
2193 : // were told not to or we know it will be futile, i.e.,
2194 : // -- the next in flow is not changing
2195 : // -- and we cannot have added more space for its first line to be
2196 : // pulled up into,
2197 : // -- it's an incremental reflow of a descendant
2198 : // -- and we didn't reflow any floats (so the available space
2199 : // didn't change)
2200 : // -- my chain of next-in-flows either has no first line, or its first
2201 : // line isn't dirty.
2202 : bool heightConstrained =
2203 0 : aState.mReflowState.availableHeight != NS_UNCONSTRAINEDSIZE;
2204 0 : bool skipPull = willReflowAgain && heightConstrained;
2205 0 : if (!skipPull && heightConstrained && aState.mNextInFlow &&
2206 : (aState.mReflowState.mFlags.mNextInFlowUntouched &&
2207 0 : !lastLineMovedUp &&
2208 0 : !(GetStateBits() & NS_FRAME_IS_DIRTY) &&
2209 0 : !reflowedFloat)) {
2210 : // We'll place lineIter at the last line of this block, so that
2211 : // nsBlockInFlowLineIterator::Next() will take us to the first
2212 : // line of my next-in-flow-chain. (But first, check that I
2213 : // have any lines -- if I don't, just bail out of this
2214 : // optimization.)
2215 0 : line_iterator lineIter = this->end_lines();
2216 0 : if (lineIter != this->begin_lines()) {
2217 0 : lineIter--; // I have lines; step back from dummy iterator to last line.
2218 0 : nsBlockInFlowLineIterator bifLineIter(this, lineIter);
2219 :
2220 : // Check for next-in-flow-chain's first line.
2221 : // (First, see if there is such a line, and second, see if it's clean)
2222 0 : if (!bifLineIter.Next() ||
2223 0 : !bifLineIter.GetLine()->IsDirty()) {
2224 0 : skipPull=true;
2225 : }
2226 : }
2227 : }
2228 :
2229 0 : if (skipPull && aState.mNextInFlow) {
2230 0 : NS_ASSERTION(heightConstrained, "Height should be constrained here\n");
2231 0 : if (IS_TRUE_OVERFLOW_CONTAINER(aState.mNextInFlow))
2232 0 : NS_FRAME_SET_OVERFLOW_INCOMPLETE(aState.mReflowStatus);
2233 : else
2234 0 : NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
2235 : }
2236 :
2237 0 : if (!skipPull && aState.mNextInFlow) {
2238 : // Pull data from a next-in-flow if there's still room for more
2239 : // content here.
2240 0 : while (keepGoing && aState.mNextInFlow) {
2241 : // Grab first line from our next-in-flow
2242 0 : nsBlockFrame* nextInFlow = aState.mNextInFlow;
2243 : nsLineBox* pulledLine;
2244 0 : nsFrameList pulledFrames;
2245 0 : bool isOverflowLine = false;
2246 0 : if (!nextInFlow->mLines.empty()) {
2247 : RemoveFirstLine(nextInFlow->mLines, nextInFlow->mFrames,
2248 0 : &pulledLine, &pulledFrames);
2249 : } else {
2250 : // Grab an overflow line if there are any
2251 0 : FrameLines* overflowLines = nextInFlow->GetOverflowLines();
2252 0 : if (!overflowLines) {
2253 : aState.mNextInFlow =
2254 0 : static_cast<nsBlockFrame*>(nextInFlow->GetNextInFlow());
2255 0 : continue;
2256 : }
2257 : bool last =
2258 : RemoveFirstLine(overflowLines->mLines, overflowLines->mFrames,
2259 0 : &pulledLine, &pulledFrames);
2260 0 : if (last) {
2261 0 : nextInFlow->DestroyOverflowLines();
2262 : }
2263 0 : isOverflowLine = true;
2264 : }
2265 :
2266 0 : if (pulledFrames.IsEmpty()) {
2267 : // The line is empty. Try the next one.
2268 0 : NS_ASSERTION(pulledLine->GetChildCount() == 0 &&
2269 : !pulledLine->mFirstChild, "bad empty line");
2270 0 : nextInFlow->FreeLineBox(pulledLine);
2271 0 : continue;
2272 : }
2273 :
2274 0 : if (pulledLine == nextInFlow->GetLineCursor()) {
2275 0 : nextInFlow->ClearLineCursor();
2276 : }
2277 0 : ReparentFrames(pulledFrames, nextInFlow, this);
2278 :
2279 0 : NS_ASSERTION(pulledFrames.LastChild() == pulledLine->LastChild(),
2280 : "Unexpected last frame");
2281 0 : NS_ASSERTION(aState.mPrevChild || mLines.empty(), "should have a prevchild here");
2282 0 : NS_ASSERTION(aState.mPrevChild == mFrames.LastChild(),
2283 : "Incorrect aState.mPrevChild before inserting line at end");
2284 :
2285 : // Shift pulledLine's frames into our mFrames list.
2286 0 : mFrames.AppendFrames(nsnull, pulledFrames);
2287 :
2288 : // Add line to our line list, and set its last child as our new prev-child
2289 0 : line = mLines.before_insert(end_lines(), pulledLine);
2290 0 : aState.mPrevChild = mFrames.LastChild();
2291 :
2292 : // Reparent floats whose placeholders are in the line.
2293 0 : ReparentFloats(pulledLine->mFirstChild, nextInFlow, isOverflowLine, true);
2294 :
2295 0 : DumpLine(aState, pulledLine, deltaY, 0);
2296 : #ifdef DEBUG
2297 0 : AutoNoisyIndenter indent2(gNoisyReflow);
2298 : #endif
2299 :
2300 0 : if (aState.mPresContext->HasPendingInterrupt()) {
2301 0 : MarkLineDirtyForInterrupt(line);
2302 : } else {
2303 : // Now reflow it and any lines that it makes during it's reflow
2304 : // (we have to loop here because reflowing the line may cause a new
2305 : // line to be created; see SplitLine's callers for examples of
2306 : // when this happens).
2307 0 : while (line != end_lines()) {
2308 0 : rv = ReflowLine(aState, line, &keepGoing);
2309 0 : NS_ENSURE_SUCCESS(rv, rv);
2310 :
2311 0 : if (aState.mReflowState.WillReflowAgainForClearance()) {
2312 0 : line->MarkDirty();
2313 0 : keepGoing = false;
2314 0 : NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
2315 0 : break;
2316 : }
2317 :
2318 0 : DumpLine(aState, line, deltaY, -1);
2319 0 : if (!keepGoing) {
2320 0 : if (0 == line->GetChildCount()) {
2321 0 : DeleteLine(aState, line, line_end);
2322 : }
2323 0 : break;
2324 : }
2325 :
2326 0 : if (LineHasClear(line.get())) {
2327 0 : foundAnyClears = true;
2328 : }
2329 :
2330 0 : if (aState.mPresContext->CheckForInterrupt(this)) {
2331 0 : MarkLineDirtyForInterrupt(line);
2332 0 : break;
2333 : }
2334 :
2335 : // If this is an inline frame then its time to stop
2336 0 : ++line;
2337 0 : aState.AdvanceToNextLine();
2338 : }
2339 : }
2340 : }
2341 :
2342 0 : if (NS_FRAME_IS_NOT_COMPLETE(aState.mReflowStatus)) {
2343 0 : aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
2344 : } //XXXfr shouldn't set this flag when nextinflow has no lines
2345 : }
2346 :
2347 : // Handle an odd-ball case: a list-item with no lines
2348 0 : if (HasOutsideBullet() && mLines.empty()) {
2349 0 : nsHTMLReflowMetrics metrics;
2350 0 : nsIFrame* bullet = GetOutsideBullet();
2351 : ReflowBullet(bullet, aState, metrics,
2352 0 : aState.mReflowState.mComputedBorderPadding.top);
2353 0 : NS_ASSERTION(!BulletIsEmpty() || metrics.height == 0,
2354 : "empty bullet took up space");
2355 :
2356 0 : if (!BulletIsEmpty()) {
2357 : // There are no lines so we have to fake up some y motion so that
2358 : // we end up with *some* height.
2359 :
2360 0 : if (metrics.ascent == nsHTMLReflowMetrics::ASK_FOR_BASELINE &&
2361 0 : !nsLayoutUtils::GetFirstLineBaseline(bullet, &metrics.ascent)) {
2362 0 : metrics.ascent = metrics.height;
2363 : }
2364 :
2365 0 : nsRefPtr<nsFontMetrics> fm;
2366 : nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm),
2367 0 : nsLayoutUtils::FontSizeInflationFor(this, nsLayoutUtils::eInReflow));
2368 0 : aState.mReflowState.rendContext->SetFont(fm); // FIXME: needed?
2369 :
2370 : nscoord minAscent =
2371 0 : nsLayoutUtils::GetCenteredFontBaseline(fm, aState.mMinLineHeight);
2372 0 : nscoord minDescent = aState.mMinLineHeight - minAscent;
2373 :
2374 0 : aState.mY += NS_MAX(minAscent, metrics.ascent) +
2375 0 : NS_MAX(minDescent, metrics.height - metrics.ascent);
2376 :
2377 0 : nscoord offset = minAscent - metrics.ascent;
2378 0 : if (offset > 0) {
2379 0 : bullet->SetRect(bullet->GetRect() + nsPoint(0, offset));
2380 : }
2381 : }
2382 : }
2383 :
2384 0 : if (foundAnyClears) {
2385 0 : AddStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN);
2386 : } else {
2387 0 : RemoveStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN);
2388 : }
2389 :
2390 : #ifdef DEBUG
2391 0 : VerifyLines(true);
2392 0 : VerifyOverflowSituation();
2393 0 : if (gNoisyReflow) {
2394 0 : IndentBy(stdout, gNoiseIndent - 1);
2395 0 : ListTag(stdout);
2396 : printf(": done reflowing dirty lines (status=%x)\n",
2397 0 : aState.mReflowStatus);
2398 : }
2399 : #endif
2400 :
2401 0 : return rv;
2402 : }
2403 :
2404 0 : static void MarkAllDescendantLinesDirty(nsBlockFrame* aBlock)
2405 : {
2406 0 : nsLineList::iterator line = aBlock->begin_lines();
2407 0 : nsLineList::iterator endLine = aBlock->end_lines();
2408 0 : while (line != endLine) {
2409 0 : if (line->IsBlock()) {
2410 0 : nsIFrame* f = line->mFirstChild;
2411 0 : nsBlockFrame* bf = nsLayoutUtils::GetAsBlock(f);
2412 0 : if (bf) {
2413 0 : MarkAllDescendantLinesDirty(bf);
2414 : }
2415 : }
2416 0 : line->MarkDirty();
2417 0 : ++line;
2418 : }
2419 0 : }
2420 :
2421 : void
2422 0 : nsBlockFrame::MarkLineDirtyForInterrupt(nsLineBox* aLine)
2423 : {
2424 0 : aLine->MarkDirty();
2425 :
2426 : // Just checking NS_FRAME_IS_DIRTY is ok, because we've already
2427 : // marked the lines that need to be marked dirty based on our
2428 : // vertical resize stuff. So we'll definitely reflow all those kids;
2429 : // the only question is how they should behave.
2430 0 : if (GetStateBits() & NS_FRAME_IS_DIRTY) {
2431 : // Mark all our child frames dirty so we make sure to reflow them
2432 : // later.
2433 0 : PRInt32 n = aLine->GetChildCount();
2434 0 : for (nsIFrame* f = aLine->mFirstChild; n > 0;
2435 : f = f->GetNextSibling(), --n) {
2436 0 : f->AddStateBits(NS_FRAME_IS_DIRTY);
2437 : }
2438 : // And mark all the floats whose reflows we might be skipping dirty too.
2439 0 : if (aLine->HasFloats()) {
2440 0 : for (nsFloatCache* fc = aLine->GetFirstFloat(); fc; fc = fc->Next()) {
2441 0 : fc->mFloat->AddStateBits(NS_FRAME_IS_DIRTY);
2442 : }
2443 : }
2444 : } else {
2445 : // Dirty all the descendant lines of block kids to handle float damage,
2446 : // since our nsFloatManager will go away by the next time we're reflowing.
2447 : // XXXbz Can we do something more like what PropagateFloatDamage does?
2448 : // Would need to sort out the exact business with mBlockDelta for that....
2449 : // This marks way too much dirty. If we ever make this better, revisit
2450 : // which lines we mark dirty in the interrupt case in ReflowDirtyLines.
2451 0 : nsBlockFrame* bf = nsLayoutUtils::GetAsBlock(aLine->mFirstChild);
2452 0 : if (bf) {
2453 0 : MarkAllDescendantLinesDirty(bf);
2454 : }
2455 : }
2456 0 : }
2457 :
2458 : void
2459 0 : nsBlockFrame::DeleteLine(nsBlockReflowState& aState,
2460 : nsLineList::iterator aLine,
2461 : nsLineList::iterator aLineEnd)
2462 : {
2463 0 : NS_PRECONDITION(0 == aLine->GetChildCount(), "can't delete !empty line");
2464 0 : if (0 == aLine->GetChildCount()) {
2465 0 : NS_ASSERTION(aState.mCurrentLine == aLine,
2466 : "using function more generally than designed, "
2467 : "but perhaps OK now");
2468 0 : nsLineBox* line = aLine;
2469 0 : aLine = mLines.erase(aLine);
2470 0 : FreeLineBox(line);
2471 : // Mark the previous margin of the next line dirty since we need to
2472 : // recompute its top position.
2473 0 : if (aLine != aLineEnd)
2474 0 : aLine->MarkPreviousMarginDirty();
2475 : }
2476 0 : }
2477 :
2478 : static void
2479 0 : InvalidateThebesLayersInLineBox(nsIFrame* aBlock, nsLineBox* aLine)
2480 : {
2481 0 : if (aBlock->GetStateBits() & NS_FRAME_HAS_CONTAINER_LAYER_DESCENDANT) {
2482 0 : PRInt32 childCount = aLine->GetChildCount();
2483 0 : for (nsIFrame* f = aLine->mFirstChild; childCount;
2484 : --childCount, f = f->GetNextSibling()) {
2485 0 : FrameLayerBuilder::InvalidateThebesLayersInSubtree(f);
2486 : }
2487 : }
2488 0 : }
2489 :
2490 : /**
2491 : * Reflow a line. The line will either contain a single block frame
2492 : * or contain 1 or more inline frames. aKeepReflowGoing indicates
2493 : * whether or not the caller should continue to reflow more lines.
2494 : */
2495 : nsresult
2496 0 : nsBlockFrame::ReflowLine(nsBlockReflowState& aState,
2497 : line_iterator aLine,
2498 : bool* aKeepReflowGoing)
2499 : {
2500 0 : nsresult rv = NS_OK;
2501 :
2502 0 : NS_ABORT_IF_FALSE(aLine->GetChildCount(), "reflowing empty line");
2503 :
2504 : // Setup the line-layout for the new line
2505 0 : aState.mCurrentLine = aLine;
2506 0 : aLine->ClearDirty();
2507 0 : aLine->InvalidateCachedIsEmpty();
2508 0 : aLine->ClearHadFloatPushed();
2509 :
2510 : // Now that we know what kind of line we have, reflow it
2511 0 : if (aLine->IsBlock()) {
2512 0 : nsRect oldBounds = aLine->mFirstChild->GetRect();
2513 0 : nsRect oldVisOverflow(aLine->GetVisualOverflowArea());
2514 0 : rv = ReflowBlockFrame(aState, aLine, aKeepReflowGoing);
2515 0 : nsRect newBounds = aLine->mFirstChild->GetRect();
2516 :
2517 : // We expect blocks to damage any area inside their bounds that is
2518 : // dirty; however, if the frame changes size or position then we
2519 : // need to do some repainting.
2520 : // XXX roc --- the above statement is ambiguous about whether 'bounds'
2521 : // means the frame's bounds or overflowArea, and in fact this is a source
2522 : // of much confusion and bugs. Thus the following hack considers *both*
2523 : // overflowArea and bounds. This should be considered a temporary hack
2524 : // until we decide how it's really supposed to work.
2525 : // Note that we have a similar hack in nsTableFrame::InvalidateFrame.
2526 0 : nsRect visOverflow(aLine->GetVisualOverflowArea());
2527 0 : if (oldVisOverflow.TopLeft() != visOverflow.TopLeft() ||
2528 0 : oldBounds.TopLeft() != newBounds.TopLeft()) {
2529 : // The block has moved, and so to be safe we need to repaint
2530 : // XXX We need to improve on this...
2531 0 : nsRect dirtyRect;
2532 0 : dirtyRect.UnionRect(oldVisOverflow, visOverflow);
2533 : #ifdef NOISY_BLOCK_INVALIDATE
2534 : printf("%p invalidate 6 (%d, %d, %d, %d)\n",
2535 : this, dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height);
2536 : #endif
2537 0 : Invalidate(dirtyRect);
2538 0 : FrameLayerBuilder::InvalidateThebesLayersInSubtree(aLine->mFirstChild);
2539 : } else {
2540 0 : nsRect combinedAreaHStrip, combinedAreaVStrip;
2541 0 : nsRect boundsHStrip, boundsVStrip;
2542 : nsLayoutUtils::GetRectDifferenceStrips(oldBounds, newBounds,
2543 0 : &boundsHStrip, &boundsVStrip);
2544 : nsLayoutUtils::GetRectDifferenceStrips(oldVisOverflow, visOverflow,
2545 : &combinedAreaHStrip,
2546 0 : &combinedAreaVStrip);
2547 :
2548 : #ifdef NOISY_BLOCK_INVALIDATE
2549 : printf("%p invalidate boundsVStrip (%d, %d, %d, %d)\n",
2550 : this, boundsVStrip.x, boundsVStrip.y, boundsVStrip.width, boundsVStrip.height);
2551 : printf("%p invalidate boundsHStrip (%d, %d, %d, %d)\n",
2552 : this, boundsHStrip.x, boundsHStrip.y, boundsHStrip.width, boundsHStrip.height);
2553 : printf("%p invalidate combinedAreaVStrip (%d, %d, %d, %d)\n",
2554 : this, combinedAreaVStrip.x, combinedAreaVStrip.y, combinedAreaVStrip.width, combinedAreaVStrip.height);
2555 : printf("%p invalidate combinedAreaHStrip (%d, %d, %d, %d)\n",
2556 : this, combinedAreaHStrip.x, combinedAreaHStrip.y, combinedAreaHStrip.width, combinedAreaHStrip.height);
2557 : #endif
2558 : // The first thing Invalidate does is check if the rect is empty, so
2559 : // don't bother doing that here.
2560 0 : Invalidate(boundsVStrip);
2561 0 : Invalidate(boundsHStrip);
2562 0 : Invalidate(combinedAreaVStrip);
2563 0 : Invalidate(combinedAreaHStrip);
2564 : }
2565 : }
2566 : else {
2567 0 : nsRect oldVisOverflow(aLine->GetVisualOverflowArea());
2568 0 : aLine->SetLineWrapped(false);
2569 :
2570 0 : rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing);
2571 :
2572 : // We don't really know what changed in the line, so use the union
2573 : // of the old and new combined areas
2574 0 : nsRect dirtyRect;
2575 0 : dirtyRect.UnionRect(oldVisOverflow, aLine->GetVisualOverflowArea());
2576 : #ifdef NOISY_BLOCK_INVALIDATE
2577 : printf("%p invalidate (%d, %d, %d, %d)\n",
2578 : this, dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height);
2579 : if (aLine->IsForceInvalidate())
2580 : printf(" dirty line is %p\n", static_cast<void*>(aLine.get()));
2581 : #endif
2582 0 : Invalidate(dirtyRect);
2583 0 : InvalidateThebesLayersInLineBox(this, aLine);
2584 : }
2585 :
2586 0 : return rv;
2587 : }
2588 :
2589 : nsIFrame*
2590 0 : nsBlockFrame::PullFrame(nsBlockReflowState& aState,
2591 : line_iterator aLine)
2592 : {
2593 : // First check our remaining lines.
2594 0 : if (end_lines() != aLine.next()) {
2595 0 : return PullFrameFrom(aState, aLine, this, false, mFrames, aLine.next());
2596 : }
2597 :
2598 0 : NS_ASSERTION(!GetOverflowLines(),
2599 : "Our overflow lines should have been removed at the start of reflow");
2600 :
2601 : // Try each next-in-flow.
2602 0 : nsBlockFrame* nextInFlow = aState.mNextInFlow;
2603 0 : while (nextInFlow) {
2604 : // first normal lines, then overflow lines
2605 0 : if (!nextInFlow->mLines.empty()) {
2606 : return PullFrameFrom(aState, aLine, nextInFlow, false,
2607 : nextInFlow->mFrames,
2608 0 : nextInFlow->mLines.begin());
2609 : }
2610 :
2611 0 : FrameLines* overflowLines = nextInFlow->GetOverflowLines();
2612 0 : if (overflowLines) {
2613 : return PullFrameFrom(aState, aLine, nextInFlow, true,
2614 : overflowLines->mFrames,
2615 0 : overflowLines->mLines.begin());
2616 : }
2617 :
2618 0 : nextInFlow = static_cast<nsBlockFrame*>(nextInFlow->GetNextInFlow());
2619 0 : aState.mNextInFlow = nextInFlow;
2620 : }
2621 :
2622 0 : return nsnull;
2623 : }
2624 :
2625 : nsIFrame*
2626 0 : nsBlockFrame::PullFrameFrom(nsBlockReflowState& aState,
2627 : nsLineBox* aLine,
2628 : nsBlockFrame* aFromContainer,
2629 : bool aFromOverflowLine,
2630 : nsFrameList& aFromFrameList,
2631 : nsLineList::iterator aFromLine)
2632 : {
2633 0 : nsLineBox* fromLine = aFromLine;
2634 0 : NS_ABORT_IF_FALSE(fromLine, "bad line to pull from");
2635 0 : NS_ABORT_IF_FALSE(fromLine->GetChildCount(), "empty line");
2636 0 : NS_ABORT_IF_FALSE(aLine->GetChildCount(), "empty line");
2637 :
2638 0 : NS_ASSERTION(fromLine->IsBlock() == fromLine->mFirstChild->GetStyleDisplay()->IsBlockOutside(),
2639 : "Disagreement about whether it's a block or not");
2640 :
2641 0 : if (fromLine->IsBlock()) {
2642 : // If our line is not empty and the child in aFromLine is a block
2643 : // then we cannot pull up the frame into this line. In this case
2644 : // we stop pulling.
2645 0 : return nsnull;
2646 : }
2647 : // Take frame from fromLine
2648 0 : nsIFrame* frame = fromLine->mFirstChild;
2649 0 : nsIFrame* newFirstChild = frame->GetNextSibling();
2650 :
2651 0 : if (aFromContainer != this) {
2652 0 : NS_ASSERTION(aState.mPrevChild == aLine->LastChild(),
2653 : "mPrevChild should be the LastChild of the line we are adding to");
2654 : // The frame is being pulled from a next-in-flow; therefore we
2655 : // need to add it to our sibling list.
2656 0 : if (NS_LIKELY(!aFromOverflowLine)) {
2657 0 : NS_ASSERTION(&aFromFrameList == &aFromContainer->mFrames,
2658 : "must be normal flow if not overflow line");
2659 0 : NS_ASSERTION(aFromLine == aFromContainer->mLines.begin(),
2660 : "should only pull from first line");
2661 : }
2662 0 : aFromFrameList.RemoveFrame(frame);
2663 :
2664 : // When pushing and pulling frames we need to check for whether any
2665 : // views need to be reparented
2666 0 : NS_ASSERTION(frame->GetParent() == aFromContainer, "unexpected parent frame");
2667 :
2668 0 : ReparentFrame(frame, aFromContainer, this);
2669 0 : mFrames.InsertFrame(nsnull, aState.mPrevChild, frame);
2670 :
2671 : // The frame might have (or contain) floats that need to be
2672 : // brought over too.
2673 0 : ReparentFloats(frame, aFromContainer, aFromOverflowLine, true);
2674 : }
2675 : // when aFromContainer is 'this', then aLine->LastChild()'s next sibling
2676 : // is already set correctly.
2677 0 : aLine->NoteFrameAdded(frame);
2678 :
2679 0 : if (fromLine->GetChildCount() > 1) {
2680 : // Mark line dirty now that we pulled a child
2681 0 : fromLine->NoteFrameRemoved(frame);
2682 0 : fromLine->MarkDirty();
2683 0 : fromLine->mFirstChild = newFirstChild;
2684 : } else {
2685 : // Free up the fromLine now that it's empty
2686 : // Its bounds might need to be redrawn, though.
2687 : // XXX WHY do we invalidate the bounds AND the combined area? doesn't
2688 : // the combined area always enclose the bounds?
2689 0 : Invalidate(fromLine->mBounds);
2690 : FrameLines* overflowLines =
2691 0 : aFromOverflowLine ? aFromContainer->RemoveOverflowLines() : nsnull;
2692 : nsLineList* fromLineList =
2693 0 : aFromOverflowLine ? &overflowLines->mLines : &aFromContainer->mLines;
2694 0 : if (aFromLine.next() != fromLineList->end())
2695 0 : aFromLine.next()->MarkPreviousMarginDirty();
2696 :
2697 0 : Invalidate(fromLine->GetVisualOverflowArea());
2698 0 : fromLineList->erase(aFromLine);
2699 : // aFromLine is now invalid
2700 0 : aFromContainer->FreeLineBox(fromLine);
2701 :
2702 : // Put any remaining overflow lines back.
2703 0 : if (aFromOverflowLine) {
2704 0 : if (!fromLineList->empty()) {
2705 0 : aFromContainer->SetOverflowLines(overflowLines);
2706 : } else {
2707 0 : delete overflowLines;
2708 : // Now any iterators into fromLineList are invalid (but
2709 : // aFromLine already was invalidated above)
2710 : }
2711 : }
2712 : }
2713 :
2714 : #ifdef DEBUG
2715 0 : VerifyLines(true);
2716 0 : VerifyOverflowSituation();
2717 : #endif
2718 :
2719 0 : return frame;
2720 : }
2721 :
2722 : static void
2723 0 : PlaceFrameView(nsIFrame* aFrame)
2724 : {
2725 0 : if (aFrame->HasView())
2726 0 : nsContainerFrame::PositionFrameView(aFrame);
2727 : else
2728 0 : nsContainerFrame::PositionChildViews(aFrame);
2729 0 : }
2730 :
2731 : void
2732 0 : nsBlockFrame::SlideLine(nsBlockReflowState& aState,
2733 : nsLineBox* aLine, nscoord aDY)
2734 : {
2735 0 : NS_PRECONDITION(aDY != 0, "why slide a line nowhere?");
2736 :
2737 0 : Invalidate(aLine->GetVisualOverflowArea());
2738 : // Adjust line state
2739 0 : aLine->SlideBy(aDY);
2740 0 : Invalidate(aLine->GetVisualOverflowArea());
2741 0 : InvalidateThebesLayersInLineBox(this, aLine);
2742 :
2743 : // Adjust the frames in the line
2744 0 : nsIFrame* kid = aLine->mFirstChild;
2745 0 : if (!kid) {
2746 0 : return;
2747 : }
2748 :
2749 0 : if (aLine->IsBlock()) {
2750 0 : if (aDY) {
2751 0 : nsPoint p = kid->GetPosition();
2752 0 : p.y += aDY;
2753 0 : kid->SetPosition(p);
2754 : }
2755 :
2756 : // Make sure the frame's view and any child views are updated
2757 0 : ::PlaceFrameView(kid);
2758 : }
2759 : else {
2760 : // Adjust the Y coordinate of the frames in the line.
2761 : // Note: we need to re-position views even if aDY is 0, because
2762 : // one of our parent frames may have moved and so the view's position
2763 : // relative to its parent may have changed
2764 0 : PRInt32 n = aLine->GetChildCount();
2765 0 : while (--n >= 0) {
2766 0 : if (aDY) {
2767 0 : nsPoint p = kid->GetPosition();
2768 0 : p.y += aDY;
2769 0 : kid->SetPosition(p);
2770 : }
2771 : // Make sure the frame's view and any child views are updated
2772 0 : ::PlaceFrameView(kid);
2773 0 : kid = kid->GetNextSibling();
2774 : }
2775 : }
2776 : }
2777 :
2778 : NS_IMETHODIMP
2779 0 : nsBlockFrame::AttributeChanged(PRInt32 aNameSpaceID,
2780 : nsIAtom* aAttribute,
2781 : PRInt32 aModType)
2782 : {
2783 : nsresult rv = nsBlockFrameSuper::AttributeChanged(aNameSpaceID,
2784 0 : aAttribute, aModType);
2785 :
2786 0 : if (NS_FAILED(rv)) {
2787 0 : return rv;
2788 : }
2789 0 : if (nsGkAtoms::start == aAttribute) {
2790 0 : nsPresContext* presContext = PresContext();
2791 :
2792 : // XXX Not sure if this is necessary anymore
2793 0 : if (RenumberLists(presContext)) {
2794 0 : presContext->PresShell()->
2795 : FrameNeedsReflow(this, nsIPresShell::eStyleChange,
2796 0 : NS_FRAME_HAS_DIRTY_CHILDREN);
2797 : }
2798 : }
2799 0 : else if (nsGkAtoms::value == aAttribute) {
2800 0 : const nsStyleDisplay* styleDisplay = GetStyleDisplay();
2801 0 : if (NS_STYLE_DISPLAY_LIST_ITEM == styleDisplay->mDisplay) {
2802 : // Search for the closest ancestor that's a block frame. We
2803 : // make the assumption that all related list items share a
2804 : // common block parent.
2805 : // XXXldb I think that's a bad assumption.
2806 0 : nsBlockFrame* blockParent = nsLayoutUtils::FindNearestBlockAncestor(this);
2807 :
2808 : // Tell the enclosing block frame to renumber list items within
2809 : // itself
2810 0 : if (nsnull != blockParent) {
2811 0 : nsPresContext* presContext = PresContext();
2812 : // XXX Not sure if this is necessary anymore
2813 0 : if (blockParent->RenumberLists(presContext)) {
2814 0 : presContext->PresShell()->
2815 : FrameNeedsReflow(blockParent, nsIPresShell::eStyleChange,
2816 0 : NS_FRAME_HAS_DIRTY_CHILDREN);
2817 : }
2818 : }
2819 : }
2820 : }
2821 :
2822 0 : return rv;
2823 : }
2824 :
2825 : static inline bool
2826 0 : IsNonAutoNonZeroHeight(const nsStyleCoord& aCoord)
2827 : {
2828 0 : if (aCoord.GetUnit() == eStyleUnit_Auto)
2829 0 : return false;
2830 0 : if (aCoord.IsCoordPercentCalcUnit()) {
2831 : // If we evaluate the length/percent/calc at a percentage basis of
2832 : // both nscoord_MAX and 0, and it's zero both ways, then it's a zero
2833 : // length, percent, or combination thereof. Test > 0 so we clamp
2834 : // negative calc() results to 0.
2835 0 : return nsRuleNode::ComputeCoordPercentCalc(aCoord, nscoord_MAX) > 0 ||
2836 0 : nsRuleNode::ComputeCoordPercentCalc(aCoord, 0) > 0;
2837 : }
2838 0 : NS_ABORT_IF_FALSE(false, "unexpected unit for height or min-height");
2839 0 : return true;
2840 : }
2841 :
2842 : /* virtual */ bool
2843 0 : nsBlockFrame::IsSelfEmpty()
2844 : {
2845 : // Blocks which are margin-roots (including inline-blocks) cannot be treated
2846 : // as empty for margin-collapsing and other purposes. They're more like
2847 : // replaced elements.
2848 0 : if (GetStateBits() & NS_BLOCK_MARGIN_ROOT)
2849 0 : return false;
2850 :
2851 0 : const nsStylePosition* position = GetStylePosition();
2852 :
2853 0 : if (IsNonAutoNonZeroHeight(position->mMinHeight) ||
2854 0 : IsNonAutoNonZeroHeight(position->mHeight))
2855 0 : return false;
2856 :
2857 0 : const nsStyleBorder* border = GetStyleBorder();
2858 0 : const nsStylePadding* padding = GetStylePadding();
2859 0 : if (border->GetActualBorderWidth(NS_SIDE_TOP) != 0 ||
2860 0 : border->GetActualBorderWidth(NS_SIDE_BOTTOM) != 0 ||
2861 0 : !nsLayoutUtils::IsPaddingZero(padding->mPadding.GetTop()) ||
2862 0 : !nsLayoutUtils::IsPaddingZero(padding->mPadding.GetBottom())) {
2863 0 : return false;
2864 : }
2865 :
2866 0 : if (HasOutsideBullet() && !BulletIsEmpty()) {
2867 0 : return false;
2868 : }
2869 :
2870 0 : return true;
2871 : }
2872 :
2873 : bool
2874 0 : nsBlockFrame::CachedIsEmpty()
2875 : {
2876 0 : if (!IsSelfEmpty()) {
2877 0 : return false;
2878 : }
2879 :
2880 0 : for (line_iterator line = begin_lines(), line_end = end_lines();
2881 : line != line_end;
2882 : ++line)
2883 : {
2884 0 : if (!line->CachedIsEmpty())
2885 0 : return false;
2886 : }
2887 :
2888 0 : return true;
2889 : }
2890 :
2891 : bool
2892 0 : nsBlockFrame::IsEmpty()
2893 : {
2894 0 : if (!IsSelfEmpty()) {
2895 0 : return false;
2896 : }
2897 :
2898 0 : for (line_iterator line = begin_lines(), line_end = end_lines();
2899 : line != line_end;
2900 : ++line)
2901 : {
2902 0 : if (!line->IsEmpty())
2903 0 : return false;
2904 : }
2905 :
2906 0 : return true;
2907 : }
2908 :
2909 : bool
2910 0 : nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState,
2911 : nsLineBox* aLine)
2912 : {
2913 0 : if (aState.GetFlag(BRS_APPLYTOPMARGIN)) {
2914 : // Apply short-circuit check to avoid searching the line list
2915 0 : return true;
2916 : }
2917 :
2918 0 : if (!aState.IsAdjacentWithTop()) {
2919 : // If we aren't at the top Y coordinate then something of non-zero
2920 : // height must have been placed. Therefore the childs top-margin
2921 : // applies.
2922 0 : aState.SetFlag(BRS_APPLYTOPMARGIN, true);
2923 0 : return true;
2924 : }
2925 :
2926 : // Determine if this line is "essentially" the first line
2927 0 : line_iterator line = begin_lines();
2928 0 : if (aState.GetFlag(BRS_HAVELINEADJACENTTOTOP)) {
2929 0 : line = aState.mLineAdjacentToTop;
2930 : }
2931 0 : while (line != aLine) {
2932 0 : if (!line->CachedIsEmpty() || line->HasClearance()) {
2933 : // A line which precedes aLine is non-empty, or has clearance,
2934 : // so therefore the top margin applies.
2935 0 : aState.SetFlag(BRS_APPLYTOPMARGIN, true);
2936 0 : return true;
2937 : }
2938 : // No need to apply the top margin if the line has floats. We
2939 : // should collapse anyway (bug 44419)
2940 0 : ++line;
2941 0 : aState.SetFlag(BRS_HAVELINEADJACENTTOTOP, true);
2942 0 : aState.mLineAdjacentToTop = line;
2943 : }
2944 :
2945 : // The line being reflowed is "essentially" the first line in the
2946 : // block. Therefore its top-margin will be collapsed by the
2947 : // generational collapsing logic with its parent (us).
2948 0 : return false;
2949 : }
2950 :
2951 : nsresult
2952 0 : nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
2953 : line_iterator aLine,
2954 : bool* aKeepReflowGoing)
2955 : {
2956 0 : NS_PRECONDITION(*aKeepReflowGoing, "bad caller");
2957 :
2958 0 : nsresult rv = NS_OK;
2959 :
2960 0 : nsIFrame* frame = aLine->mFirstChild;
2961 0 : if (!frame) {
2962 0 : NS_ASSERTION(false, "program error - unexpected empty line");
2963 0 : return NS_ERROR_NULL_POINTER;
2964 : }
2965 :
2966 : // Prepare the block reflow engine
2967 0 : const nsStyleDisplay* display = frame->GetStyleDisplay();
2968 0 : nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState);
2969 :
2970 0 : PRUint8 breakType = display->mBreakType;
2971 0 : if (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType) {
2972 : breakType = nsLayoutUtils::CombineBreakType(breakType,
2973 0 : aState.mFloatBreakType);
2974 0 : aState.mFloatBreakType = NS_STYLE_CLEAR_NONE;
2975 : }
2976 :
2977 : // Clear past floats before the block if the clear style is not none
2978 0 : aLine->SetBreakTypeBefore(breakType);
2979 :
2980 : // See if we should apply the top margin. If the block frame being
2981 : // reflowed is a continuation (non-null prev-in-flow) then we don't
2982 : // apply its top margin because it's not significant. Otherwise, dig
2983 : // deeper.
2984 : bool applyTopMargin =
2985 0 : !frame->GetPrevInFlow() && ShouldApplyTopMargin(aState, aLine);
2986 :
2987 0 : if (applyTopMargin) {
2988 : // The HasClearance setting is only valid if ShouldApplyTopMargin
2989 : // returned false (in which case the top-margin-root set our
2990 : // clearance flag). Otherwise clear it now. We'll set it later on
2991 : // ourselves if necessary.
2992 0 : aLine->ClearHasClearance();
2993 : }
2994 0 : bool treatWithClearance = aLine->HasClearance();
2995 :
2996 0 : bool mightClearFloats = breakType != NS_STYLE_CLEAR_NONE;
2997 0 : nsIFrame *replacedBlock = nsnull;
2998 0 : if (!nsBlockFrame::BlockCanIntersectFloats(frame)) {
2999 0 : mightClearFloats = true;
3000 0 : replacedBlock = frame;
3001 : }
3002 :
3003 : // If our top margin was counted as part of some parents top-margin
3004 : // collapse and we are being speculatively reflowed assuming this
3005 : // frame DID NOT need clearance, then we need to check that
3006 : // assumption.
3007 0 : if (!treatWithClearance && !applyTopMargin && mightClearFloats &&
3008 : aState.mReflowState.mDiscoveredClearance) {
3009 0 : nscoord curY = aState.mY + aState.mPrevBottomMargin.get();
3010 0 : nscoord clearY = aState.ClearFloats(curY, breakType, replacedBlock);
3011 0 : if (clearY != curY) {
3012 : // Looks like that assumption was invalid, we do need
3013 : // clearance. Tell our ancestor so it can reflow again. It is
3014 : // responsible for actually setting our clearance flag before
3015 : // the next reflow.
3016 0 : treatWithClearance = true;
3017 : // Only record the first frame that requires clearance
3018 0 : if (!*aState.mReflowState.mDiscoveredClearance) {
3019 0 : *aState.mReflowState.mDiscoveredClearance = frame;
3020 : }
3021 0 : aState.mPrevChild = frame;
3022 : // Exactly what we do now is flexible since we'll definitely be
3023 : // reflowed.
3024 0 : return NS_OK;
3025 : }
3026 : }
3027 0 : if (treatWithClearance) {
3028 0 : applyTopMargin = true;
3029 : }
3030 :
3031 0 : nsIFrame* clearanceFrame = nsnull;
3032 0 : nscoord startingY = aState.mY;
3033 0 : nsCollapsingMargin incomingMargin = aState.mPrevBottomMargin;
3034 : nscoord clearance;
3035 : // Save the original position of the frame so that we can reposition
3036 : // its view as needed.
3037 0 : nsPoint originalPosition = frame->GetPosition();
3038 0 : while (true) {
3039 : // Save the frame's current position. We might need it later.
3040 0 : nscoord passOriginalY = frame->GetRect().y;
3041 :
3042 0 : clearance = 0;
3043 0 : nscoord topMargin = 0;
3044 0 : bool mayNeedRetry = false;
3045 0 : bool clearedFloats = false;
3046 0 : if (applyTopMargin) {
3047 : // Precompute the blocks top margin value so that we can get the
3048 : // correct available space (there might be a float that's
3049 : // already been placed below the aState.mPrevBottomMargin
3050 :
3051 : // Setup a reflowState to get the style computed margin-top
3052 : // value. We'll use a reason of `resize' so that we don't fudge
3053 : // any incremental reflow state.
3054 :
3055 : // The availSpace here is irrelevant to our needs - all we want
3056 : // out if this setup is the margin-top value which doesn't depend
3057 : // on the childs available space.
3058 : // XXX building a complete nsHTMLReflowState just to get the margin-top
3059 : // seems like a waste. And we do this for almost every block!
3060 0 : nsSize availSpace(aState.mContentArea.width, NS_UNCONSTRAINEDSIZE);
3061 : nsHTMLReflowState reflowState(aState.mPresContext, aState.mReflowState,
3062 0 : frame, availSpace);
3063 :
3064 0 : if (treatWithClearance) {
3065 0 : aState.mY += aState.mPrevBottomMargin.get();
3066 0 : aState.mPrevBottomMargin.Zero();
3067 : }
3068 :
3069 : // Now compute the collapsed margin-top value into aState.mPrevBottomMargin, assuming
3070 : // that all child margins collapse down to clearanceFrame.
3071 : nsBlockReflowContext::ComputeCollapsedTopMargin(reflowState,
3072 0 : &aState.mPrevBottomMargin, clearanceFrame, &mayNeedRetry);
3073 :
3074 : // XXX optimization; we could check the collapsing children to see if they are sure
3075 : // to require clearance, and so avoid retrying them
3076 :
3077 0 : if (clearanceFrame) {
3078 : // Don't allow retries on the second pass. The clearance decisions for the
3079 : // blocks whose top-margins collapse with ours are now fixed.
3080 0 : mayNeedRetry = false;
3081 : }
3082 :
3083 0 : if (!treatWithClearance && !clearanceFrame && mightClearFloats) {
3084 : // We don't know if we need clearance and this is the first,
3085 : // optimistic pass. So determine whether *this block* needs
3086 : // clearance. Note that we do not allow the decision for whether
3087 : // this block has clearance to change on the second pass; that
3088 : // decision is only allowed to be made under the optimistic
3089 : // first pass.
3090 0 : nscoord curY = aState.mY + aState.mPrevBottomMargin.get();
3091 0 : nscoord clearY = aState.ClearFloats(curY, breakType, replacedBlock);
3092 0 : if (clearY != curY) {
3093 : // Looks like we need clearance and we didn't know about it already. So
3094 : // recompute collapsed margin
3095 0 : treatWithClearance = true;
3096 : // Remember this decision, needed for incremental reflow
3097 0 : aLine->SetHasClearance();
3098 :
3099 : // Apply incoming margins
3100 0 : aState.mY += aState.mPrevBottomMargin.get();
3101 0 : aState.mPrevBottomMargin.Zero();
3102 :
3103 : // Compute the collapsed margin again, ignoring the incoming margin this time
3104 0 : mayNeedRetry = false;
3105 : nsBlockReflowContext::ComputeCollapsedTopMargin(reflowState,
3106 0 : &aState.mPrevBottomMargin, clearanceFrame, &mayNeedRetry);
3107 : }
3108 : }
3109 :
3110 : // Temporarily advance the running Y value so that the
3111 : // GetAvailableSpace method will return the right available
3112 : // space. This undone as soon as the horizontal margins are
3113 : // computed.
3114 0 : topMargin = aState.mPrevBottomMargin.get();
3115 :
3116 0 : if (treatWithClearance) {
3117 0 : nscoord currentY = aState.mY;
3118 : // advance mY to the clear position.
3119 0 : aState.mY = aState.ClearFloats(aState.mY, breakType, replacedBlock);
3120 :
3121 0 : clearedFloats = aState.mY != currentY;
3122 :
3123 : // Compute clearance. It's the amount we need to add to the top
3124 : // border-edge of the frame, after applying collapsed margins
3125 : // from the frame and its children, to get it to line up with
3126 : // the bottom of the floats. The former is currentY + topMargin,
3127 : // the latter is the current aState.mY.
3128 : // Note that negative clearance is possible
3129 0 : clearance = aState.mY - (currentY + topMargin);
3130 :
3131 : // Add clearance to our top margin while we compute available
3132 : // space for the frame
3133 0 : topMargin += clearance;
3134 :
3135 : // Note that aState.mY should stay where it is: at the top
3136 : // border-edge of the frame
3137 : } else {
3138 : // Advance aState.mY to the top border-edge of the frame.
3139 0 : aState.mY += topMargin;
3140 : }
3141 : }
3142 :
3143 : // Here aState.mY is the top border-edge of the block.
3144 : // Compute the available space for the block
3145 0 : nsFlowAreaRect floatAvailableSpace = aState.GetFloatAvailableSpace();
3146 : #ifdef REALLY_NOISY_REFLOW
3147 : printf("setting line %p isImpacted to %s\n",
3148 : aLine.get(), floatAvailableSpace.mHasFloats?"true":"false");
3149 : #endif
3150 0 : aLine->SetLineIsImpactedByFloat(floatAvailableSpace.mHasFloats);
3151 0 : nsRect availSpace;
3152 : aState.ComputeBlockAvailSpace(frame, display, floatAvailableSpace,
3153 0 : replacedBlock != nsnull, availSpace);
3154 :
3155 : // The check for
3156 : // (!aState.mReflowState.mFlags.mIsTopOfPage || clearedFloats)
3157 : // is to some degree out of paranoia: if we reliably eat up top
3158 : // margins at the top of the page as we ought to, it wouldn't be
3159 : // needed.
3160 0 : if ((!aState.mReflowState.mFlags.mIsTopOfPage || clearedFloats) &&
3161 : availSpace.height < 0) {
3162 : // We know already that this child block won't fit on this
3163 : // page/column due to the top margin or the clearance. So we need
3164 : // to get out of here now. (If we don't, most blocks will handle
3165 : // things fine, and report break-before, but zero-height blocks
3166 : // won't, and will thus make their parent overly-large and force
3167 : // *it* to be pushed in its entirety.)
3168 : // Doing this means that we also don't need to worry about the
3169 : // |availSpace.height += topMargin| below interacting with pushed
3170 : // floats (which force nscoord_MAX clearance) to cause a
3171 : // constrained height to turn into an unconstrained one.
3172 0 : aState.mY = startingY;
3173 0 : aState.mPrevBottomMargin = incomingMargin;
3174 0 : PushLines(aState, aLine.prev());
3175 0 : NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
3176 0 : *aKeepReflowGoing = false;
3177 0 : return NS_OK;
3178 : }
3179 :
3180 : // Now put the Y coordinate back to the top of the top-margin +
3181 : // clearance, and flow the block.
3182 0 : aState.mY -= topMargin;
3183 0 : availSpace.y -= topMargin;
3184 0 : if (NS_UNCONSTRAINEDSIZE != availSpace.height) {
3185 0 : availSpace.height += topMargin;
3186 : }
3187 :
3188 : // Reflow the block into the available space
3189 : // construct the html reflow state for the block. ReflowBlock
3190 : // will initialize it
3191 : nsHTMLReflowState blockHtmlRS(aState.mPresContext, aState.mReflowState, frame,
3192 0 : nsSize(availSpace.width, availSpace.height));
3193 0 : blockHtmlRS.mFlags.mHasClearance = aLine->HasClearance();
3194 :
3195 : nsFloatManager::SavedState floatManagerState;
3196 0 : if (mayNeedRetry) {
3197 0 : blockHtmlRS.mDiscoveredClearance = &clearanceFrame;
3198 0 : aState.mFloatManager->PushState(&floatManagerState);
3199 0 : } else if (!applyTopMargin) {
3200 0 : blockHtmlRS.mDiscoveredClearance = aState.mReflowState.mDiscoveredClearance;
3201 : }
3202 :
3203 0 : nsReflowStatus frameReflowStatus = NS_FRAME_COMPLETE;
3204 : rv = brc.ReflowBlock(availSpace, applyTopMargin, aState.mPrevBottomMargin,
3205 0 : clearance, aState.IsAdjacentWithTop(),
3206 0 : aLine.get(), blockHtmlRS, frameReflowStatus, aState);
3207 :
3208 : // If this was a second-pass reflow and the block's vertical position
3209 : // changed, invalidates from the first pass might have happened in the
3210 : // wrong places. Invalidate the entire overflow rect at the new position.
3211 0 : if (!mayNeedRetry && clearanceFrame &&
3212 0 : frame->GetRect().y != passOriginalY) {
3213 0 : Invalidate(frame->GetVisualOverflowRect() + frame->GetPosition());
3214 : }
3215 :
3216 0 : NS_ENSURE_SUCCESS(rv, rv);
3217 :
3218 0 : if (mayNeedRetry && clearanceFrame) {
3219 0 : aState.mFloatManager->PopState(&floatManagerState);
3220 0 : aState.mY = startingY;
3221 0 : aState.mPrevBottomMargin = incomingMargin;
3222 0 : continue;
3223 : }
3224 :
3225 0 : aState.mPrevChild = frame;
3226 :
3227 0 : if (blockHtmlRS.WillReflowAgainForClearance()) {
3228 : // If an ancestor of ours is going to reflow for clearance, we
3229 : // need to avoid calling PlaceBlock, because it unsets dirty bits
3230 : // on the child block (both itself, and through its call to
3231 : // nsFrame::DidReflow), and those dirty bits imply dirtiness for
3232 : // all of the child block, including the lines it didn't reflow.
3233 0 : NS_ASSERTION(originalPosition == frame->GetPosition(),
3234 : "we need to call PositionChildViews");
3235 0 : return NS_OK;
3236 : }
3237 :
3238 : #if defined(REFLOW_STATUS_COVERAGE)
3239 : RecordReflowStatus(true, frameReflowStatus);
3240 : #endif
3241 :
3242 0 : if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus)) {
3243 : // None of the child block fits.
3244 0 : PushLines(aState, aLine.prev());
3245 0 : *aKeepReflowGoing = false;
3246 0 : NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
3247 : }
3248 : else {
3249 : // Note: line-break-after a block is a nop
3250 :
3251 : // Try to place the child block.
3252 : // Don't force the block to fit if we have positive clearance, because
3253 : // pushing it to the next page would give it more room.
3254 : // Don't force the block to fit if it's impacted by a float. If it is,
3255 : // then pushing it to the next page would give it more room. Note that
3256 : // isImpacted doesn't include impact from the block's own floats.
3257 0 : bool forceFit = aState.IsAdjacentWithTop() && clearance <= 0 &&
3258 0 : !floatAvailableSpace.mHasFloats;
3259 0 : nsCollapsingMargin collapsedBottomMargin;
3260 0 : nsOverflowAreas overflowAreas;
3261 : *aKeepReflowGoing = brc.PlaceBlock(blockHtmlRS, forceFit, aLine.get(),
3262 : collapsedBottomMargin,
3263 0 : aLine->mBounds, overflowAreas,
3264 0 : frameReflowStatus);
3265 0 : if (aLine->SetCarriedOutBottomMargin(collapsedBottomMargin)) {
3266 0 : line_iterator nextLine = aLine;
3267 0 : ++nextLine;
3268 0 : if (nextLine != end_lines()) {
3269 0 : nextLine->MarkPreviousMarginDirty();
3270 : }
3271 : }
3272 :
3273 0 : aLine->SetOverflowAreas(overflowAreas);
3274 0 : if (*aKeepReflowGoing) {
3275 : // Some of the child block fit
3276 :
3277 : // Advance to new Y position
3278 0 : nscoord newY = aLine->mBounds.YMost();
3279 0 : aState.mY = newY;
3280 :
3281 : // Continue the block frame now if it didn't completely fit in
3282 : // the available space.
3283 0 : if (!NS_FRAME_IS_FULLY_COMPLETE(frameReflowStatus)) {
3284 : bool madeContinuation;
3285 0 : rv = CreateContinuationFor(aState, nsnull, frame, madeContinuation);
3286 0 : NS_ENSURE_SUCCESS(rv, rv);
3287 :
3288 0 : nsIFrame* nextFrame = frame->GetNextInFlow();
3289 0 : NS_ASSERTION(nextFrame, "We're supposed to have a next-in-flow by now");
3290 :
3291 0 : if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus)) {
3292 : // If nextFrame used to be an overflow container, make it a normal block
3293 0 : if (!madeContinuation &&
3294 0 : (NS_FRAME_IS_OVERFLOW_CONTAINER & nextFrame->GetStateBits())) {
3295 0 : aState.mOverflowTracker->Finish(frame);
3296 : nsContainerFrame* parent =
3297 0 : static_cast<nsContainerFrame*>(nextFrame->GetParent());
3298 0 : rv = parent->StealFrame(aState.mPresContext, nextFrame);
3299 0 : NS_ENSURE_SUCCESS(rv, rv);
3300 0 : if (parent != this)
3301 0 : ReparentFrame(nextFrame, parent, this);
3302 0 : mFrames.InsertFrame(nsnull, frame, nextFrame);
3303 0 : madeContinuation = true; // needs to be added to mLines
3304 0 : nextFrame->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
3305 0 : frameReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
3306 : }
3307 :
3308 : // Push continuation to a new line, but only if we actually made one.
3309 0 : if (madeContinuation) {
3310 0 : nsLineBox* line = NewLineBox(nextFrame, true);
3311 0 : NS_ENSURE_TRUE(line, NS_ERROR_OUT_OF_MEMORY);
3312 0 : mLines.after_insert(aLine, line);
3313 : }
3314 :
3315 0 : PushLines(aState, aLine);
3316 0 : NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
3317 :
3318 : // If we need to reflow the continuation of the block child,
3319 : // then we'd better reflow our continuation
3320 0 : if (frameReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) {
3321 0 : aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
3322 : // We also need to make that continuation's line dirty so
3323 : // it gets reflowed when we reflow our next in flow. The
3324 : // nif's line must always be either a line of the nif's
3325 : // parent block (only if we didn't make a continuation) or
3326 : // else one of our own overflow lines. In the latter case
3327 : // the line is already marked dirty, so just handle the
3328 : // first case.
3329 0 : if (!madeContinuation) {
3330 : nsBlockFrame* nifBlock =
3331 0 : nsLayoutUtils::GetAsBlock(nextFrame->GetParent());
3332 0 : NS_ASSERTION(nifBlock,
3333 : "A block's child's next in flow's parent must be a block!");
3334 0 : for (line_iterator line = nifBlock->begin_lines(),
3335 0 : line_end = nifBlock->end_lines(); line != line_end; ++line) {
3336 0 : if (line->Contains(nextFrame)) {
3337 0 : line->MarkDirty();
3338 0 : break;
3339 : }
3340 : }
3341 : }
3342 : }
3343 0 : *aKeepReflowGoing = false;
3344 :
3345 : // The bottom margin for a block is only applied on the last
3346 : // flow block. Since we just continued the child block frame,
3347 : // we know that line->mFirstChild is not the last flow block
3348 : // therefore zero out the running margin value.
3349 : #ifdef NOISY_VERTICAL_MARGINS
3350 : ListTag(stdout);
3351 : printf(": reflow incomplete, frame=");
3352 : nsFrame::ListTag(stdout, frame);
3353 : printf(" prevBottomMargin=%d, setting to zero\n",
3354 : aState.mPrevBottomMargin);
3355 : #endif
3356 0 : aState.mPrevBottomMargin.Zero();
3357 : }
3358 : else { // frame is complete but its overflow is not complete
3359 : // Disconnect the next-in-flow and put it in our overflow tracker
3360 0 : if (!madeContinuation &&
3361 0 : !(NS_FRAME_IS_OVERFLOW_CONTAINER & nextFrame->GetStateBits())) {
3362 : // It already exists, but as a normal next-in-flow, so we need
3363 : // to dig it out of the child lists.
3364 : nsContainerFrame* parent = static_cast<nsContainerFrame*>
3365 0 : (nextFrame->GetParent());
3366 0 : rv = parent->StealFrame(aState.mPresContext, nextFrame);
3367 0 : NS_ENSURE_SUCCESS(rv, rv);
3368 : }
3369 0 : else if (madeContinuation) {
3370 0 : mFrames.RemoveFrame(nextFrame);
3371 : }
3372 :
3373 : // Put it in our overflow list
3374 0 : aState.mOverflowTracker->Insert(nextFrame, frameReflowStatus);
3375 0 : NS_MergeReflowStatusInto(&aState.mReflowStatus, frameReflowStatus);
3376 :
3377 : #ifdef NOISY_VERTICAL_MARGINS
3378 : ListTag(stdout);
3379 : printf(": reflow complete but overflow incomplete for ");
3380 : nsFrame::ListTag(stdout, frame);
3381 : printf(" prevBottomMargin=%d collapsedBottomMargin=%d\n",
3382 : aState.mPrevBottomMargin, collapsedBottomMargin.get());
3383 : #endif
3384 0 : aState.mPrevBottomMargin = collapsedBottomMargin;
3385 0 : }
3386 : }
3387 : else { // frame is fully complete
3388 : #ifdef NOISY_VERTICAL_MARGINS
3389 : ListTag(stdout);
3390 : printf(": reflow complete for ");
3391 : nsFrame::ListTag(stdout, frame);
3392 : printf(" prevBottomMargin=%d collapsedBottomMargin=%d\n",
3393 : aState.mPrevBottomMargin, collapsedBottomMargin.get());
3394 : #endif
3395 0 : aState.mPrevBottomMargin = collapsedBottomMargin;
3396 : }
3397 : #ifdef NOISY_VERTICAL_MARGINS
3398 : ListTag(stdout);
3399 : printf(": frame=");
3400 : nsFrame::ListTag(stdout, frame);
3401 : printf(" carriedOutBottomMargin=%d collapsedBottomMargin=%d => %d\n",
3402 : brc.GetCarriedOutBottomMargin(), collapsedBottomMargin.get(),
3403 : aState.mPrevBottomMargin);
3404 : #endif
3405 : }
3406 : else {
3407 : // None of the block fits. Determine the correct reflow status.
3408 0 : if (aLine == mLines.front() && !GetPrevInFlow()) {
3409 : // If it's our very first line then we need to be pushed to
3410 : // our parents next-in-flow. Therefore, return break-before
3411 : // status for our reflow status.
3412 0 : aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
3413 : }
3414 : else {
3415 : // Push the line that didn't fit and any lines that follow it
3416 : // to our next-in-flow.
3417 0 : PushLines(aState, aLine.prev());
3418 0 : NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
3419 : }
3420 : }
3421 : }
3422 0 : break; // out of the reflow retry loop
3423 : }
3424 :
3425 : // Now that we've got its final position all figured out, position any child
3426 : // views it may have. Note that the case when frame has a view got handled
3427 : // by FinishReflowChild, but that function didn't have the coordinates needed
3428 : // to correctly decide whether to reposition child views.
3429 0 : if (originalPosition != frame->GetPosition() && !frame->HasView()) {
3430 0 : nsContainerFrame::PositionChildViews(frame);
3431 : }
3432 :
3433 : #ifdef DEBUG
3434 0 : VerifyLines(true);
3435 : #endif
3436 0 : return rv;
3437 : }
3438 :
3439 : nsresult
3440 0 : nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState,
3441 : line_iterator aLine,
3442 : bool* aKeepReflowGoing)
3443 : {
3444 0 : nsresult rv = NS_OK;
3445 0 : *aKeepReflowGoing = true;
3446 :
3447 0 : aLine->SetLineIsImpactedByFloat(false);
3448 :
3449 : // Setup initial coordinate system for reflowing the inline frames
3450 : // into. Apply a previous block frame's bottom margin first.
3451 0 : if (ShouldApplyTopMargin(aState, aLine)) {
3452 0 : aState.mY += aState.mPrevBottomMargin.get();
3453 : }
3454 0 : nsFlowAreaRect floatAvailableSpace = aState.GetFloatAvailableSpace();
3455 :
3456 : LineReflowStatus lineReflowStatus;
3457 0 : do {
3458 0 : nscoord availableSpaceHeight = 0;
3459 0 : do {
3460 0 : bool allowPullUp = true;
3461 0 : nsIContent* forceBreakInContent = nsnull;
3462 0 : PRInt32 forceBreakOffset = -1;
3463 0 : gfxBreakPriority forceBreakPriority = eNoBreak;
3464 0 : do {
3465 : nsFloatManager::SavedState floatManagerState;
3466 0 : aState.mReflowState.mFloatManager->PushState(&floatManagerState);
3467 :
3468 : // Once upon a time we allocated the first 30 nsLineLayout objects
3469 : // on the stack, and then we switched to the heap. At that time
3470 : // these objects were large (1100 bytes on a 32 bit system).
3471 : // Then the nsLineLayout object was shrunk to 156 bytes by
3472 : // removing some internal buffers. Given that it is so much
3473 : // smaller, the complexity of 2 different ways of allocating
3474 : // no longer makes sense. Now we always allocate on the stack.
3475 : nsLineLayout lineLayout(aState.mPresContext,
3476 : aState.mReflowState.mFloatManager,
3477 0 : &aState.mReflowState, &aLine);
3478 0 : lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber);
3479 0 : if (forceBreakInContent) {
3480 0 : lineLayout.ForceBreakAtPosition(forceBreakInContent, forceBreakOffset);
3481 : }
3482 : rv = DoReflowInlineFrames(aState, lineLayout, aLine,
3483 : floatAvailableSpace, availableSpaceHeight,
3484 : &floatManagerState, aKeepReflowGoing,
3485 0 : &lineReflowStatus, allowPullUp);
3486 0 : lineLayout.EndLineReflow();
3487 :
3488 0 : if (NS_FAILED(rv)) {
3489 0 : return rv;
3490 : }
3491 :
3492 0 : if (LINE_REFLOW_REDO_NO_PULL == lineReflowStatus ||
3493 : LINE_REFLOW_REDO_MORE_FLOATS == lineReflowStatus ||
3494 : LINE_REFLOW_REDO_NEXT_BAND == lineReflowStatus) {
3495 0 : if (lineLayout.NeedsBackup()) {
3496 0 : NS_ASSERTION(!forceBreakInContent, "Backing up twice; this should never be necessary");
3497 : // If there is no saved break position, then this will set
3498 : // set forceBreakInContent to null and we won't back up, which is
3499 : // correct.
3500 0 : forceBreakInContent = lineLayout.GetLastOptionalBreakPosition(&forceBreakOffset, &forceBreakPriority);
3501 : } else {
3502 0 : forceBreakInContent = nsnull;
3503 : }
3504 : // restore the float manager state
3505 0 : aState.mReflowState.mFloatManager->PopState(&floatManagerState);
3506 : // Clear out float lists
3507 0 : aState.mCurrentLineFloats.DeleteAll();
3508 0 : aState.mBelowCurrentLineFloats.DeleteAll();
3509 : }
3510 :
3511 : // Don't allow pullup on a subsequent LINE_REFLOW_REDO_NO_PULL pass
3512 0 : allowPullUp = false;
3513 : } while (LINE_REFLOW_REDO_NO_PULL == lineReflowStatus);
3514 : } while (LINE_REFLOW_REDO_MORE_FLOATS == lineReflowStatus);
3515 : } while (LINE_REFLOW_REDO_NEXT_BAND == lineReflowStatus);
3516 :
3517 0 : return rv;
3518 : }
3519 :
3520 : void
3521 0 : nsBlockFrame::PushTruncatedLine(nsBlockReflowState& aState,
3522 : line_iterator aLine,
3523 : bool& aKeepReflowGoing)
3524 : {
3525 0 : line_iterator prevLine = aLine;
3526 0 : --prevLine;
3527 0 : PushLines(aState, prevLine);
3528 0 : aKeepReflowGoing = false;
3529 0 : NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
3530 0 : }
3531 :
3532 : #ifdef DEBUG
3533 : static const char* LineReflowStatusNames[] = {
3534 : "LINE_REFLOW_OK", "LINE_REFLOW_STOP", "LINE_REFLOW_REDO_NO_PULL",
3535 : "LINE_REFLOW_REDO_MORE_FLOATS",
3536 : "LINE_REFLOW_REDO_NEXT_BAND", "LINE_REFLOW_TRUNCATED"
3537 : };
3538 : #endif
3539 :
3540 : nsresult
3541 0 : nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState,
3542 : nsLineLayout& aLineLayout,
3543 : line_iterator aLine,
3544 : nsFlowAreaRect& aFloatAvailableSpace,
3545 : nscoord& aAvailableSpaceHeight,
3546 : nsFloatManager::SavedState*
3547 : aFloatStateBeforeLine,
3548 : bool* aKeepReflowGoing,
3549 : LineReflowStatus* aLineReflowStatus,
3550 : bool aAllowPullUp)
3551 : {
3552 : // Forget all of the floats on the line
3553 0 : aLine->FreeFloats(aState.mFloatCacheFreeList);
3554 0 : aState.mFloatOverflowAreas.Clear();
3555 :
3556 : // We need to set this flag on the line if any of our reflow passes
3557 : // are impacted by floats.
3558 0 : if (aFloatAvailableSpace.mHasFloats)
3559 0 : aLine->SetLineIsImpactedByFloat(true);
3560 : #ifdef REALLY_NOISY_REFLOW
3561 : printf("nsBlockFrame::DoReflowInlineFrames %p impacted = %d\n",
3562 : this, aFloatAvailableSpace.mHasFloats);
3563 : #endif
3564 :
3565 0 : nscoord x = aFloatAvailableSpace.mRect.x;
3566 0 : nscoord availWidth = aFloatAvailableSpace.mRect.width;
3567 : nscoord availHeight;
3568 0 : if (aState.GetFlag(BRS_UNCONSTRAINEDHEIGHT)) {
3569 0 : availHeight = NS_UNCONSTRAINEDSIZE;
3570 : }
3571 : else {
3572 : /* XXX get the height right! */
3573 0 : availHeight = aFloatAvailableSpace.mRect.height;
3574 : }
3575 :
3576 : // Make sure to enable resize optimization before we call BeginLineReflow
3577 : // because it might get disabled there
3578 0 : aLine->EnableResizeReflowOptimization();
3579 :
3580 : // For unicode-bidi: plaintext, we need to get the direction of the line from
3581 : // the resolved paragraph level of the first frame on the line, not the block
3582 : // frame, because the block frame could be split by hard line breaks into
3583 : // multiple paragraphs with different base direction
3584 : PRUint8 direction;
3585 0 : if (GetStyleTextReset()->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_PLAINTEXT) {
3586 0 : FramePropertyTable *propTable = aState.mPresContext->PropertyTable();
3587 0 : direction = NS_PTR_TO_INT32(propTable->Get(aLine->mFirstChild,
3588 0 : BaseLevelProperty())) & 1;
3589 : } else {
3590 0 : direction = GetStyleVisibility()->mDirection;
3591 : }
3592 :
3593 : aLineLayout.BeginLineReflow(x, aState.mY,
3594 : availWidth, availHeight,
3595 : aFloatAvailableSpace.mHasFloats,
3596 : false, /*XXX isTopOfPage*/
3597 0 : direction);
3598 :
3599 0 : aState.SetFlag(BRS_LINE_LAYOUT_EMPTY, false);
3600 :
3601 : // XXX Unfortunately we need to know this before reflowing the first
3602 : // inline frame in the line. FIX ME.
3603 0 : if ((0 == aLineLayout.GetLineNumber()) &&
3604 : (NS_BLOCK_HAS_FIRST_LETTER_CHILD & mState) &&
3605 : (NS_BLOCK_HAS_FIRST_LETTER_STYLE & mState)) {
3606 0 : aLineLayout.SetFirstLetterStyleOK(true);
3607 : }
3608 0 : NS_ASSERTION(!((NS_BLOCK_HAS_FIRST_LETTER_CHILD & mState) &&
3609 : GetPrevContinuation()),
3610 : "first letter child bit should only be on first continuation");
3611 :
3612 : // Reflow the frames that are already on the line first
3613 0 : nsresult rv = NS_OK;
3614 0 : LineReflowStatus lineReflowStatus = LINE_REFLOW_OK;
3615 : PRInt32 i;
3616 0 : nsIFrame* frame = aLine->mFirstChild;
3617 :
3618 0 : if (aFloatAvailableSpace.mHasFloats) {
3619 : // There is a soft break opportunity at the start of the line, because
3620 : // we can always move this line down below float(s).
3621 0 : if (aLineLayout.NotifyOptionalBreakPosition(frame->GetContent(), 0, true, eNormalBreak)) {
3622 0 : lineReflowStatus = LINE_REFLOW_REDO_NEXT_BAND;
3623 : }
3624 : }
3625 :
3626 : // need to repeatedly call GetChildCount here, because the child
3627 : // count can change during the loop!
3628 0 : for (i = 0; LINE_REFLOW_OK == lineReflowStatus && i < aLine->GetChildCount();
3629 : i++, frame = frame->GetNextSibling()) {
3630 : rv = ReflowInlineFrame(aState, aLineLayout, aLine, frame,
3631 0 : &lineReflowStatus);
3632 0 : NS_ENSURE_SUCCESS(rv, rv);
3633 0 : if (LINE_REFLOW_OK != lineReflowStatus) {
3634 : // It is possible that one or more of next lines are empty
3635 : // (because of DeleteNextInFlowChild). If so, delete them now
3636 : // in case we are finished.
3637 0 : ++aLine;
3638 0 : while ((aLine != end_lines()) && (0 == aLine->GetChildCount())) {
3639 : // XXX Is this still necessary now that DeleteNextInFlowChild
3640 : // uses DoRemoveFrame?
3641 0 : nsLineBox *toremove = aLine;
3642 0 : aLine = mLines.erase(aLine);
3643 0 : NS_ASSERTION(nsnull == toremove->mFirstChild, "bad empty line");
3644 0 : FreeLineBox(toremove);
3645 : }
3646 0 : --aLine;
3647 :
3648 0 : NS_ASSERTION(lineReflowStatus != LINE_REFLOW_TRUNCATED,
3649 : "ReflowInlineFrame should never determine that a line "
3650 : "needs to go to the next page/column");
3651 : }
3652 : }
3653 :
3654 : // Don't pull up new frames into lines with continuation placeholders
3655 0 : if (aAllowPullUp) {
3656 : // Pull frames and reflow them until we can't
3657 0 : while (LINE_REFLOW_OK == lineReflowStatus) {
3658 0 : frame = PullFrame(aState, aLine);
3659 0 : if (!frame) {
3660 0 : break;
3661 : }
3662 :
3663 0 : while (LINE_REFLOW_OK == lineReflowStatus) {
3664 0 : PRInt32 oldCount = aLine->GetChildCount();
3665 : rv = ReflowInlineFrame(aState, aLineLayout, aLine, frame,
3666 0 : &lineReflowStatus);
3667 0 : NS_ENSURE_SUCCESS(rv, rv);
3668 0 : if (aLine->GetChildCount() != oldCount) {
3669 : // We just created a continuation for aFrame AND its going
3670 : // to end up on this line (e.g. :first-letter
3671 : // situation). Therefore we have to loop here before trying
3672 : // to pull another frame.
3673 0 : frame = frame->GetNextSibling();
3674 : }
3675 : else {
3676 0 : break;
3677 : }
3678 : }
3679 : }
3680 : }
3681 :
3682 0 : aState.SetFlag(BRS_LINE_LAYOUT_EMPTY, aLineLayout.LineIsEmpty());
3683 :
3684 : // We only need to backup if the line isn't going to be reflowed again anyway
3685 0 : bool needsBackup = aLineLayout.NeedsBackup() &&
3686 0 : (lineReflowStatus == LINE_REFLOW_STOP || lineReflowStatus == LINE_REFLOW_OK);
3687 0 : if (needsBackup && aLineLayout.HaveForcedBreakPosition()) {
3688 : NS_WARNING("We shouldn't be backing up more than once! "
3689 : "Someone must have set a break opportunity beyond the available width, "
3690 0 : "even though there were better break opportunities before it");
3691 0 : needsBackup = false;
3692 : }
3693 0 : if (needsBackup) {
3694 : // We need to try backing up to before a text run
3695 : PRInt32 offset;
3696 : gfxBreakPriority breakPriority;
3697 0 : nsIContent* breakContent = aLineLayout.GetLastOptionalBreakPosition(&offset, &breakPriority);
3698 : // XXX It's possible, in fact not unusual, for the break opportunity to already
3699 : // be the end of the line. We should detect that and optimize to not
3700 : // re-do the line.
3701 0 : if (breakContent) {
3702 : // We can back up!
3703 0 : lineReflowStatus = LINE_REFLOW_REDO_NO_PULL;
3704 : }
3705 : } else {
3706 : // In case we reflow this line again, remember that we don't
3707 : // need to force any breaking
3708 0 : aLineLayout.ClearOptionalBreakPosition();
3709 : }
3710 :
3711 0 : if (LINE_REFLOW_REDO_NEXT_BAND == lineReflowStatus) {
3712 : // This happens only when we have a line that is impacted by
3713 : // floats and the first element in the line doesn't fit with
3714 : // the floats.
3715 : //
3716 : // What we do is to advance past the first float we find and
3717 : // then reflow the line all over again.
3718 0 : NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aFloatAvailableSpace.mRect.height,
3719 : "unconstrained height on totally empty line");
3720 :
3721 : // See the analogous code for blocks in nsBlockReflowState::ClearFloats.
3722 0 : if (aFloatAvailableSpace.mRect.height > 0) {
3723 0 : NS_ASSERTION(aFloatAvailableSpace.mHasFloats,
3724 : "redo line on totally empty line with non-empty band...");
3725 : // We should never hit this case if we've placed floats on the
3726 : // line; if we have, then the GetFloatAvailableSpace call is wrong
3727 : // and needs to happen after the caller pops the space manager
3728 : // state.
3729 0 : aState.mFloatManager->AssertStateMatches(aFloatStateBeforeLine);
3730 0 : aState.mY += aFloatAvailableSpace.mRect.height;
3731 0 : aFloatAvailableSpace = aState.GetFloatAvailableSpace();
3732 : } else {
3733 0 : NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mReflowState.availableHeight,
3734 : "We shouldn't be running out of height here");
3735 0 : if (NS_UNCONSTRAINEDSIZE == aState.mReflowState.availableHeight) {
3736 : // just move it down a bit to try to get out of this mess
3737 0 : aState.mY += 1;
3738 : // We should never hit this case if we've placed floats on the
3739 : // line; if we have, then the GetFloatAvailableSpace call is wrong
3740 : // and needs to happen after the caller pops the space manager
3741 : // state.
3742 0 : aState.mFloatManager->AssertStateMatches(aFloatStateBeforeLine);
3743 0 : aFloatAvailableSpace = aState.GetFloatAvailableSpace();
3744 : } else {
3745 : // There's nowhere to retry placing the line, so we want to push
3746 : // it to the next page/column where its contents can fit not
3747 : // next to a float.
3748 0 : lineReflowStatus = LINE_REFLOW_TRUNCATED;
3749 : // Push the line that didn't fit
3750 0 : PushTruncatedLine(aState, aLine, *aKeepReflowGoing);
3751 : }
3752 : }
3753 :
3754 : // XXX: a small optimization can be done here when paginating:
3755 : // if the new Y coordinate is past the end of the block then
3756 : // push the line and return now instead of later on after we are
3757 : // past the float.
3758 : }
3759 0 : else if (LINE_REFLOW_TRUNCATED != lineReflowStatus &&
3760 : LINE_REFLOW_REDO_NO_PULL != lineReflowStatus) {
3761 : // If we are propagating out a break-before status then there is
3762 : // no point in placing the line.
3763 0 : if (!NS_INLINE_IS_BREAK_BEFORE(aState.mReflowStatus)) {
3764 0 : if (!PlaceLine(aState, aLineLayout, aLine, aFloatStateBeforeLine,
3765 : aFloatAvailableSpace.mRect, aAvailableSpaceHeight,
3766 0 : aKeepReflowGoing)) {
3767 0 : lineReflowStatus = LINE_REFLOW_REDO_MORE_FLOATS;
3768 : // PlaceLine already called GetAvailableSpaceForHeight for us.
3769 : }
3770 : }
3771 : }
3772 : #ifdef DEBUG
3773 0 : if (gNoisyReflow) {
3774 0 : printf("Line reflow status = %s\n", LineReflowStatusNames[lineReflowStatus]);
3775 : }
3776 : #endif
3777 :
3778 0 : if (aLineLayout.GetDirtyNextLine()) {
3779 : // aLine may have been pushed to the overflow lines.
3780 0 : FrameLines* overflowLines = GetOverflowLines();
3781 : // We can't just compare iterators front() to aLine here, since they may be in
3782 : // different lists.
3783 : bool pushedToOverflowLines = overflowLines &&
3784 0 : overflowLines->mLines.front() == aLine.get();
3785 0 : if (pushedToOverflowLines) {
3786 : // aLine is stale, it's associated with the main line list but it should
3787 : // be associated with the overflow line list now
3788 0 : aLine = overflowLines->mLines.begin();
3789 : }
3790 0 : nsBlockInFlowLineIterator iter(this, aLine, pushedToOverflowLines);
3791 0 : if (iter.Next() && iter.GetLine()->IsInline()) {
3792 0 : iter.GetLine()->MarkDirty();
3793 0 : if (iter.GetContainer() != this) {
3794 0 : aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
3795 : }
3796 : }
3797 : }
3798 :
3799 0 : *aLineReflowStatus = lineReflowStatus;
3800 :
3801 0 : return rv;
3802 : }
3803 :
3804 : /**
3805 : * Reflow an inline frame. The reflow status is mapped from the frames
3806 : * reflow status to the lines reflow status (not to our reflow status).
3807 : * The line reflow status is simple: true means keep placing frames
3808 : * on the line; false means don't (the line is done). If the line
3809 : * has some sort of breaking affect then aLine's break-type will be set
3810 : * to something other than NS_STYLE_CLEAR_NONE.
3811 : */
3812 : nsresult
3813 0 : nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
3814 : nsLineLayout& aLineLayout,
3815 : line_iterator aLine,
3816 : nsIFrame* aFrame,
3817 : LineReflowStatus* aLineReflowStatus)
3818 : {
3819 0 : NS_ENSURE_ARG_POINTER(aFrame);
3820 :
3821 0 : *aLineReflowStatus = LINE_REFLOW_OK;
3822 :
3823 : #ifdef NOISY_FIRST_LETTER
3824 : ListTag(stdout);
3825 : printf(": reflowing ");
3826 : nsFrame::ListTag(stdout, aFrame);
3827 : printf(" reflowingFirstLetter=%s\n",
3828 : aLineLayout.GetFirstLetterStyleOK() ? "on" : "off");
3829 : #endif
3830 :
3831 : // Reflow the inline frame
3832 : nsReflowStatus frameReflowStatus;
3833 : bool pushedFrame;
3834 : nsresult rv = aLineLayout.ReflowFrame(aFrame, frameReflowStatus,
3835 0 : nsnull, pushedFrame);
3836 0 : NS_ENSURE_SUCCESS(rv, rv);
3837 :
3838 0 : if (frameReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) {
3839 0 : aLineLayout.SetDirtyNextLine();
3840 : }
3841 :
3842 0 : NS_ENSURE_SUCCESS(rv, rv);
3843 : #ifdef REALLY_NOISY_REFLOW_CHILD
3844 : nsFrame::ListTag(stdout, aFrame);
3845 : printf(": status=%x\n", frameReflowStatus);
3846 : #endif
3847 :
3848 : #if defined(REFLOW_STATUS_COVERAGE)
3849 : RecordReflowStatus(false, frameReflowStatus);
3850 : #endif
3851 :
3852 : // Send post-reflow notification
3853 0 : aState.mPrevChild = aFrame;
3854 :
3855 : /* XXX
3856 : This is where we need to add logic to handle some odd behavior.
3857 : For one thing, we should usually place at least one thing next
3858 : to a left float, even when that float takes up all the width on a line.
3859 : see bug 22496
3860 : */
3861 :
3862 : // Process the child frames reflow status. There are 5 cases:
3863 : // complete, not-complete, break-before, break-after-complete,
3864 : // break-after-not-complete. There are two situations: we are a
3865 : // block or we are an inline. This makes a total of 10 cases
3866 : // (fortunately, there is some overlap).
3867 0 : aLine->SetBreakTypeAfter(NS_STYLE_CLEAR_NONE);
3868 0 : if (NS_INLINE_IS_BREAK(frameReflowStatus) ||
3869 : (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType)) {
3870 : // Always abort the line reflow (because a line break is the
3871 : // minimal amount of break we do).
3872 0 : *aLineReflowStatus = LINE_REFLOW_STOP;
3873 :
3874 : // XXX what should aLine's break-type be set to in all these cases?
3875 0 : PRUint8 breakType = NS_INLINE_GET_BREAK_TYPE(frameReflowStatus);
3876 0 : NS_ASSERTION((NS_STYLE_CLEAR_NONE != breakType) ||
3877 : (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType), "bad break type");
3878 0 : NS_ASSERTION(NS_STYLE_CLEAR_PAGE != breakType, "no page breaks yet");
3879 :
3880 0 : if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus)) {
3881 : // Break-before cases.
3882 0 : if (aFrame == aLine->mFirstChild) {
3883 : // If we break before the first frame on the line then we must
3884 : // be trying to place content where there's no room (e.g. on a
3885 : // line with wide floats). Inform the caller to reflow the
3886 : // line after skipping past a float.
3887 0 : *aLineReflowStatus = LINE_REFLOW_REDO_NEXT_BAND;
3888 : }
3889 : else {
3890 : // It's not the first child on this line so go ahead and split
3891 : // the line. We will see the frame again on the next-line.
3892 0 : rv = SplitLine(aState, aLineLayout, aLine, aFrame, aLineReflowStatus);
3893 0 : NS_ENSURE_SUCCESS(rv, rv);
3894 :
3895 : // If we're splitting the line because the frame didn't fit and it
3896 : // was pushed, then mark the line as having word wrapped. We need to
3897 : // know that if we're shrink wrapping our width
3898 0 : if (pushedFrame) {
3899 0 : aLine->SetLineWrapped(true);
3900 : }
3901 : }
3902 : }
3903 : else {
3904 : // If a float split and its prev-in-flow was followed by a <BR>, then combine
3905 : // the <BR>'s break type with the inline's break type (the inline will be the very
3906 : // next frame after the split float).
3907 0 : if (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType) {
3908 : breakType = nsLayoutUtils::CombineBreakType(breakType,
3909 0 : aState.mFloatBreakType);
3910 0 : aState.mFloatBreakType = NS_STYLE_CLEAR_NONE;
3911 : }
3912 : // Break-after cases
3913 0 : if (breakType == NS_STYLE_CLEAR_LINE) {
3914 0 : if (!aLineLayout.GetLineEndsInBR()) {
3915 0 : breakType = NS_STYLE_CLEAR_NONE;
3916 : }
3917 : }
3918 0 : aLine->SetBreakTypeAfter(breakType);
3919 0 : if (NS_FRAME_IS_COMPLETE(frameReflowStatus)) {
3920 : // Split line, but after the frame just reflowed
3921 0 : rv = SplitLine(aState, aLineLayout, aLine, aFrame->GetNextSibling(), aLineReflowStatus);
3922 0 : NS_ENSURE_SUCCESS(rv, rv);
3923 :
3924 0 : if (NS_INLINE_IS_BREAK_AFTER(frameReflowStatus) &&
3925 0 : !aLineLayout.GetLineEndsInBR()) {
3926 0 : aLineLayout.SetDirtyNextLine();
3927 : }
3928 : }
3929 : }
3930 : }
3931 :
3932 0 : if (!NS_FRAME_IS_FULLY_COMPLETE(frameReflowStatus)) {
3933 : // Create a continuation for the incomplete frame. Note that the
3934 : // frame may already have a continuation.
3935 0 : nsIAtom* frameType = aFrame->GetType();
3936 :
3937 : bool madeContinuation;
3938 0 : rv = CreateContinuationFor(aState, aLine, aFrame, madeContinuation);
3939 0 : NS_ENSURE_SUCCESS(rv, rv);
3940 :
3941 : // Remember that the line has wrapped
3942 0 : if (!aLineLayout.GetLineEndsInBR()) {
3943 0 : aLine->SetLineWrapped(true);
3944 : }
3945 :
3946 : // If we just ended a first-letter frame or reflowed a placeholder then
3947 : // don't split the line and don't stop the line reflow...
3948 : // But if we are going to stop anyways we'd better split the line.
3949 0 : if ((!(frameReflowStatus & NS_INLINE_BREAK_FIRST_LETTER_COMPLETE) &&
3950 : nsGkAtoms::placeholderFrame != frameType) ||
3951 : *aLineReflowStatus == LINE_REFLOW_STOP) {
3952 : // Split line after the current frame
3953 0 : *aLineReflowStatus = LINE_REFLOW_STOP;
3954 0 : rv = SplitLine(aState, aLineLayout, aLine, aFrame->GetNextSibling(), aLineReflowStatus);
3955 0 : NS_ENSURE_SUCCESS(rv, rv);
3956 : }
3957 : }
3958 :
3959 0 : return NS_OK;
3960 : }
3961 :
3962 : nsresult
3963 0 : nsBlockFrame::CreateContinuationFor(nsBlockReflowState& aState,
3964 : nsLineBox* aLine,
3965 : nsIFrame* aFrame,
3966 : bool& aMadeNewFrame)
3967 : {
3968 0 : aMadeNewFrame = false;
3969 :
3970 0 : if (!aFrame->GetNextInFlow()) {
3971 : nsIFrame* newFrame;
3972 : nsresult rv = aState.mPresContext->PresShell()->FrameConstructor()->
3973 0 : CreateContinuingFrame(aState.mPresContext, aFrame, this, &newFrame);
3974 0 : if (NS_FAILED(rv)) {
3975 0 : return rv;
3976 : }
3977 :
3978 0 : mFrames.InsertFrame(nsnull, aFrame, newFrame);
3979 :
3980 0 : if (aLine) {
3981 0 : aLine->NoteFrameAdded(newFrame);
3982 : }
3983 :
3984 0 : aMadeNewFrame = true;
3985 : }
3986 : #ifdef DEBUG
3987 0 : VerifyLines(false);
3988 : #endif
3989 0 : return NS_OK;
3990 : }
3991 :
3992 : nsresult
3993 0 : nsBlockFrame::SplitFloat(nsBlockReflowState& aState,
3994 : nsIFrame* aFloat,
3995 : nsReflowStatus aFloatStatus)
3996 : {
3997 0 : nsIFrame* nextInFlow = aFloat->GetNextInFlow();
3998 0 : if (nextInFlow) {
3999 : nsContainerFrame *oldParent =
4000 0 : static_cast<nsContainerFrame*>(nextInFlow->GetParent());
4001 0 : DebugOnly<nsresult> rv = oldParent->StealFrame(aState.mPresContext, nextInFlow);
4002 0 : NS_ASSERTION(NS_SUCCEEDED(rv), "StealFrame failed");
4003 0 : if (oldParent != this) {
4004 0 : ReparentFrame(nextInFlow, oldParent, this);
4005 : }
4006 : } else {
4007 : nsresult rv = aState.mPresContext->PresShell()->FrameConstructor()->
4008 0 : CreateContinuingFrame(aState.mPresContext, aFloat, this, &nextInFlow);
4009 0 : NS_ENSURE_SUCCESS(rv, rv);
4010 : }
4011 0 : if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aFloatStatus))
4012 0 : aFloat->GetNextInFlow()->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
4013 :
4014 : // The containing block is now overflow-incomplete.
4015 0 : NS_FRAME_SET_OVERFLOW_INCOMPLETE(aState.mReflowStatus);
4016 :
4017 0 : if (aFloat->GetStyleDisplay()->mFloats == NS_STYLE_FLOAT_LEFT) {
4018 0 : aState.mFloatManager->SetSplitLeftFloatAcrossBreak();
4019 : } else {
4020 0 : NS_ABORT_IF_FALSE(aFloat->GetStyleDisplay()->mFloats ==
4021 : NS_STYLE_FLOAT_RIGHT, "unexpected float side");
4022 0 : aState.mFloatManager->SetSplitRightFloatAcrossBreak();
4023 : }
4024 :
4025 0 : aState.AppendPushedFloat(nextInFlow);
4026 0 : return NS_OK;
4027 : }
4028 :
4029 : static nsFloatCache*
4030 0 : GetLastFloat(nsLineBox* aLine)
4031 : {
4032 0 : nsFloatCache* fc = aLine->GetFirstFloat();
4033 0 : while (fc && fc->Next()) {
4034 0 : fc = fc->Next();
4035 : }
4036 0 : return fc;
4037 : }
4038 :
4039 : static bool
4040 0 : CheckPlaceholderInLine(nsIFrame* aBlock, nsLineBox* aLine, nsFloatCache* aFC)
4041 : {
4042 0 : if (!aFC)
4043 0 : return true;
4044 0 : NS_ASSERTION(!aFC->mFloat->GetPrevContinuation(),
4045 : "float in a line should never be a continuation");
4046 0 : NS_ASSERTION(!(aFC->mFloat->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT),
4047 : "float in a line should never be a pushed float");
4048 : nsIFrame* ph = aBlock->PresContext()->FrameManager()->
4049 0 : GetPlaceholderFrameFor(aFC->mFloat->GetFirstInFlow());
4050 0 : for (nsIFrame* f = ph; f; f = f->GetParent()) {
4051 0 : if (f->GetParent() == aBlock)
4052 0 : return aLine->Contains(f);
4053 : }
4054 0 : NS_ASSERTION(false, "aBlock is not an ancestor of aFrame!");
4055 0 : return true;
4056 : }
4057 :
4058 : nsresult
4059 0 : nsBlockFrame::SplitLine(nsBlockReflowState& aState,
4060 : nsLineLayout& aLineLayout,
4061 : line_iterator aLine,
4062 : nsIFrame* aFrame,
4063 : LineReflowStatus* aLineReflowStatus)
4064 : {
4065 0 : NS_ABORT_IF_FALSE(aLine->IsInline(), "illegal SplitLine on block line");
4066 :
4067 0 : PRInt32 pushCount = aLine->GetChildCount() - aLineLayout.GetCurrentSpanCount();
4068 0 : NS_ABORT_IF_FALSE(pushCount >= 0, "bad push count");
4069 :
4070 : #ifdef DEBUG
4071 0 : if (gNoisyReflow) {
4072 0 : nsFrame::IndentBy(stdout, gNoiseIndent);
4073 : printf("split line: from line=%p pushCount=%d aFrame=",
4074 0 : static_cast<void*>(aLine.get()), pushCount);
4075 0 : if (aFrame) {
4076 0 : nsFrame::ListTag(stdout, aFrame);
4077 : }
4078 : else {
4079 0 : printf("(null)");
4080 : }
4081 0 : printf("\n");
4082 0 : if (gReallyNoisyReflow) {
4083 0 : aLine->List(stdout, gNoiseIndent+1);
4084 : }
4085 : }
4086 : #endif
4087 :
4088 0 : if (0 != pushCount) {
4089 0 : NS_ABORT_IF_FALSE(aLine->GetChildCount() > pushCount, "bad push");
4090 0 : NS_ABORT_IF_FALSE(nsnull != aFrame, "whoops");
4091 : #ifdef DEBUG
4092 : {
4093 0 : nsIFrame *f = aFrame;
4094 0 : PRInt32 count = pushCount;
4095 0 : while (f && count > 0) {
4096 0 : f = f->GetNextSibling();
4097 0 : --count;
4098 : }
4099 0 : NS_ASSERTION(count == 0, "Not enough frames to push");
4100 : }
4101 : #endif
4102 :
4103 : // Put frames being split out into their own line
4104 0 : nsLineBox* newLine = NewLineBox(aLine, aFrame, pushCount);
4105 0 : if (!newLine) {
4106 0 : return NS_ERROR_OUT_OF_MEMORY;
4107 : }
4108 0 : mLines.after_insert(aLine, newLine);
4109 : #ifdef DEBUG
4110 0 : if (gReallyNoisyReflow) {
4111 0 : newLine->List(stdout, gNoiseIndent+1);
4112 : }
4113 : #endif
4114 :
4115 : // Let line layout know that some frames are no longer part of its
4116 : // state.
4117 0 : aLineLayout.SplitLineTo(aLine->GetChildCount());
4118 :
4119 : // If floats have been placed whose placeholders have been pushed to the new
4120 : // line, we need to reflow the old line again. We don't want to look at the
4121 : // frames in the new line, because as a large paragraph is laid out the
4122 : // we'd get O(N^2) performance. So instead we just check that the last
4123 : // float and the last below-current-line float are still in aLine.
4124 0 : if (!CheckPlaceholderInLine(this, aLine, GetLastFloat(aLine)) ||
4125 0 : !CheckPlaceholderInLine(this, aLine, aState.mBelowCurrentLineFloats.Tail())) {
4126 0 : *aLineReflowStatus = LINE_REFLOW_REDO_NO_PULL;
4127 : }
4128 :
4129 : #ifdef DEBUG
4130 0 : VerifyLines(true);
4131 : #endif
4132 : }
4133 0 : return NS_OK;
4134 : }
4135 :
4136 : bool
4137 0 : nsBlockFrame::IsLastLine(nsBlockReflowState& aState,
4138 : line_iterator aLine)
4139 : {
4140 0 : while (++aLine != end_lines()) {
4141 : // There is another line
4142 0 : if (0 != aLine->GetChildCount()) {
4143 : // If the next line is a block line then this line is the last in a
4144 : // group of inline lines.
4145 0 : return aLine->IsBlock();
4146 : }
4147 : // The next line is empty, try the next one
4148 : }
4149 :
4150 : // XXX Not sure about this part
4151 : // Try our next-in-flows lines to answer the question
4152 0 : nsBlockFrame* nextInFlow = (nsBlockFrame*) GetNextInFlow();
4153 0 : while (nsnull != nextInFlow) {
4154 0 : for (line_iterator line = nextInFlow->begin_lines(),
4155 0 : line_end = nextInFlow->end_lines();
4156 : line != line_end;
4157 : ++line)
4158 : {
4159 0 : if (0 != line->GetChildCount())
4160 0 : return line->IsBlock();
4161 : }
4162 0 : nextInFlow = (nsBlockFrame*) nextInFlow->GetNextInFlow();
4163 : }
4164 :
4165 : // This is the last line - so don't allow justification
4166 0 : return true;
4167 : }
4168 :
4169 : bool
4170 0 : nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
4171 : nsLineLayout& aLineLayout,
4172 : line_iterator aLine,
4173 : nsFloatManager::SavedState *aFloatStateBeforeLine,
4174 : nsRect& aFloatAvailableSpace,
4175 : nscoord& aAvailableSpaceHeight,
4176 : bool* aKeepReflowGoing)
4177 : {
4178 : // Trim extra white-space from the line before placing the frames
4179 0 : aLineLayout.TrimTrailingWhiteSpace();
4180 :
4181 : // Vertically align the frames on this line.
4182 : //
4183 : // According to the CSS2 spec, section 12.6.1, the "marker" box
4184 : // participates in the height calculation of the list-item box's
4185 : // first line box.
4186 : //
4187 : // There are exactly two places a bullet can be placed: near the
4188 : // first or second line. It's only placed on the second line in a
4189 : // rare case: when the first line is empty.
4190 0 : bool addedBullet = false;
4191 0 : if (HasOutsideBullet() &&
4192 0 : ((aLine == mLines.front() &&
4193 0 : (!aLineLayout.IsZeroHeight() || (aLine == mLines.back()))) ||
4194 0 : (mLines.front() != mLines.back() &&
4195 0 : 0 == mLines.front()->mBounds.height &&
4196 0 : aLine == mLines.begin().next()))) {
4197 0 : nsHTMLReflowMetrics metrics;
4198 0 : nsIFrame* bullet = GetOutsideBullet();
4199 0 : ReflowBullet(bullet, aState, metrics, aState.mY);
4200 0 : NS_ASSERTION(!BulletIsEmpty() || metrics.height == 0,
4201 : "empty bullet took up space");
4202 0 : aLineLayout.AddBulletFrame(bullet, metrics);
4203 0 : addedBullet = true;
4204 : }
4205 0 : aLineLayout.VerticalAlignLine();
4206 :
4207 : // We want to compare to the available space that we would have had in
4208 : // the line's height *before* we placed any floats in the line itself.
4209 : // Floats that are in the line are handled during line reflow (and may
4210 : // result in floats being pushed to below the line or (I HOPE???) in a
4211 : // reflow with a forced break position).
4212 0 : nsRect oldFloatAvailableSpace(aFloatAvailableSpace);
4213 : // As we redo for floats, we can't reduce the amount of height we're
4214 : // checking.
4215 0 : aAvailableSpaceHeight = NS_MAX(aAvailableSpaceHeight, aLine->mBounds.height);
4216 : aFloatAvailableSpace =
4217 0 : aState.GetFloatAvailableSpaceForHeight(aLine->mBounds.y,
4218 : aAvailableSpaceHeight,
4219 0 : aFloatStateBeforeLine).mRect;
4220 0 : NS_ASSERTION(aFloatAvailableSpace.y == oldFloatAvailableSpace.y, "yikes");
4221 : // Restore the height to the position of the next band.
4222 0 : aFloatAvailableSpace.height = oldFloatAvailableSpace.height;
4223 : // If the available space between the floats is smaller now that we
4224 : // know the height, return false (and cause another pass with
4225 : // LINE_REFLOW_REDO_MORE_FLOATS).
4226 0 : if (AvailableSpaceShrunk(oldFloatAvailableSpace, aFloatAvailableSpace)) {
4227 0 : return false;
4228 : }
4229 :
4230 : #ifdef DEBUG
4231 : {
4232 : static nscoord lastHeight = 0;
4233 0 : if (CRAZY_HEIGHT(aLine->mBounds.y)) {
4234 0 : lastHeight = aLine->mBounds.y;
4235 0 : if (abs(aLine->mBounds.y - lastHeight) > CRAZY_H/10) {
4236 0 : nsFrame::ListTag(stdout);
4237 : printf(": line=%p y=%d line.bounds.height=%d\n",
4238 0 : static_cast<void*>(aLine.get()),
4239 0 : aLine->mBounds.y, aLine->mBounds.height);
4240 : }
4241 : }
4242 : else {
4243 0 : lastHeight = 0;
4244 : }
4245 : }
4246 : #endif
4247 :
4248 : // Only block frames horizontally align their children because
4249 : // inline frames "shrink-wrap" around their children (therefore
4250 : // there is no extra horizontal space).
4251 0 : const nsStyleText* styleText = GetStyleText();
4252 :
4253 : /**
4254 : * text-align-last defaults to the same value as text-align when
4255 : * text-align-last is set to auto (unless when text-align is set to justify),
4256 : * so in that case we don't need to set isLastLine.
4257 : *
4258 : * In other words, isLastLine really means isLastLineAndWeCare.
4259 : */
4260 : bool isLastLine = ((NS_STYLE_TEXT_ALIGN_AUTO != styleText->mTextAlignLast ||
4261 : NS_STYLE_TEXT_ALIGN_JUSTIFY == styleText->mTextAlign) &&
4262 0 : (aLineLayout.GetLineEndsInBR() ||
4263 0 : IsLastLine(aState, aLine)));
4264 0 : aLineLayout.HorizontalAlignFrames(aLine->mBounds, isLastLine);
4265 : // XXX: not only bidi: right alignment can be broken after
4266 : // RelativePositionFrames!!!
4267 : // XXXldb Is something here considering relatively positioned frames at
4268 : // other than their original positions?
4269 : #ifdef IBMBIDI
4270 : // XXXldb Why don't we do this earlier?
4271 0 : if (aState.mPresContext->BidiEnabled()) {
4272 0 : if (!aState.mPresContext->IsVisualMode() ||
4273 0 : GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
4274 0 : nsBidiPresUtils::ReorderFrames(aLine->mFirstChild, aLine->GetChildCount());
4275 : } // not visual mode
4276 : } // bidi enabled
4277 : #endif // IBMBIDI
4278 :
4279 : // From here on, pfd->mBounds rectangles are incorrect because bidi
4280 : // might have moved frames around!
4281 0 : nsOverflowAreas overflowAreas;
4282 0 : aLineLayout.RelativePositionFrames(overflowAreas);
4283 0 : aLine->SetOverflowAreas(overflowAreas);
4284 0 : if (addedBullet) {
4285 0 : aLineLayout.RemoveBulletFrame(GetOutsideBullet());
4286 : }
4287 :
4288 : // Inline lines do not have margins themselves; however they are
4289 : // impacted by prior block margins. If this line ends up having some
4290 : // height then we zero out the previous bottom margin value that was
4291 : // already applied to the line's starting Y coordinate. Otherwise we
4292 : // leave it be so that the previous blocks bottom margin can be
4293 : // collapsed with a block that follows.
4294 : nscoord newY;
4295 :
4296 0 : if (!aLine->CachedIsEmpty()) {
4297 : // This line has some height. Therefore the application of the
4298 : // previous-bottom-margin should stick.
4299 0 : aState.mPrevBottomMargin.Zero();
4300 0 : newY = aLine->mBounds.YMost();
4301 : }
4302 : else {
4303 : // Don't let the previous-bottom-margin value affect the newY
4304 : // coordinate (it was applied in ReflowInlineFrames speculatively)
4305 : // since the line is empty.
4306 : // We already called |ShouldApplyTopMargin|, and if we applied it
4307 : // then BRS_APPLYTOPMARGIN is set.
4308 0 : nscoord dy = aState.GetFlag(BRS_APPLYTOPMARGIN)
4309 0 : ? -aState.mPrevBottomMargin.get() : 0;
4310 0 : newY = aState.mY + dy;
4311 : }
4312 :
4313 : // See if the line fit. If it doesn't we need to push it. Our first
4314 : // line will always fit.
4315 0 : if (mLines.front() != aLine &&
4316 : newY > aState.mBottomEdge &&
4317 : aState.mBottomEdge != NS_UNCONSTRAINEDSIZE) {
4318 : // Push this line and all of its children and anything else that
4319 : // follows to our next-in-flow
4320 0 : NS_ASSERTION((aState.mCurrentLine == aLine), "oops");
4321 0 : PushLines(aState, aLine.prev());
4322 :
4323 : // Stop reflow and whack the reflow status if reflow hasn't
4324 : // already been stopped.
4325 0 : if (*aKeepReflowGoing) {
4326 0 : NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
4327 0 : *aKeepReflowGoing = false;
4328 : }
4329 0 : return true;
4330 : }
4331 :
4332 0 : aState.mY = newY;
4333 :
4334 : // Add the already placed current-line floats to the line
4335 0 : aLine->AppendFloats(aState.mCurrentLineFloats);
4336 :
4337 : // Any below current line floats to place?
4338 0 : if (aState.mBelowCurrentLineFloats.NotEmpty()) {
4339 : // Reflow the below-current-line floats, which places on the line's
4340 : // float list.
4341 0 : aState.PlaceBelowCurrentLineFloats(aState.mBelowCurrentLineFloats, aLine);
4342 0 : aLine->AppendFloats(aState.mBelowCurrentLineFloats);
4343 : }
4344 :
4345 : // When a line has floats, factor them into the combined-area
4346 : // computations.
4347 0 : if (aLine->HasFloats()) {
4348 : // Combine the float combined area (stored in aState) and the
4349 : // value computed by the line layout code.
4350 0 : nsOverflowAreas lineOverflowAreas;
4351 0 : NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
4352 0 : nsRect &o = lineOverflowAreas.Overflow(otype);
4353 0 : o = aLine->GetOverflowArea(otype);
4354 : #ifdef NOISY_COMBINED_AREA
4355 : ListTag(stdout);
4356 : printf(": overflow %d lineCA=%d,%d,%d,%d floatCA=%d,%d,%d,%d\n",
4357 : otype,
4358 : o.x, o.y, o.width, o.height,
4359 : aState.mFloatOverflowAreas.Overflow(otype).x,
4360 : aState.mFloatOverflowAreas.Overflow(otype).y,
4361 : aState.mFloatOverflowAreas.Overflow(otype).width,
4362 : aState.mFloatOverflowAreas.Overflow(otype).height);
4363 : #endif
4364 0 : o.UnionRect(aState.mFloatOverflowAreas.Overflow(otype), o);
4365 :
4366 : #ifdef NOISY_COMBINED_AREA
4367 : printf(" ==> final lineCA=%d,%d,%d,%d\n",
4368 : o.x, o.y, o.width, o.height);
4369 : #endif
4370 : }
4371 0 : aLine->SetOverflowAreas(lineOverflowAreas);
4372 : }
4373 :
4374 : // Apply break-after clearing if necessary
4375 : // This must stay in sync with |ReflowDirtyLines|.
4376 0 : if (aLine->HasFloatBreakAfter()) {
4377 0 : aState.mY = aState.ClearFloats(aState.mY, aLine->GetBreakTypeAfter());
4378 : }
4379 0 : return true;
4380 : }
4381 :
4382 : void
4383 0 : nsBlockFrame::PushLines(nsBlockReflowState& aState,
4384 : nsLineList::iterator aLineBefore)
4385 : {
4386 : // NOTE: aLineBefore is always a normal line, not an overflow line.
4387 : // The following expression will assert otherwise.
4388 0 : DebugOnly<bool> check = aLineBefore == mLines.begin();
4389 :
4390 0 : nsLineList::iterator overBegin(aLineBefore.next());
4391 :
4392 : // PushTruncatedPlaceholderLine sometimes pushes the first line. Ugh.
4393 0 : bool firstLine = overBegin == begin_lines();
4394 :
4395 0 : if (overBegin != end_lines()) {
4396 : // Remove floats in the lines from mFloats
4397 0 : nsFrameList floats;
4398 0 : CollectFloats(overBegin->mFirstChild, floats, false, true);
4399 :
4400 0 : if (floats.NotEmpty()) {
4401 : // Push the floats onto the front of the overflow out-of-flows list
4402 0 : nsAutoOOFFrameList oofs(this);
4403 0 : oofs.mList.InsertFrames(nsnull, nsnull, floats);
4404 : }
4405 :
4406 : // overflow lines can already exist in some cases, in particular,
4407 : // when shrinkwrapping and we discover that the shrinkwap causes
4408 : // the height of some child block to grow which creates additional
4409 : // overflowing content. In such cases we must prepend the new
4410 : // overflow to the existing overflow.
4411 0 : FrameLines* overflowLines = RemoveOverflowLines();
4412 0 : if (!overflowLines) {
4413 : // XXXldb use presshell arena!
4414 0 : overflowLines = new FrameLines();
4415 : }
4416 0 : if (overflowLines) {
4417 : nsIFrame* lineBeforeLastFrame;
4418 0 : if (firstLine) {
4419 0 : lineBeforeLastFrame = nsnull; // removes all frames
4420 : } else {
4421 0 : nsIFrame* f = overBegin->mFirstChild;
4422 0 : lineBeforeLastFrame = f ? f->GetPrevSibling() : mFrames.LastChild();
4423 0 : NS_ASSERTION(!f || lineBeforeLastFrame == aLineBefore->LastChild(),
4424 : "unexpected line frames");
4425 : }
4426 0 : nsFrameList pushedFrames = mFrames.RemoveFramesAfter(lineBeforeLastFrame);
4427 0 : overflowLines->mFrames.InsertFrames(nsnull, nsnull, pushedFrames);
4428 :
4429 0 : overflowLines->mLines.splice(overflowLines->mLines.begin(), mLines,
4430 0 : overBegin, end_lines());
4431 0 : NS_ASSERTION(!overflowLines->mLines.empty(), "should not be empty");
4432 : // this takes ownership but it won't delete it immediately so we
4433 : // can keep using it.
4434 0 : SetOverflowLines(overflowLines);
4435 :
4436 : // Mark all the overflow lines dirty so that they get reflowed when
4437 : // they are pulled up by our next-in-flow.
4438 :
4439 : // XXXldb Can this get called O(N) times making the whole thing O(N^2)?
4440 0 : for (line_iterator line = overflowLines->mLines.begin(),
4441 0 : line_end = overflowLines->mLines.end();
4442 : line != line_end;
4443 : ++line)
4444 : {
4445 0 : line->MarkDirty();
4446 0 : line->MarkPreviousMarginDirty();
4447 0 : line->mBounds.SetRect(0, 0, 0, 0);
4448 0 : if (line->HasFloats()) {
4449 0 : line->FreeFloats(aState.mFloatCacheFreeList);
4450 : }
4451 : }
4452 : }
4453 : }
4454 :
4455 : #ifdef DEBUG
4456 0 : VerifyOverflowSituation();
4457 : #endif
4458 0 : }
4459 :
4460 : // The overflowLines property is stored as a pointer to a line list,
4461 : // which must be deleted. However, the following functions all maintain
4462 : // the invariant that the property is never set if the list is empty.
4463 :
4464 : bool
4465 0 : nsBlockFrame::DrainOverflowLines()
4466 : {
4467 : #ifdef DEBUG
4468 0 : VerifyOverflowSituation();
4469 : #endif
4470 0 : FrameLines* overflowLines = nsnull;
4471 0 : FrameLines* ourOverflowLines = nsnull;
4472 :
4473 : // First grab the prev-in-flows overflow lines
4474 0 : nsBlockFrame* prevBlock = (nsBlockFrame*) GetPrevInFlow();
4475 0 : if (prevBlock) {
4476 0 : prevBlock->ClearLineCursor();
4477 0 : overflowLines = prevBlock->RemoveOverflowLines();
4478 0 : if (overflowLines) {
4479 0 : NS_ASSERTION(!overflowLines->mLines.empty(),
4480 : "overflow lines should never be set and empty");
4481 : // Make all the frames on the overflow line list mine.
4482 0 : ReparentFrames(overflowLines->mFrames, prevBlock, this);
4483 :
4484 : // Make the overflow out-of-flow frames mine too.
4485 0 : nsAutoOOFFrameList oofs(prevBlock);
4486 0 : if (oofs.mList.NotEmpty()) {
4487 0 : ReparentFrames(oofs.mList, prevBlock, this);
4488 0 : mFloats.InsertFrames(nsnull, nsnull, oofs.mList);
4489 : }
4490 : }
4491 :
4492 : // The lines on the overflow list have already been marked dirty and their
4493 : // previous margins marked dirty also.
4494 : }
4495 :
4496 : // Don't need to reparent frames in our own overflow lines/oofs, because they're
4497 : // already ours. But we should put overflow floats back in mFloats.
4498 0 : ourOverflowLines = RemoveOverflowLines();
4499 0 : if (ourOverflowLines) {
4500 0 : nsAutoOOFFrameList oofs(this);
4501 0 : if (oofs.mList.NotEmpty()) {
4502 : // The overflow floats go after our regular floats
4503 0 : mFloats.AppendFrames(nsnull, oofs.mList);
4504 : }
4505 : }
4506 :
4507 0 : if (!overflowLines && !ourOverflowLines) {
4508 : // nothing to do; always the case for non-constrained-height reflows
4509 0 : return false;
4510 : }
4511 :
4512 : // Now join the line lists into mLines
4513 0 : if (overflowLines) {
4514 0 : if (!overflowLines->mLines.empty()) {
4515 : // Join the line lists
4516 0 : if (!mLines.empty()) {
4517 : // Remember to recompute the margins on the first line. This will
4518 : // also recompute the correct deltaY if necessary.
4519 0 : mLines.front()->MarkPreviousMarginDirty();
4520 : }
4521 :
4522 : // Join the sibling lists together
4523 0 : mFrames.InsertFrames(nsnull, nsnull, overflowLines->mFrames);
4524 :
4525 : // Place overflow lines at the front of our line list
4526 0 : mLines.splice(mLines.begin(), overflowLines->mLines);
4527 0 : NS_ASSERTION(overflowLines->mLines.empty(), "splice should empty list");
4528 : }
4529 0 : delete overflowLines;
4530 : }
4531 0 : if (ourOverflowLines) {
4532 0 : if (!ourOverflowLines->mLines.empty()) {
4533 0 : mFrames.AppendFrames(nsnull, ourOverflowLines->mFrames);
4534 0 : mLines.splice(mLines.end(), ourOverflowLines->mLines);
4535 : }
4536 0 : delete ourOverflowLines;
4537 : }
4538 :
4539 0 : return true;
4540 : }
4541 :
4542 : // This function assumes our prev-in-flow has completed reflow and its
4543 : // mFloats may contain frames at the end of its float list, marked with
4544 : // NS_FRAME_IS_PUSHED_FLOAT, that should be pulled to this block.
4545 : void
4546 0 : nsBlockFrame::DrainPushedFloats(nsBlockReflowState& aState)
4547 : {
4548 : #ifdef DEBUG
4549 : // Between when we drain pushed floats and when we complete reflow,
4550 : // we're allowed to have multiple continuations of the same float on
4551 : // our floats list, since a first-in-flow might get pushed to a later
4552 : // continuation of its containing block. But it's not permitted
4553 : // outside that time.
4554 0 : nsLayoutUtils::AssertNoDuplicateContinuations(this, mFloats);
4555 : #endif
4556 :
4557 : // Take any continuations we need to take from our prev-in-flow.
4558 0 : nsBlockFrame* prevBlock = static_cast<nsBlockFrame*>(GetPrevInFlow());
4559 0 : if (!prevBlock)
4560 0 : return;
4561 0 : nsFrameList *list = prevBlock->RemovePushedFloats();
4562 0 : if (list) {
4563 0 : if (list->NotEmpty()) {
4564 0 : mFloats.InsertFrames(this, nsnull, *list);
4565 : }
4566 0 : delete list;
4567 : }
4568 : }
4569 :
4570 : nsBlockFrame::FrameLines*
4571 0 : nsBlockFrame::GetOverflowLines() const
4572 : {
4573 0 : if (!HasOverflowLines()) {
4574 0 : return nsnull;
4575 : }
4576 : FrameLines* prop =
4577 0 : static_cast<FrameLines*>(Properties().Get(OverflowLinesProperty()));
4578 0 : NS_ASSERTION(prop && !prop->mLines.empty() &&
4579 : prop->mLines.front()->mFirstChild == prop->mFrames.FirstChild(),
4580 : "value should always be stored and non-empty when state set");
4581 0 : return prop;
4582 : }
4583 :
4584 : nsBlockFrame::FrameLines*
4585 0 : nsBlockFrame::RemoveOverflowLines()
4586 : {
4587 0 : if (!HasOverflowLines()) {
4588 0 : return nsnull;
4589 : }
4590 : FrameLines* prop =
4591 0 : static_cast<FrameLines*>(Properties().Remove(OverflowLinesProperty()));
4592 0 : NS_ASSERTION(prop && !prop->mLines.empty() &&
4593 : prop->mLines.front()->mFirstChild == prop->mFrames.FirstChild(),
4594 : "value should always be stored and non-empty when state set");
4595 0 : RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES);
4596 0 : return prop;
4597 : }
4598 :
4599 : void
4600 0 : nsBlockFrame::DestroyOverflowLines()
4601 : {
4602 0 : NS_ASSERTION(HasOverflowLines(), "huh?");
4603 : FrameLines* prop =
4604 0 : static_cast<FrameLines*>(Properties().Remove(OverflowLinesProperty()));
4605 0 : NS_ASSERTION(prop && prop->mLines.empty(),
4606 : "value should always be stored but empty when destroying");
4607 0 : RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES);
4608 0 : delete prop;
4609 0 : }
4610 :
4611 : // This takes ownership of aOverflowLines.
4612 : // XXX We should allocate overflowLines from presShell arena!
4613 : void
4614 0 : nsBlockFrame::SetOverflowLines(FrameLines* aOverflowLines)
4615 : {
4616 0 : NS_ASSERTION(aOverflowLines, "null lines");
4617 0 : NS_ASSERTION(!aOverflowLines->mLines.empty(), "empty lines");
4618 0 : NS_ASSERTION(aOverflowLines->mLines.front()->mFirstChild ==
4619 : aOverflowLines->mFrames.FirstChild(),
4620 : "invalid overflow lines / frames");
4621 0 : NS_ASSERTION(!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_LINES),
4622 : "Overwriting existing overflow lines");
4623 :
4624 0 : FrameProperties props = Properties();
4625 : // Verify that we won't overwrite an existing overflow list
4626 0 : NS_ASSERTION(!props.Get(OverflowLinesProperty()), "existing overflow list");
4627 0 : props.Set(OverflowLinesProperty(), aOverflowLines);
4628 0 : AddStateBits(NS_BLOCK_HAS_OVERFLOW_LINES);
4629 0 : }
4630 :
4631 : nsFrameList*
4632 0 : nsBlockFrame::GetOverflowOutOfFlows() const
4633 : {
4634 0 : if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS)) {
4635 0 : return nsnull;
4636 : }
4637 : nsFrameList* result =
4638 0 : GetPropTableFrames(PresContext(), OverflowOutOfFlowsProperty());
4639 0 : NS_ASSERTION(result, "value should always be non-empty when state set");
4640 0 : return result;
4641 : }
4642 :
4643 : // This takes ownership of the frames
4644 : void
4645 0 : nsBlockFrame::SetOverflowOutOfFlows(const nsFrameList& aList,
4646 : nsFrameList* aPropValue)
4647 : {
4648 0 : NS_PRECONDITION(!!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS) ==
4649 : !!aPropValue, "state does not match value");
4650 :
4651 0 : if (aList.IsEmpty()) {
4652 0 : if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS)) {
4653 0 : return;
4654 : }
4655 : nsFrameList* list =
4656 : RemovePropTableFrames(PresContext(),
4657 0 : OverflowOutOfFlowsProperty());
4658 0 : NS_ASSERTION(aPropValue == list, "prop value mismatch");
4659 0 : delete list;
4660 0 : RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS);
4661 : }
4662 0 : else if (GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS) {
4663 0 : NS_ASSERTION(aPropValue == GetPropTableFrames(PresContext(),
4664 : OverflowOutOfFlowsProperty()),
4665 : "prop value mismatch");
4666 0 : *aPropValue = aList;
4667 : }
4668 : else {
4669 0 : SetPropTableFrames(PresContext(), new nsFrameList(aList),
4670 0 : OverflowOutOfFlowsProperty());
4671 0 : AddStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS);
4672 : }
4673 : }
4674 :
4675 : nsBulletFrame*
4676 0 : nsBlockFrame::GetInsideBullet() const
4677 : {
4678 0 : if (!HasInsideBullet()) {
4679 0 : return nsnull;
4680 : }
4681 0 : NS_ASSERTION(!HasOutsideBullet(), "invalid bullet state");
4682 : nsBulletFrame* frame =
4683 0 : static_cast<nsBulletFrame*>(Properties().Get(InsideBulletProperty()));
4684 0 : NS_ASSERTION(frame && frame->GetType() == nsGkAtoms::bulletFrame,
4685 : "bogus inside bullet frame");
4686 0 : return frame;
4687 : }
4688 :
4689 : nsBulletFrame*
4690 0 : nsBlockFrame::GetOutsideBullet() const
4691 : {
4692 0 : nsFrameList* list = GetOutsideBulletList();
4693 0 : return list ? static_cast<nsBulletFrame*>(list->FirstChild())
4694 0 : : nsnull;
4695 : }
4696 :
4697 : nsFrameList*
4698 0 : nsBlockFrame::GetOutsideBulletList() const
4699 : {
4700 0 : if (!HasOutsideBullet()) {
4701 0 : return nsnull;
4702 : }
4703 0 : NS_ASSERTION(!HasInsideBullet(), "invalid bullet state");
4704 : nsFrameList* list =
4705 0 : static_cast<nsFrameList*>(Properties().Get(OutsideBulletProperty()));
4706 0 : NS_ASSERTION(list && list->GetLength() == 1 &&
4707 : list->FirstChild()->GetType() == nsGkAtoms::bulletFrame,
4708 : "bogus outside bullet list");
4709 0 : return list;
4710 : }
4711 :
4712 : nsFrameList*
4713 0 : nsBlockFrame::GetPushedFloats() const
4714 : {
4715 0 : if (!HasPushedFloats()) {
4716 0 : return nsnull;
4717 : }
4718 : nsFrameList* result =
4719 0 : static_cast<nsFrameList*>(Properties().Get(PushedFloatProperty()));
4720 0 : NS_ASSERTION(result, "value should always be non-empty when state set");
4721 0 : return result;
4722 : }
4723 :
4724 : nsFrameList*
4725 0 : nsBlockFrame::EnsurePushedFloats()
4726 : {
4727 0 : nsFrameList *result = GetPushedFloats();
4728 0 : if (result)
4729 0 : return result;
4730 :
4731 0 : result = new nsFrameList;
4732 0 : Properties().Set(PushedFloatProperty(), result);
4733 0 : AddStateBits(NS_BLOCK_HAS_PUSHED_FLOATS);
4734 :
4735 0 : return result;
4736 : }
4737 :
4738 : nsFrameList*
4739 0 : nsBlockFrame::RemovePushedFloats()
4740 : {
4741 0 : if (!HasPushedFloats()) {
4742 0 : return nsnull;
4743 : }
4744 : nsFrameList *result =
4745 0 : static_cast<nsFrameList*>(Properties().Remove(PushedFloatProperty()));
4746 0 : RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS);
4747 0 : NS_ASSERTION(result, "value should always be non-empty when state set");
4748 0 : return result;
4749 : }
4750 :
4751 : //////////////////////////////////////////////////////////////////////
4752 : // Frame list manipulation routines
4753 :
4754 : NS_IMETHODIMP
4755 0 : nsBlockFrame::AppendFrames(ChildListID aListID,
4756 : nsFrameList& aFrameList)
4757 : {
4758 0 : if (aFrameList.IsEmpty()) {
4759 0 : return NS_OK;
4760 : }
4761 0 : if (aListID != kPrincipalList) {
4762 0 : if (kAbsoluteList == aListID) {
4763 0 : return nsContainerFrame::AppendFrames(aListID, aFrameList);
4764 : }
4765 0 : else if (kFloatList == aListID) {
4766 0 : mFloats.AppendFrames(nsnull, aFrameList);
4767 0 : return NS_OK;
4768 : }
4769 : else {
4770 0 : NS_ERROR("unexpected child list");
4771 0 : return NS_ERROR_INVALID_ARG;
4772 : }
4773 : }
4774 :
4775 : // Find the proper last-child for where the append should go
4776 0 : nsIFrame* lastKid = mFrames.LastChild();
4777 0 : NS_ASSERTION((mLines.empty() ? nsnull : mLines.back()->LastChild()) ==
4778 : lastKid, "out-of-sync mLines / mFrames");
4779 :
4780 : // Add frames after the last child
4781 : #ifdef NOISY_REFLOW_REASON
4782 : ListTag(stdout);
4783 : printf(": append ");
4784 : nsFrame::ListTag(stdout, aFrameList);
4785 : if (lastKid) {
4786 : printf(" after ");
4787 : nsFrame::ListTag(stdout, lastKid);
4788 : }
4789 : printf("\n");
4790 : #endif
4791 0 : nsresult rv = AddFrames(aFrameList, lastKid);
4792 0 : if (NS_FAILED(rv)) {
4793 0 : return rv;
4794 : }
4795 0 : aFrameList.Clear();
4796 :
4797 0 : PresContext()->PresShell()->
4798 : FrameNeedsReflow(this, nsIPresShell::eTreeChange,
4799 0 : NS_FRAME_HAS_DIRTY_CHILDREN); // XXX sufficient?
4800 0 : return NS_OK;
4801 : }
4802 :
4803 : NS_IMETHODIMP
4804 0 : nsBlockFrame::InsertFrames(ChildListID aListID,
4805 : nsIFrame* aPrevFrame,
4806 : nsFrameList& aFrameList)
4807 : {
4808 0 : NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
4809 : "inserting after sibling frame with different parent");
4810 :
4811 0 : if (aListID != kPrincipalList) {
4812 0 : if (kAbsoluteList == aListID) {
4813 0 : return nsContainerFrame::InsertFrames(aListID, aPrevFrame, aFrameList);
4814 : }
4815 0 : else if (kFloatList == aListID) {
4816 0 : mFloats.InsertFrames(this, aPrevFrame, aFrameList);
4817 0 : return NS_OK;
4818 : }
4819 : #ifdef IBMBIDI
4820 0 : else if (kNoReflowPrincipalList == aListID) {}
4821 : #endif // IBMBIDI
4822 : else {
4823 0 : NS_ERROR("unexpected child list");
4824 0 : return NS_ERROR_INVALID_ARG;
4825 : }
4826 : }
4827 :
4828 : #ifdef NOISY_REFLOW_REASON
4829 : ListTag(stdout);
4830 : printf(": insert ");
4831 : nsFrame::ListTag(stdout, aFrameList);
4832 : if (aPrevFrame) {
4833 : printf(" after ");
4834 : nsFrame::ListTag(stdout, aPrevFrame);
4835 : }
4836 : printf("\n");
4837 : #endif
4838 0 : nsresult rv = AddFrames(aFrameList, aPrevFrame);
4839 0 : if (NS_FAILED(rv)) {
4840 0 : return rv;
4841 : }
4842 : #ifdef IBMBIDI
4843 0 : if (aListID != kNoReflowPrincipalList)
4844 : #endif // IBMBIDI
4845 0 : PresContext()->PresShell()->
4846 : FrameNeedsReflow(this, nsIPresShell::eTreeChange,
4847 0 : NS_FRAME_HAS_DIRTY_CHILDREN); // XXX sufficient?
4848 0 : return NS_OK;
4849 : }
4850 :
4851 : static bool
4852 0 : ShouldPutNextSiblingOnNewLine(nsIFrame* aLastFrame)
4853 : {
4854 0 : nsIAtom* type = aLastFrame->GetType();
4855 0 : if (type == nsGkAtoms::brFrame)
4856 0 : return true;
4857 0 : if (type == nsGkAtoms::textFrame)
4858 0 : return aLastFrame->HasTerminalNewline() &&
4859 0 : aLastFrame->GetStyleText()->NewlineIsSignificant();
4860 0 : return false;
4861 : }
4862 :
4863 : nsresult
4864 0 : nsBlockFrame::AddFrames(nsFrameList& aFrameList, nsIFrame* aPrevSibling)
4865 : {
4866 : // Clear our line cursor, since our lines may change.
4867 0 : ClearLineCursor();
4868 :
4869 0 : if (aFrameList.IsEmpty()) {
4870 0 : return NS_OK;
4871 : }
4872 :
4873 : // If we're inserting at the beginning of our list and we have an
4874 : // inside bullet, insert after that bullet.
4875 0 : if (!aPrevSibling && HasInsideBullet()) {
4876 0 : aPrevSibling = GetInsideBullet();
4877 : }
4878 :
4879 : // Attempt to find the line that contains the previous sibling
4880 : FrameLines* overflowLines;
4881 0 : nsLineList* lineList = &mLines;
4882 0 : nsLineList::iterator prevSibLine = lineList->end();
4883 0 : PRInt32 prevSiblingIndex = -1;
4884 0 : if (aPrevSibling) {
4885 : // XXX_perf This is technically O(N^2) in some cases, but by using
4886 : // RFind instead of Find, we make it O(N) in the most common case,
4887 : // which is appending content.
4888 :
4889 : // Find the line that contains the previous sibling
4890 0 : if (!nsLineBox::RFindLineContaining(aPrevSibling, lineList->begin(),
4891 : prevSibLine, mFrames.LastChild(),
4892 0 : &prevSiblingIndex)) {
4893 : // Not in mLines - try overflow lines.
4894 0 : overflowLines = GetOverflowLines();
4895 0 : lineList = overflowLines ? &overflowLines->mLines : nsnull;
4896 0 : if (overflowLines) {
4897 0 : prevSibLine = overflowLines->mLines.end();
4898 0 : prevSiblingIndex = -1;
4899 0 : if (!nsLineBox::RFindLineContaining(aPrevSibling, lineList->begin(),
4900 : prevSibLine,
4901 : overflowLines->mFrames.LastChild(),
4902 0 : &prevSiblingIndex)) {
4903 0 : lineList = nsnull;
4904 : }
4905 : }
4906 0 : if (!lineList) {
4907 : // Note: defensive code! RFindLineContaining must not return
4908 : // false in this case, so if it does...
4909 0 : NS_NOTREACHED("prev sibling not in line list");
4910 0 : lineList = &mLines;
4911 0 : aPrevSibling = nsnull;
4912 0 : prevSibLine = lineList->end();
4913 : }
4914 : }
4915 : }
4916 :
4917 : // Find the frame following aPrevSibling so that we can join up the
4918 : // two lists of frames.
4919 0 : if (aPrevSibling) {
4920 : // Split line containing aPrevSibling in two if the insertion
4921 : // point is somewhere in the middle of the line.
4922 0 : PRInt32 rem = prevSibLine->GetChildCount() - prevSiblingIndex - 1;
4923 0 : if (rem) {
4924 : // Split the line in two where the frame(s) are being inserted.
4925 0 : nsLineBox* line = NewLineBox(prevSibLine, aPrevSibling->GetNextSibling(), rem);
4926 0 : if (!line) {
4927 0 : return NS_ERROR_OUT_OF_MEMORY;
4928 : }
4929 0 : lineList->after_insert(prevSibLine, line);
4930 : // Mark prevSibLine dirty and as needing textrun invalidation, since
4931 : // we may be breaking up text in the line. Its previous line may also
4932 : // need to be invalidated because it may be able to pull some text up.
4933 0 : MarkLineDirty(prevSibLine);
4934 : // The new line will also need its textruns recomputed because of the
4935 : // frame changes.
4936 0 : line->MarkDirty();
4937 0 : line->SetInvalidateTextRuns(true);
4938 : }
4939 : }
4940 0 : else if (! lineList->empty()) {
4941 0 : lineList->front()->MarkDirty();
4942 0 : lineList->front()->SetInvalidateTextRuns(true);
4943 : }
4944 0 : nsFrameList& frames = lineList == &mLines ? mFrames : overflowLines->mFrames;
4945 : const nsFrameList::Slice& newFrames =
4946 0 : frames.InsertFrames(nsnull, aPrevSibling, aFrameList);
4947 :
4948 : // Walk through the new frames being added and update the line data
4949 : // structures to fit.
4950 0 : for (nsFrameList::Enumerator e(newFrames); !e.AtEnd(); e.Next()) {
4951 0 : nsIFrame* newFrame = e.get();
4952 0 : NS_ASSERTION(!aPrevSibling || aPrevSibling->GetNextSibling() == newFrame,
4953 : "Unexpected aPrevSibling");
4954 0 : NS_ASSERTION(newFrame->GetType() != nsGkAtoms::placeholderFrame ||
4955 : (!newFrame->GetStyleDisplay()->IsAbsolutelyPositioned() &&
4956 : !newFrame->GetStyleDisplay()->IsFloating()),
4957 : "Placeholders should not float or be positioned");
4958 :
4959 0 : bool isBlock = newFrame->GetStyleDisplay()->IsBlockOutside();
4960 :
4961 : // If the frame is a block frame, or if there is no previous line or if the
4962 : // previous line is a block line we need to make a new line. We also make
4963 : // a new line, as an optimization, in the two cases we know we'll need it:
4964 : // if the previous line ended with a <br>, or if it has significant whitespace
4965 : // and ended in a newline.
4966 0 : if (isBlock || prevSibLine == lineList->end() || prevSibLine->IsBlock() ||
4967 0 : (aPrevSibling && ShouldPutNextSiblingOnNewLine(aPrevSibling))) {
4968 : // Create a new line for the frame and add its line to the line
4969 : // list.
4970 0 : nsLineBox* line = NewLineBox(newFrame, isBlock);
4971 0 : if (!line) {
4972 0 : return NS_ERROR_OUT_OF_MEMORY;
4973 : }
4974 0 : if (prevSibLine != lineList->end()) {
4975 : // Append new line after prevSibLine
4976 0 : lineList->after_insert(prevSibLine, line);
4977 0 : ++prevSibLine;
4978 : }
4979 : else {
4980 : // New line is going before the other lines
4981 0 : lineList->push_front(line);
4982 0 : prevSibLine = lineList->begin();
4983 : }
4984 : }
4985 : else {
4986 0 : prevSibLine->NoteFrameAdded(newFrame);
4987 : // We're adding inline content to prevSibLine, so we need to mark it
4988 : // dirty, ensure its textruns are recomputed, and possibly do the same
4989 : // to its previous line since that line may be able to pull content up.
4990 0 : MarkLineDirty(prevSibLine);
4991 : }
4992 :
4993 0 : aPrevSibling = newFrame;
4994 : }
4995 :
4996 : #ifdef DEBUG
4997 0 : VerifyLines(true);
4998 : #endif
4999 0 : return NS_OK;
5000 : }
5001 :
5002 : nsBlockFrame::line_iterator
5003 0 : nsBlockFrame::RemoveFloat(nsIFrame* aFloat) {
5004 : // Find which line contains the float, so we can update
5005 : // the float cache.
5006 0 : line_iterator line = begin_lines(), line_end = end_lines();
5007 0 : for ( ; line != line_end; ++line) {
5008 0 : if (line->IsInline() && line->RemoveFloat(aFloat)) {
5009 0 : break;
5010 : }
5011 : }
5012 :
5013 : // Try to destroy if it's in mFloats.
5014 0 : if (mFloats.DestroyFrameIfPresent(aFloat)) {
5015 0 : return line;
5016 : }
5017 :
5018 : // Try our overflow list
5019 : {
5020 0 : nsAutoOOFFrameList oofs(this);
5021 0 : if (oofs.mList.DestroyFrameIfPresent(aFloat)) {
5022 0 : return line_end;
5023 : }
5024 : }
5025 :
5026 0 : NS_ERROR("Destroying float without removing from a child list.");
5027 0 : return line_end;
5028 : }
5029 :
5030 0 : static void MarkSameFloatManagerLinesDirty(nsBlockFrame* aBlock)
5031 : {
5032 0 : nsBlockFrame* blockWithFloatMgr = aBlock;
5033 0 : while (!(blockWithFloatMgr->GetStateBits() & NS_BLOCK_FLOAT_MGR)) {
5034 0 : nsBlockFrame* bf = nsLayoutUtils::GetAsBlock(blockWithFloatMgr->GetParent());
5035 0 : if (!bf) {
5036 0 : break;
5037 : }
5038 0 : blockWithFloatMgr = bf;
5039 : }
5040 :
5041 : // Mark every line at and below the line where the float was
5042 : // dirty, and mark their lines dirty too. We could probably do
5043 : // something more efficient --- e.g., just dirty the lines that intersect
5044 : // the float vertically.
5045 0 : MarkAllDescendantLinesDirty(blockWithFloatMgr);
5046 0 : }
5047 :
5048 : /**
5049 : * Returns true if aFrame is a block that has one or more float children.
5050 : */
5051 0 : static bool BlockHasAnyFloats(nsIFrame* aFrame)
5052 : {
5053 0 : nsBlockFrame* block = nsLayoutUtils::GetAsBlock(aFrame);
5054 0 : if (!block)
5055 0 : return false;
5056 0 : if (block->GetFirstChild(nsIFrame::kFloatList))
5057 0 : return true;
5058 :
5059 0 : nsLineList::iterator line = block->begin_lines();
5060 0 : nsLineList::iterator endLine = block->end_lines();
5061 0 : while (line != endLine) {
5062 0 : if (line->IsBlock() && BlockHasAnyFloats(line->mFirstChild))
5063 0 : return true;
5064 0 : ++line;
5065 : }
5066 0 : return false;
5067 : }
5068 :
5069 : NS_IMETHODIMP
5070 0 : nsBlockFrame::RemoveFrame(ChildListID aListID,
5071 : nsIFrame* aOldFrame)
5072 : {
5073 0 : nsresult rv = NS_OK;
5074 :
5075 : #ifdef NOISY_REFLOW_REASON
5076 : ListTag(stdout);
5077 : printf(": remove ");
5078 : nsFrame::ListTag(stdout, aOldFrame);
5079 : printf("\n");
5080 : #endif
5081 :
5082 0 : if (aListID == kPrincipalList) {
5083 0 : bool hasFloats = BlockHasAnyFloats(aOldFrame);
5084 0 : rv = DoRemoveFrame(aOldFrame, REMOVE_FIXED_CONTINUATIONS);
5085 0 : if (hasFloats) {
5086 0 : MarkSameFloatManagerLinesDirty(this);
5087 : }
5088 : }
5089 0 : else if (kAbsoluteList == aListID) {
5090 0 : nsContainerFrame::RemoveFrame(aListID, aOldFrame);
5091 0 : return NS_OK;
5092 : }
5093 0 : else if (kFloatList == aListID) {
5094 : // Make sure to mark affected lines dirty for the float frame
5095 : // we are removing; this way is a bit messy, but so is the rest of the code.
5096 : // See bug 390762.
5097 0 : NS_ASSERTION(!aOldFrame->GetPrevContinuation(),
5098 : "RemoveFrame should not be called on pushed floats.");
5099 0 : for (nsIFrame* f = aOldFrame;
5100 0 : f && !(f->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER);
5101 0 : f = f->GetNextContinuation()) {
5102 0 : MarkSameFloatManagerLinesDirty(static_cast<nsBlockFrame*>(f->GetParent()));
5103 : }
5104 0 : DoRemoveOutOfFlowFrame(aOldFrame);
5105 : }
5106 : #ifdef IBMBIDI
5107 0 : else if (kNoReflowPrincipalList == aListID) {
5108 : // Skip the call to |FrameNeedsReflow| below by returning now.
5109 0 : return DoRemoveFrame(aOldFrame, REMOVE_FIXED_CONTINUATIONS);
5110 : }
5111 : #endif // IBMBIDI
5112 : else {
5113 0 : NS_ERROR("unexpected child list");
5114 0 : rv = NS_ERROR_INVALID_ARG;
5115 : }
5116 :
5117 0 : if (NS_SUCCEEDED(rv)) {
5118 0 : PresContext()->PresShell()->
5119 : FrameNeedsReflow(this, nsIPresShell::eTreeChange,
5120 0 : NS_FRAME_HAS_DIRTY_CHILDREN); // XXX sufficient?
5121 : }
5122 0 : return rv;
5123 : }
5124 :
5125 : void
5126 0 : nsBlockFrame::DoRemoveOutOfFlowFrame(nsIFrame* aFrame)
5127 : {
5128 : // The containing block is always the parent of aFrame.
5129 0 : nsBlockFrame* block = (nsBlockFrame*)aFrame->GetParent();
5130 :
5131 : // Remove aFrame from the appropriate list.
5132 0 : const nsStyleDisplay* display = aFrame->GetStyleDisplay();
5133 0 : if (display->IsAbsolutelyPositioned()) {
5134 : // This also deletes the next-in-flows
5135 : block->GetAbsoluteContainingBlock()->RemoveFrame(block,
5136 : kAbsoluteList,
5137 0 : aFrame);
5138 : }
5139 : else {
5140 : // First remove aFrame's next-in-flows
5141 0 : nsIFrame* nif = aFrame->GetNextInFlow();
5142 0 : if (nif) {
5143 0 : static_cast<nsContainerFrame*>(nif->GetParent())
5144 0 : ->DeleteNextInFlowChild(aFrame->PresContext(), nif, false);
5145 : }
5146 : // Now remove aFrame
5147 : // This also destroys the frame.
5148 0 : block->RemoveFloat(aFrame);
5149 : }
5150 0 : }
5151 :
5152 : /**
5153 : * This helps us iterate over the list of all normal + overflow lines
5154 : */
5155 : void
5156 0 : nsBlockFrame::TryAllLines(nsLineList::iterator* aIterator,
5157 : nsLineList::iterator* aStartIterator,
5158 : nsLineList::iterator* aEndIterator,
5159 : bool* aInOverflowLines,
5160 : FrameLines** aOverflowLines)
5161 : {
5162 0 : if (*aIterator == *aEndIterator) {
5163 0 : if (!*aInOverflowLines) {
5164 : // Try the overflow lines
5165 0 : *aInOverflowLines = true;
5166 0 : FrameLines* lines = GetOverflowLines();
5167 0 : if (lines) {
5168 0 : *aStartIterator = lines->mLines.begin();
5169 0 : *aIterator = *aStartIterator;
5170 0 : *aEndIterator = lines->mLines.end();
5171 0 : *aOverflowLines = lines;
5172 : }
5173 : }
5174 : }
5175 0 : }
5176 :
5177 0 : nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame,
5178 : line_iterator aLine)
5179 0 : : mFrame(aFrame), mLine(aLine), mInOverflowLines(nsnull)
5180 : {
5181 : // This will assert if aLine isn't in mLines of aFrame:
5182 0 : DebugOnly<bool> check = aLine == mFrame->begin_lines();
5183 0 : }
5184 :
5185 0 : nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame,
5186 : line_iterator aLine, bool aInOverflow)
5187 0 : : mFrame(aFrame), mLine(aLine), mInOverflowLines(nsnull)
5188 : {
5189 0 : if (aInOverflow) {
5190 0 : mInOverflowLines = &aFrame->GetOverflowLines()->mLines;
5191 : }
5192 0 : }
5193 :
5194 0 : nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame,
5195 : bool* aFoundValidLine)
5196 0 : : mFrame(aFrame), mInOverflowLines(nsnull)
5197 : {
5198 0 : mLine = aFrame->begin_lines();
5199 0 : *aFoundValidLine = FindValidLine();
5200 0 : }
5201 :
5202 : static nsIFrame*
5203 0 : FindChildContaining(nsBlockFrame* aFrame, nsIFrame* aFindFrame)
5204 : {
5205 0 : NS_ASSERTION(aFrame, "must have frame");
5206 : nsIFrame* child;
5207 0 : while (true) {
5208 0 : nsIFrame* block = aFrame;
5209 0 : do {
5210 0 : child = nsLayoutUtils::FindChildContainingDescendant(block, aFindFrame);
5211 0 : if (child)
5212 0 : break;
5213 0 : block = block->GetNextContinuation();
5214 : } while (block);
5215 0 : if (!child)
5216 0 : return nsnull;
5217 0 : if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW))
5218 : break;
5219 0 : aFindFrame = aFrame->PresContext()->FrameManager()->GetPlaceholderFrameFor(child);
5220 : }
5221 :
5222 0 : return child;
5223 : }
5224 :
5225 0 : nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame,
5226 : nsIFrame* aFindFrame, bool* aFoundValidLine)
5227 0 : : mFrame(aFrame), mInOverflowLines(nsnull)
5228 : {
5229 0 : *aFoundValidLine = false;
5230 :
5231 0 : nsIFrame* child = FindChildContaining(aFrame, aFindFrame);
5232 0 : if (!child)
5233 0 : return;
5234 :
5235 : // Try to use the cursor if it exists, otherwise fall back to the first line
5236 0 : nsLineBox* cursor = aFrame->GetLineCursor();
5237 0 : if (!cursor) {
5238 0 : line_iterator iter = aFrame->begin_lines();
5239 0 : if (iter != aFrame->end_lines()) {
5240 0 : cursor = iter;
5241 : }
5242 : }
5243 :
5244 0 : if (cursor) {
5245 : // Perform a simultaneous forward and reverse search starting from the
5246 : // line cursor.
5247 0 : nsBlockFrame::line_iterator line = aFrame->line(cursor);
5248 0 : nsBlockFrame::reverse_line_iterator rline = aFrame->rline(cursor);
5249 0 : nsBlockFrame::line_iterator line_end = aFrame->end_lines();
5250 0 : nsBlockFrame::reverse_line_iterator rline_end = aFrame->rend_lines();
5251 : // rline is positioned on the line containing 'cursor', so it's not
5252 : // rline_end. So we can safely increment it (i.e. move it to one line
5253 : // earlier) to start searching there.
5254 0 : ++rline;
5255 0 : while (line != line_end || rline != rline_end) {
5256 0 : if (line != line_end) {
5257 0 : if (line->Contains(child)) {
5258 0 : *aFoundValidLine = true;
5259 0 : mLine = line;
5260 0 : return;
5261 : }
5262 0 : ++line;
5263 : }
5264 0 : if (rline != rline_end) {
5265 0 : if (rline->Contains(child)) {
5266 0 : *aFoundValidLine = true;
5267 0 : mLine = rline;
5268 0 : return;
5269 : }
5270 0 : ++rline;
5271 : }
5272 : }
5273 : // Didn't find the line
5274 : }
5275 :
5276 : // If we reach here, it means that we have not been able to find the
5277 : // desired frame in our in-flow lines. So we should start looking at
5278 : // our overflow lines. In order to do that, we set mLine to the end
5279 : // iterator so that FindValidLine starts to look at overflow lines,
5280 : // if any.
5281 :
5282 0 : mLine = aFrame->end_lines();
5283 :
5284 0 : if (!FindValidLine())
5285 0 : return;
5286 :
5287 0 : do {
5288 0 : if (mLine->Contains(child)) {
5289 0 : *aFoundValidLine = true;
5290 0 : return;
5291 : }
5292 : } while (Next());
5293 : }
5294 :
5295 : nsBlockFrame::line_iterator
5296 0 : nsBlockInFlowLineIterator::End()
5297 : {
5298 0 : return mInOverflowLines ? mInOverflowLines->end() : mFrame->end_lines();
5299 : }
5300 :
5301 : bool
5302 0 : nsBlockInFlowLineIterator::IsLastLineInList()
5303 : {
5304 0 : line_iterator end = End();
5305 0 : return mLine != end && mLine.next() == end;
5306 : }
5307 :
5308 : bool
5309 0 : nsBlockInFlowLineIterator::Next()
5310 : {
5311 0 : ++mLine;
5312 0 : return FindValidLine();
5313 : }
5314 :
5315 : bool
5316 0 : nsBlockInFlowLineIterator::Prev()
5317 : {
5318 0 : line_iterator begin = mInOverflowLines ? mInOverflowLines->begin() : mFrame->begin_lines();
5319 0 : if (mLine != begin) {
5320 0 : --mLine;
5321 0 : return true;
5322 : }
5323 0 : bool currentlyInOverflowLines = mInOverflowLines != nsnull;
5324 0 : while (true) {
5325 0 : if (currentlyInOverflowLines) {
5326 0 : mInOverflowLines = nsnull;
5327 0 : mLine = mFrame->end_lines();
5328 0 : if (mLine != mFrame->begin_lines()) {
5329 0 : --mLine;
5330 0 : return true;
5331 : }
5332 : } else {
5333 0 : mFrame = static_cast<nsBlockFrame*>(mFrame->GetPrevInFlow());
5334 0 : if (!mFrame)
5335 0 : return false;
5336 0 : nsBlockFrame::FrameLines* overflowLines = mFrame->GetOverflowLines();
5337 0 : mInOverflowLines = overflowLines ? &overflowLines->mLines : nsnull;
5338 0 : if (mInOverflowLines) {
5339 0 : mLine = mInOverflowLines->end();
5340 0 : NS_ASSERTION(mLine != mInOverflowLines->begin(), "empty overflow line list?");
5341 0 : --mLine;
5342 0 : return true;
5343 : }
5344 : }
5345 0 : currentlyInOverflowLines = !currentlyInOverflowLines;
5346 : }
5347 : }
5348 :
5349 : bool
5350 0 : nsBlockInFlowLineIterator::FindValidLine()
5351 : {
5352 0 : line_iterator end = mInOverflowLines ? mInOverflowLines->end() : mFrame->end_lines();
5353 0 : if (mLine != end)
5354 0 : return true;
5355 0 : bool currentlyInOverflowLines = mInOverflowLines != nsnull;
5356 0 : while (true) {
5357 0 : if (currentlyInOverflowLines) {
5358 0 : mFrame = static_cast<nsBlockFrame*>(mFrame->GetNextInFlow());
5359 0 : if (!mFrame)
5360 0 : return false;
5361 0 : mInOverflowLines = nsnull;
5362 0 : mLine = mFrame->begin_lines();
5363 0 : if (mLine != mFrame->end_lines())
5364 0 : return true;
5365 : } else {
5366 0 : nsBlockFrame::FrameLines* overflowLines = mFrame->GetOverflowLines();
5367 0 : mInOverflowLines = overflowLines ? &overflowLines->mLines : nsnull;
5368 0 : if (mInOverflowLines) {
5369 0 : mLine = mInOverflowLines->begin();
5370 0 : NS_ASSERTION(mLine != mInOverflowLines->end(), "empty overflow line list?");
5371 0 : return true;
5372 : }
5373 : }
5374 0 : currentlyInOverflowLines = !currentlyInOverflowLines;
5375 : }
5376 : }
5377 :
5378 0 : static nsresult RemoveBlockChild(nsIFrame* aFrame,
5379 : bool aRemoveOnlyFluidContinuations)
5380 : {
5381 0 : if (!aFrame)
5382 0 : return NS_OK;
5383 :
5384 0 : nsBlockFrame* nextBlock = nsLayoutUtils::GetAsBlock(aFrame->GetParent());
5385 0 : NS_ASSERTION(nextBlock,
5386 : "Our child's continuation's parent is not a block?");
5387 : return nextBlock->DoRemoveFrame(aFrame,
5388 0 : (aRemoveOnlyFluidContinuations ? 0 : nsBlockFrame::REMOVE_FIXED_CONTINUATIONS));
5389 : }
5390 :
5391 : // This function removes aDeletedFrame and all its continuations. It
5392 : // is optimized for deleting a whole series of frames. The easy
5393 : // implementation would invoke itself recursively on
5394 : // aDeletedFrame->GetNextContinuation, then locate the line containing
5395 : // aDeletedFrame and remove aDeletedFrame from that line. But here we
5396 : // start by locating aDeletedFrame and then scanning from that point
5397 : // on looking for continuations.
5398 : nsresult
5399 0 : nsBlockFrame::DoRemoveFrame(nsIFrame* aDeletedFrame, PRUint32 aFlags)
5400 : {
5401 : // Clear our line cursor, since our lines may change.
5402 0 : ClearLineCursor();
5403 :
5404 0 : nsPresContext* presContext = PresContext();
5405 0 : if (aDeletedFrame->GetStateBits() &
5406 : (NS_FRAME_OUT_OF_FLOW | NS_FRAME_IS_OVERFLOW_CONTAINER)) {
5407 0 : if (!aDeletedFrame->GetPrevInFlow()) {
5408 0 : NS_ASSERTION(aDeletedFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW,
5409 : "Expected out-of-flow frame");
5410 0 : DoRemoveOutOfFlowFrame(aDeletedFrame);
5411 : }
5412 : else {
5413 : nsContainerFrame::DeleteNextInFlowChild(presContext, aDeletedFrame,
5414 0 : (aFlags & FRAMES_ARE_EMPTY) != 0);
5415 : }
5416 0 : return NS_OK;
5417 : }
5418 :
5419 : // Find the line that contains deletedFrame
5420 0 : nsLineList::iterator line_start = mLines.begin(),
5421 0 : line_end = mLines.end();
5422 0 : nsLineList::iterator line = line_start;
5423 0 : FrameLines* overflowLines = nsnull;
5424 0 : bool searchingOverflowList = false;
5425 : // Make sure we look in the overflow lines even if the normal line
5426 : // list is empty
5427 : TryAllLines(&line, &line_start, &line_end, &searchingOverflowList,
5428 0 : &overflowLines);
5429 0 : while (line != line_end) {
5430 0 : if (line->Contains(aDeletedFrame)) {
5431 0 : break;
5432 : }
5433 0 : ++line;
5434 : TryAllLines(&line, &line_start, &line_end, &searchingOverflowList,
5435 0 : &overflowLines);
5436 : }
5437 :
5438 0 : if (line == line_end) {
5439 0 : NS_ERROR("can't find deleted frame in lines");
5440 0 : return NS_ERROR_FAILURE;
5441 : }
5442 :
5443 0 : if (!(aFlags & FRAMES_ARE_EMPTY)) {
5444 0 : if (line != line_start) {
5445 0 : line.prev()->MarkDirty();
5446 0 : line.prev()->SetInvalidateTextRuns(true);
5447 : }
5448 0 : else if (searchingOverflowList && !mLines.empty()) {
5449 0 : mLines.back()->MarkDirty();
5450 0 : mLines.back()->SetInvalidateTextRuns(true);
5451 : }
5452 : }
5453 :
5454 0 : while (line != line_end && aDeletedFrame) {
5455 0 : NS_ASSERTION(this == aDeletedFrame->GetParent(), "messed up delete code");
5456 0 : NS_ASSERTION(line->Contains(aDeletedFrame), "frame not in line");
5457 :
5458 0 : if (!(aFlags & FRAMES_ARE_EMPTY)) {
5459 0 : line->MarkDirty();
5460 0 : line->SetInvalidateTextRuns(true);
5461 : }
5462 :
5463 : // If the frame being deleted is the last one on the line then
5464 : // optimize away the line->Contains(next-in-flow) call below.
5465 0 : bool isLastFrameOnLine = 1 == line->GetChildCount();
5466 0 : if (!isLastFrameOnLine) {
5467 0 : line_iterator next = line.next();
5468 0 : nsIFrame* lastFrame = next != line_end ?
5469 0 : next->mFirstChild->GetPrevSibling() :
5470 0 : (searchingOverflowList ? overflowLines->mFrames.LastChild() :
5471 0 : mFrames.LastChild());
5472 0 : NS_ASSERTION(next == line_end || lastFrame == line->LastChild(),
5473 : "unexpected line frames");
5474 0 : isLastFrameOnLine = lastFrame == aDeletedFrame;
5475 : }
5476 :
5477 : // Remove aDeletedFrame from the line
5478 0 : if (line->mFirstChild == aDeletedFrame) {
5479 : // We should be setting this to null if aDeletedFrame
5480 : // is the only frame on the line. HOWEVER in that case
5481 : // we will be removing the line anyway, see below.
5482 0 : line->mFirstChild = aDeletedFrame->GetNextSibling();
5483 : }
5484 :
5485 : // Hmm, this won't do anything if we're removing a frame in the first
5486 : // overflow line... Hopefully doesn't matter
5487 0 : --line;
5488 0 : if (line != line_end && !line->IsBlock()) {
5489 : // Since we just removed a frame that follows some inline
5490 : // frames, we need to reflow the previous line.
5491 0 : line->MarkDirty();
5492 : }
5493 0 : ++line;
5494 :
5495 : // Take aDeletedFrame out of the sibling list. Note that
5496 : // prevSibling will only be nsnull when we are deleting the very
5497 : // first frame in the main or overflow list.
5498 0 : if (searchingOverflowList) {
5499 0 : overflowLines->mFrames.RemoveFrame(aDeletedFrame);
5500 : } else {
5501 0 : mFrames.RemoveFrame(aDeletedFrame);
5502 : }
5503 :
5504 : // Update the child count of the line to be accurate
5505 0 : line->NoteFrameRemoved(aDeletedFrame);
5506 :
5507 : // Destroy frame; capture its next continuation first in case we need
5508 : // to destroy that too.
5509 : nsIFrame* deletedNextContinuation = (aFlags & REMOVE_FIXED_CONTINUATIONS) ?
5510 0 : aDeletedFrame->GetNextContinuation() : aDeletedFrame->GetNextInFlow();
5511 : #ifdef NOISY_REMOVE_FRAME
5512 : printf("DoRemoveFrame: %s line=%p frame=",
5513 : searchingOverflowList?"overflow":"normal", line.get());
5514 : nsFrame::ListTag(stdout, aDeletedFrame);
5515 : printf(" prevSibling=%p deletedNextContinuation=%p\n",
5516 : aDeletedFrame->GetPrevSibling(), deletedNextContinuation);
5517 : #endif
5518 :
5519 : // If next-in-flow is an overflow container, must remove it first.
5520 0 : if (deletedNextContinuation &&
5521 0 : deletedNextContinuation->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) {
5522 0 : static_cast<nsContainerFrame*>(deletedNextContinuation->GetParent())
5523 0 : ->DeleteNextInFlowChild(presContext, deletedNextContinuation, false);
5524 0 : deletedNextContinuation = nsnull;
5525 : }
5526 :
5527 0 : aDeletedFrame->Destroy();
5528 0 : aDeletedFrame = deletedNextContinuation;
5529 :
5530 0 : bool haveAdvancedToNextLine = false;
5531 : // If line is empty, remove it now.
5532 0 : if (0 == line->GetChildCount()) {
5533 : #ifdef NOISY_REMOVE_FRAME
5534 : printf("DoRemoveFrame: %s line=%p became empty so it will be removed\n",
5535 : searchingOverflowList?"overflow":"normal", line.get());
5536 : #endif
5537 0 : nsLineBox *cur = line;
5538 0 : if (!searchingOverflowList) {
5539 0 : line = mLines.erase(line);
5540 : // Invalidate the space taken up by the line.
5541 : // XXX We need to do this if we're removing a frame as a result of
5542 : // a call to RemoveFrame(), but we may not need to do this in all
5543 : // cases...
5544 0 : nsRect visOverflow(cur->GetVisualOverflowArea());
5545 : #ifdef NOISY_BLOCK_INVALIDATE
5546 : printf("%p invalidate 10 (%d, %d, %d, %d)\n",
5547 : this, visOverflow.x, visOverflow.y,
5548 : visOverflow.width, visOverflow.height);
5549 : #endif
5550 0 : Invalidate(visOverflow);
5551 : } else {
5552 : // XXX update searchingOverflowList directly, remove only when empty
5553 0 : FrameLines* overflowLines = RemoveOverflowLines();
5554 0 : line = overflowLines->mLines.erase(line);
5555 0 : if (!overflowLines->mLines.empty()) {
5556 0 : SetOverflowLines(overflowLines);
5557 : } else {
5558 0 : delete overflowLines;
5559 : // We just invalidated our iterators. Since we were in
5560 : // the overflow lines list, which is now empty, set them
5561 : // so we're at the end of the regular line list.
5562 0 : line_start = mLines.begin();
5563 0 : line_end = mLines.end();
5564 0 : line = line_end;
5565 : }
5566 : }
5567 0 : FreeLineBox(cur);
5568 :
5569 : // If we're removing a line, ReflowDirtyLines isn't going to
5570 : // know that it needs to slide lines unless something is marked
5571 : // dirty. So mark the previous margin of the next line dirty if
5572 : // there is one.
5573 0 : if (line != line_end) {
5574 0 : line->MarkPreviousMarginDirty();
5575 : }
5576 0 : haveAdvancedToNextLine = true;
5577 : } else {
5578 : // Make the line that just lost a frame dirty, and advance to
5579 : // the next line.
5580 0 : if (!deletedNextContinuation || isLastFrameOnLine ||
5581 0 : !line->Contains(deletedNextContinuation)) {
5582 0 : line->MarkDirty();
5583 0 : ++line;
5584 0 : haveAdvancedToNextLine = true;
5585 : }
5586 : }
5587 :
5588 0 : if (deletedNextContinuation) {
5589 : // See if we should keep looking in the current flow's line list.
5590 0 : if (deletedNextContinuation->GetParent() != this) {
5591 : // The deceased frames continuation is not a child of the
5592 : // current block. So break out of the loop so that we advance
5593 : // to the next parent.
5594 : //
5595 : // If we have a continuation in a different block then all bets are
5596 : // off regarding whether we are deleting frames without actual content,
5597 : // so don't propagate FRAMES_ARE_EMPTY any further.
5598 0 : aFlags &= ~FRAMES_ARE_EMPTY;
5599 0 : break;
5600 : }
5601 :
5602 : // If we advanced to the next line then check if we should switch to the
5603 : // overflow line list.
5604 0 : if (haveAdvancedToNextLine) {
5605 0 : if (line != line_end && !searchingOverflowList &&
5606 0 : !line->Contains(deletedNextContinuation)) {
5607 : // We have advanced to the next *normal* line but the next-in-flow
5608 : // is not there - force a switch to the overflow line list.
5609 0 : line = line_end;
5610 : }
5611 :
5612 : TryAllLines(&line, &line_start, &line_end, &searchingOverflowList,
5613 0 : &overflowLines);
5614 : #ifdef NOISY_REMOVE_FRAME
5615 : printf("DoRemoveFrame: now on %s line=%p\n",
5616 : searchingOverflowList?"overflow":"normal", line.get());
5617 : #endif
5618 : }
5619 : }
5620 : }
5621 :
5622 0 : if (!(aFlags & FRAMES_ARE_EMPTY) && line.next() != line_end) {
5623 0 : line.next()->MarkDirty();
5624 0 : line.next()->SetInvalidateTextRuns(true);
5625 : }
5626 :
5627 : #ifdef DEBUG
5628 0 : VerifyLines(true);
5629 0 : VerifyOverflowSituation();
5630 : #endif
5631 :
5632 : // Advance to next flow block if the frame has more continuations
5633 0 : return RemoveBlockChild(aDeletedFrame, !(aFlags & REMOVE_FIXED_CONTINUATIONS));
5634 : }
5635 :
5636 : nsresult
5637 0 : nsBlockFrame::StealFrame(nsPresContext* aPresContext,
5638 : nsIFrame* aChild,
5639 : bool aForceNormal)
5640 : {
5641 0 : NS_PRECONDITION(aPresContext && aChild, "null pointer");
5642 :
5643 0 : if ((aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
5644 0 : aChild->GetStyleDisplay()->IsFloating()) {
5645 0 : bool removed = mFloats.RemoveFrameIfPresent(aChild);
5646 0 : if (!removed) {
5647 0 : nsFrameList* list = GetPushedFloats();
5648 0 : if (list) {
5649 0 : removed = list->RemoveFrameIfPresent(aChild);
5650 : }
5651 : }
5652 0 : return removed ? NS_OK : NS_ERROR_UNEXPECTED;
5653 : }
5654 :
5655 0 : if ((aChild->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)
5656 0 : && !aForceNormal)
5657 0 : return nsContainerFrame::StealFrame(aPresContext, aChild);
5658 :
5659 : // Find the line and the previous sibling that contains
5660 : // aChild; we also find the pointer to the line.
5661 0 : nsLineList::iterator line = mLines.begin(),
5662 0 : line_start = line,
5663 0 : line_end = mLines.end();
5664 0 : bool searchingOverflowList = false;
5665 0 : FrameLines* overflowLines = nsnull;
5666 0 : nsIFrame* prevSibling = nsnull;
5667 : // Make sure we look in the overflow lines even if the normal line
5668 : // list is empty
5669 : TryAllLines(&line, &line_start, &line_end, &searchingOverflowList,
5670 0 : &overflowLines);
5671 0 : while (line != line_end) {
5672 0 : nsIFrame* frame = line->mFirstChild;
5673 0 : PRInt32 n = line->GetChildCount();
5674 0 : while (--n >= 0) {
5675 0 : if (frame == aChild) {
5676 0 : if (frame == line->mFirstChild) {
5677 0 : line->mFirstChild = frame->GetNextSibling();
5678 : }
5679 0 : if (searchingOverflowList) {
5680 0 : overflowLines->mFrames.RemoveFrame(frame);
5681 : } else {
5682 0 : mFrames.RemoveFrame(frame);
5683 : }
5684 :
5685 : // Register removal with the line boxes
5686 0 : line->NoteFrameRemoved(frame);
5687 0 : if (line->GetChildCount() > 0) {
5688 0 : line->MarkDirty();
5689 : } else {
5690 : // Remove the line box
5691 0 : nsLineBox* lineBox = line;
5692 0 : if (searchingOverflowList) {
5693 : // Erase line, but avoid making the overflow line list empty
5694 : // XXX update overflowLines directly, remove only when empty
5695 0 : RemoveOverflowLines();
5696 0 : line = overflowLines->mLines.erase(line);
5697 0 : if (!overflowLines->mLines.empty()) {
5698 0 : SetOverflowLines(overflowLines);
5699 : } else {
5700 0 : delete overflowLines;
5701 : // We just invalidated our iterators. Since we were in
5702 : // the overflow lines list, which is now empty, set them
5703 : // so we're at the end of the regular line list.
5704 0 : line_start = mLines.begin();
5705 0 : line_end = mLines.end();
5706 0 : line = line_end;
5707 : }
5708 : } else {
5709 0 : line = mLines.erase(line);
5710 : }
5711 0 : FreeLineBox(lineBox);
5712 0 : if (line != line_end) {
5713 : // Line disappeared, so tell next line it may have to change position
5714 0 : line->MarkPreviousMarginDirty();
5715 : }
5716 : }
5717 :
5718 : // Ok, we're done
5719 0 : return NS_OK;
5720 : }
5721 0 : prevSibling = frame;
5722 0 : frame = frame->GetNextSibling();
5723 : }
5724 0 : ++line;
5725 : TryAllLines(&line, &line_start, &line_end, &searchingOverflowList,
5726 0 : &overflowLines);
5727 0 : if (prevSibling && !prevSibling->GetNextSibling()) {
5728 : // We just switched to the overflow list. Null out prevSibling
5729 0 : prevSibling = nsnull;
5730 : }
5731 : }
5732 0 : return NS_ERROR_UNEXPECTED;
5733 : }
5734 :
5735 : void
5736 0 : nsBlockFrame::DeleteNextInFlowChild(nsPresContext* aPresContext,
5737 : nsIFrame* aNextInFlow,
5738 : bool aDeletingEmptyFrames)
5739 : {
5740 0 : NS_PRECONDITION(aNextInFlow->GetPrevInFlow(), "bad next-in-flow");
5741 :
5742 0 : if (aNextInFlow->GetStateBits() &
5743 : (NS_FRAME_OUT_OF_FLOW | NS_FRAME_IS_OVERFLOW_CONTAINER)) {
5744 : nsContainerFrame::DeleteNextInFlowChild(aPresContext,
5745 0 : aNextInFlow, aDeletingEmptyFrames);
5746 : }
5747 : else {
5748 : #ifdef DEBUG
5749 0 : if (aDeletingEmptyFrames) {
5750 0 : nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(aNextInFlow);
5751 : }
5752 : #endif
5753 : DoRemoveFrame(aNextInFlow,
5754 0 : aDeletingEmptyFrames ? FRAMES_ARE_EMPTY : 0);
5755 : }
5756 0 : }
5757 :
5758 : ////////////////////////////////////////////////////////////////////////
5759 : // Float support
5760 :
5761 : nsRect
5762 0 : nsBlockFrame::AdjustFloatAvailableSpace(nsBlockReflowState& aState,
5763 : const nsRect& aFloatAvailableSpace,
5764 : nsIFrame* aFloatFrame)
5765 : {
5766 : // Compute the available width. By default, assume the width of the
5767 : // containing block.
5768 : nscoord availWidth;
5769 0 : const nsStyleDisplay* floatDisplay = aFloatFrame->GetStyleDisplay();
5770 :
5771 0 : if (NS_STYLE_DISPLAY_TABLE != floatDisplay->mDisplay ||
5772 0 : eCompatibility_NavQuirks != aState.mPresContext->CompatibilityMode() ) {
5773 0 : availWidth = aState.mContentArea.width;
5774 : }
5775 : else {
5776 : // This quirk matches the one in nsBlockReflowState::FlowAndPlaceFloat
5777 : // give tables only the available space
5778 : // if they can shrink we may not be constrained to place
5779 : // them in the next line
5780 0 : availWidth = aFloatAvailableSpace.width;
5781 : }
5782 :
5783 : nscoord availHeight = NS_UNCONSTRAINEDSIZE == aState.mContentArea.height
5784 : ? NS_UNCONSTRAINEDSIZE
5785 0 : : NS_MAX(0, aState.mContentArea.YMost() - aState.mY);
5786 :
5787 : #ifdef DISABLE_FLOAT_BREAKING_IN_COLUMNS
5788 0 : if (availHeight != NS_UNCONSTRAINEDSIZE &&
5789 0 : nsLayoutUtils::GetClosestFrameOfType(this, nsGkAtoms::columnSetFrame)) {
5790 : // Tell the float it has unrestricted height, so it won't break.
5791 : // If the float doesn't actually fit in the column it will fail to be
5792 : // placed, and either move to the top of the next column or just
5793 : // overflow.
5794 0 : availHeight = NS_UNCONSTRAINEDSIZE;
5795 : }
5796 : #endif
5797 :
5798 : return nsRect(aState.mContentArea.x,
5799 : aState.mContentArea.y,
5800 0 : availWidth, availHeight);
5801 : }
5802 :
5803 : nscoord
5804 0 : nsBlockFrame::ComputeFloatWidth(nsBlockReflowState& aState,
5805 : const nsRect& aFloatAvailableSpace,
5806 : nsIFrame* aFloat)
5807 : {
5808 0 : NS_PRECONDITION(aFloat->GetStateBits() & NS_FRAME_OUT_OF_FLOW,
5809 : "aFloat must be an out-of-flow frame");
5810 : // Reflow the float.
5811 : nsRect availSpace = AdjustFloatAvailableSpace(aState, aFloatAvailableSpace,
5812 0 : aFloat);
5813 :
5814 : nsHTMLReflowState floatRS(aState.mPresContext, aState.mReflowState, aFloat,
5815 0 : nsSize(availSpace.width, availSpace.height));
5816 0 : return floatRS.ComputedWidth() + floatRS.mComputedBorderPadding.LeftRight() +
5817 0 : floatRS.mComputedMargin.LeftRight();
5818 : }
5819 :
5820 : nsresult
5821 0 : nsBlockFrame::ReflowFloat(nsBlockReflowState& aState,
5822 : const nsRect& aAdjustedAvailableSpace,
5823 : nsIFrame* aFloat,
5824 : nsMargin& aFloatMargin,
5825 : bool aFloatPushedDown,
5826 : nsReflowStatus& aReflowStatus)
5827 : {
5828 0 : NS_PRECONDITION(aFloat->GetStateBits() & NS_FRAME_OUT_OF_FLOW,
5829 : "aFloat must be an out-of-flow frame");
5830 : // Reflow the float.
5831 0 : aReflowStatus = NS_FRAME_COMPLETE;
5832 :
5833 : #ifdef NOISY_FLOAT
5834 : printf("Reflow Float %p in parent %p, availSpace(%d,%d,%d,%d)\n",
5835 : aFloat, this,
5836 : aFloatAvailableSpace.x, aFloatAvailableSpace.y,
5837 : aFloatAvailableSpace.width, aFloatAvailableSpace.height
5838 : );
5839 : #endif
5840 :
5841 : nsHTMLReflowState floatRS(aState.mPresContext, aState.mReflowState, aFloat,
5842 : nsSize(aAdjustedAvailableSpace.width,
5843 0 : aAdjustedAvailableSpace.height));
5844 :
5845 : // Normally the mIsTopOfPage state is copied from the parent reflow
5846 : // state. However, when reflowing a float, if we've placed other
5847 : // floats that force this float *down* or *narrower*, we should unset
5848 : // the mIsTopOfPage state.
5849 : // FIXME: This is somewhat redundant with the |isAdjacentWithTop|
5850 : // variable below, which has the exact same effect. Perhaps it should
5851 : // be merged into that, except that the test for narrowing here is not
5852 : // about adjacency with the top, so it seems misleading.
5853 0 : if (floatRS.mFlags.mIsTopOfPage &&
5854 : (aFloatPushedDown ||
5855 : aAdjustedAvailableSpace.width != aState.mContentArea.width)) {
5856 0 : floatRS.mFlags.mIsTopOfPage = false;
5857 : }
5858 :
5859 : // Setup a block reflow context to reflow the float.
5860 0 : nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState);
5861 :
5862 : // Reflow the float
5863 0 : bool isAdjacentWithTop = aState.IsAdjacentWithTop();
5864 :
5865 0 : nsIFrame* clearanceFrame = nsnull;
5866 : nsresult rv;
5867 0 : do {
5868 0 : nsCollapsingMargin margin;
5869 0 : bool mayNeedRetry = false;
5870 0 : floatRS.mDiscoveredClearance = nsnull;
5871 : // Only first in flow gets a top margin.
5872 0 : if (!aFloat->GetPrevInFlow()) {
5873 : nsBlockReflowContext::ComputeCollapsedTopMargin(floatRS, &margin,
5874 0 : clearanceFrame, &mayNeedRetry);
5875 :
5876 0 : if (mayNeedRetry && !clearanceFrame) {
5877 0 : floatRS.mDiscoveredClearance = &clearanceFrame;
5878 : // We don't need to push the float manager state because the the block has its own
5879 : // float manager that will be destroyed and recreated
5880 : }
5881 : }
5882 :
5883 : rv = brc.ReflowBlock(aAdjustedAvailableSpace, true, margin,
5884 : 0, isAdjacentWithTop,
5885 : nsnull, floatRS,
5886 0 : aReflowStatus, aState);
5887 0 : } while (NS_SUCCEEDED(rv) && clearanceFrame);
5888 :
5889 : // An incomplete reflow status means we should split the float
5890 : // if the height is constrained (bug 145305).
5891 0 : if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus) &&
5892 : (NS_UNCONSTRAINEDSIZE == aAdjustedAvailableSpace.height))
5893 0 : aReflowStatus = NS_FRAME_COMPLETE;
5894 :
5895 0 : if (aReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) {
5896 0 : aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
5897 : }
5898 :
5899 0 : if (aFloat->GetType() == nsGkAtoms::letterFrame) {
5900 : // We never split floating first letters; an incomplete state for
5901 : // such frames simply means that there is more content to be
5902 : // reflowed on the line.
5903 0 : if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus))
5904 0 : aReflowStatus = NS_FRAME_COMPLETE;
5905 : }
5906 :
5907 0 : if (NS_FAILED(rv)) {
5908 0 : return rv;
5909 : }
5910 :
5911 : // Capture the margin information for the caller
5912 0 : aFloatMargin = floatRS.mComputedMargin; // float margins don't collapse
5913 :
5914 0 : const nsHTMLReflowMetrics& metrics = brc.GetMetrics();
5915 :
5916 : // Set the rect, make sure the view is properly sized and positioned,
5917 : // and tell the frame we're done reflowing it
5918 : // XXXldb This seems like the wrong place to be doing this -- shouldn't
5919 : // we be doing this in nsBlockReflowState::FlowAndPlaceFloat after
5920 : // we've positioned the float, and shouldn't we be doing the equivalent
5921 : // of |::PlaceFrameView| here?
5922 0 : aFloat->SetSize(nsSize(metrics.width, metrics.height));
5923 0 : if (aFloat->HasView()) {
5924 : nsContainerFrame::SyncFrameViewAfterReflow(aState.mPresContext, aFloat,
5925 : aFloat->GetView(),
5926 0 : metrics.VisualOverflow(),
5927 0 : NS_FRAME_NO_MOVE_VIEW);
5928 : }
5929 : // Pass floatRS so the frame hierarchy can be used (redoFloatRS has the same hierarchy)
5930 : aFloat->DidReflow(aState.mPresContext, &floatRS,
5931 0 : NS_FRAME_REFLOW_FINISHED);
5932 :
5933 : #ifdef NOISY_FLOAT
5934 : printf("end ReflowFloat %p, sized to %d,%d\n",
5935 : aFloat, metrics.width, metrics.height);
5936 : #endif
5937 :
5938 0 : return NS_OK;
5939 : }
5940 :
5941 : PRUint8
5942 0 : nsBlockFrame::FindTrailingClear()
5943 : {
5944 : // find the break type of the last line
5945 0 : for (nsIFrame* b = this; b; b = b->GetPrevInFlow()) {
5946 0 : nsBlockFrame* block = static_cast<nsBlockFrame*>(b);
5947 0 : line_iterator endLine = block->end_lines();
5948 0 : if (endLine != block->begin_lines()) {
5949 0 : --endLine;
5950 0 : return endLine->GetBreakTypeAfter();
5951 : }
5952 : }
5953 0 : return NS_STYLE_CLEAR_NONE;
5954 : }
5955 :
5956 : nsresult
5957 0 : nsBlockFrame::ReflowPushedFloats(nsBlockReflowState& aState,
5958 : nsOverflowAreas& aOverflowAreas,
5959 : nsReflowStatus& aStatus)
5960 : {
5961 0 : nsresult rv = NS_OK;
5962 0 : for (nsIFrame* f = mFloats.FirstChild(), *next;
5963 0 : f && (f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT);
5964 : f = next) {
5965 : // save next sibling now, since reflowing could push the entire
5966 : // float, changing its siblings
5967 0 : next = f->GetNextSibling();
5968 :
5969 : // When we push a first-continuation float in a non-initial reflow,
5970 : // it's possible that we end up with two continuations with the same
5971 : // parent. This happens if, on the previous reflow of the block or
5972 : // a previous reflow of the line containing the block, the float was
5973 : // split between continuations A and B of the parent, but on the
5974 : // current reflow, none of the float can fit in A.
5975 : //
5976 : // When this happens, we might even have the two continuations
5977 : // out-of-order due to the management of the pushed floats. In
5978 : // particular, if the float's placeholder was in a pushed line that
5979 : // we reflowed before it was pushed, and we split the float during
5980 : // that reflow, we might have the continuation of the float before
5981 : // the float itself. (In the general case, however, it's correct
5982 : // for floats in the pushed floats list to come before floats
5983 : // anchored in pushed lines; however, in this case it's wrong. We
5984 : // should probably find a way to fix it somehow, since it leads to
5985 : // incorrect layout in some cases.)
5986 : //
5987 : // When we have these out-of-order continuations, we might hit the
5988 : // next-continuation before the previous-continuation. When that
5989 : // happens, just push it. When we reflow the next continuation,
5990 : // we'll either pull all of its content back and destroy it (by
5991 : // calling DeleteNextInFlowChild), or nsBlockFrame::SplitFloat will
5992 : // pull it out of its current position and push it again (and
5993 : // potentially repeat this cycle for the next continuation, although
5994 : // hopefully then they'll be in the right order).
5995 : //
5996 : // We should also need this code for the in-order case if the first
5997 : // continuation of a float gets moved across more than one
5998 : // continuation of the containing block. In this case we'd manage
5999 : // to push the second continuation without this check, but not the
6000 : // third and later.
6001 0 : nsIFrame *prevContinuation = f->GetPrevContinuation();
6002 0 : if (prevContinuation && prevContinuation->GetParent() == f->GetParent()) {
6003 0 : mFloats.RemoveFrame(f);
6004 0 : aState.AppendPushedFloat(f);
6005 0 : continue;
6006 : }
6007 :
6008 0 : if (NS_SUBTREE_DIRTY(f) || aState.mReflowState.ShouldReflowAllKids()) {
6009 : // Cache old bounds
6010 0 : nsRect oldRect = f->GetRect();
6011 0 : nsRect oldOverflow = f->GetVisualOverflowRect();
6012 :
6013 : // Reflow
6014 0 : aState.FlowAndPlaceFloat(f);
6015 :
6016 : // Invalidate if there was a position or size change
6017 0 : nsRect rect = f->GetRect();
6018 0 : if (!rect.IsEqualInterior(oldRect)) {
6019 0 : nsRect dirtyRect = oldOverflow;
6020 0 : dirtyRect.MoveBy(oldRect.x, oldRect.y);
6021 0 : Invalidate(dirtyRect);
6022 :
6023 0 : dirtyRect = f->GetVisualOverflowRect();
6024 0 : dirtyRect.MoveBy(rect.x, rect.y);
6025 0 : Invalidate(dirtyRect);
6026 : }
6027 : }
6028 : else {
6029 : // Just reload the float region into the space manager
6030 0 : nsRect region = nsFloatManager::GetRegionFor(f);
6031 0 : aState.mFloatManager->AddFloat(f, region);
6032 0 : if (f->GetNextInFlow())
6033 0 : NS_MergeReflowStatusInto(&aStatus, NS_FRAME_OVERFLOW_INCOMPLETE);
6034 : }
6035 :
6036 0 : ConsiderChildOverflow(aOverflowAreas, f);
6037 : }
6038 :
6039 : // If there are continued floats, then we may need to continue BR clearance
6040 0 : if (0 != aState.ClearFloats(0, NS_STYLE_CLEAR_LEFT_AND_RIGHT)) {
6041 0 : aState.mFloatBreakType = static_cast<nsBlockFrame*>(GetPrevInFlow())
6042 0 : ->FindTrailingClear();
6043 : }
6044 :
6045 0 : return rv;
6046 : }
6047 :
6048 : void
6049 0 : nsBlockFrame::RecoverFloats(nsFloatManager& aFloatManager)
6050 : {
6051 : // Recover our own floats
6052 0 : nsIFrame* stop = nsnull; // Stop before we reach pushed floats that
6053 : // belong to our next-in-flow
6054 0 : for (nsIFrame* f = mFloats.FirstChild(); f && f != stop; f = f->GetNextSibling()) {
6055 0 : nsRect region = nsFloatManager::GetRegionFor(f);
6056 0 : aFloatManager.AddFloat(f, region);
6057 0 : if (!stop && f->GetNextInFlow())
6058 0 : stop = f->GetNextInFlow();
6059 : }
6060 :
6061 : // Recurse into our overflow container children
6062 0 : for (nsIFrame* oc = GetFirstChild(kOverflowContainersList);
6063 : oc; oc = oc->GetNextSibling()) {
6064 0 : RecoverFloatsFor(oc, aFloatManager);
6065 : }
6066 :
6067 : // Recurse into our normal children
6068 0 : for (nsBlockFrame::line_iterator line = begin_lines(); line != end_lines(); ++line) {
6069 0 : if (line->IsBlock()) {
6070 0 : RecoverFloatsFor(line->mFirstChild, aFloatManager);
6071 : }
6072 : }
6073 0 : }
6074 :
6075 : void
6076 0 : nsBlockFrame::RecoverFloatsFor(nsIFrame* aFrame,
6077 : nsFloatManager& aFloatManager)
6078 : {
6079 0 : NS_PRECONDITION(aFrame, "null frame");
6080 : // Only blocks have floats
6081 0 : nsBlockFrame* block = nsLayoutUtils::GetAsBlock(aFrame);
6082 : // Don't recover any state inside a block that has its own space manager
6083 : // (we don't currently have any blocks like this, though, thanks to our
6084 : // use of extra frames for 'overflow')
6085 0 : if (block && !nsBlockFrame::BlockNeedsFloatManager(block)) {
6086 : // If the element is relatively positioned, then adjust x and y
6087 : // accordingly so that we consider relatively positioned frames
6088 : // at their original position.
6089 0 : nsPoint pos = block->GetPosition() - block->GetRelativeOffset();
6090 0 : aFloatManager.Translate(pos.x, pos.y);
6091 0 : block->RecoverFloats(aFloatManager);
6092 0 : aFloatManager.Translate(-pos.x, -pos.y);
6093 : }
6094 0 : }
6095 :
6096 : //////////////////////////////////////////////////////////////////////
6097 : // Painting, event handling
6098 :
6099 : PRIntn
6100 0 : nsBlockFrame::GetSkipSides() const
6101 : {
6102 0 : if (IS_TRUE_OVERFLOW_CONTAINER(this))
6103 0 : return (1 << NS_SIDE_TOP) | (1 << NS_SIDE_BOTTOM);
6104 :
6105 0 : PRIntn skip = 0;
6106 0 : if (GetPrevInFlow()) {
6107 0 : skip |= 1 << NS_SIDE_TOP;
6108 : }
6109 0 : nsIFrame* nif = GetNextInFlow();
6110 0 : if (nif && !IS_TRUE_OVERFLOW_CONTAINER(nif)) {
6111 0 : skip |= 1 << NS_SIDE_BOTTOM;
6112 : }
6113 0 : return skip;
6114 : }
6115 :
6116 : #ifdef DEBUG
6117 0 : static void ComputeVisualOverflowArea(nsLineList& aLines,
6118 : nscoord aWidth, nscoord aHeight,
6119 : nsRect& aResult)
6120 : {
6121 0 : nscoord xa = 0, ya = 0, xb = aWidth, yb = aHeight;
6122 0 : for (nsLineList::iterator line = aLines.begin(), line_end = aLines.end();
6123 : line != line_end;
6124 : ++line) {
6125 : // Compute min and max x/y values for the reflowed frame's
6126 : // combined areas
6127 0 : nsRect visOverflow(line->GetVisualOverflowArea());
6128 0 : nscoord x = visOverflow.x;
6129 0 : nscoord y = visOverflow.y;
6130 0 : nscoord xmost = x + visOverflow.width;
6131 0 : nscoord ymost = y + visOverflow.height;
6132 0 : if (x < xa) {
6133 0 : xa = x;
6134 : }
6135 0 : if (xmost > xb) {
6136 0 : xb = xmost;
6137 : }
6138 0 : if (y < ya) {
6139 0 : ya = y;
6140 : }
6141 0 : if (ymost > yb) {
6142 0 : yb = ymost;
6143 : }
6144 : }
6145 :
6146 0 : aResult.x = xa;
6147 0 : aResult.y = ya;
6148 0 : aResult.width = xb - xa;
6149 0 : aResult.height = yb - ya;
6150 0 : }
6151 : #endif
6152 :
6153 : bool
6154 0 : nsBlockFrame::IsVisibleInSelection(nsISelection* aSelection)
6155 : {
6156 0 : nsCOMPtr<nsIDOMHTMLHtmlElement> html(do_QueryInterface(mContent));
6157 0 : nsCOMPtr<nsIDOMHTMLBodyElement> body(do_QueryInterface(mContent));
6158 0 : if (html || body)
6159 0 : return true;
6160 :
6161 0 : nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mContent));
6162 : bool visible;
6163 0 : nsresult rv = aSelection->ContainsNode(node, true, &visible);
6164 0 : return NS_SUCCEEDED(rv) && visible;
6165 : }
6166 :
6167 : #ifdef DEBUG
6168 0 : static void DebugOutputDrawLine(PRInt32 aDepth, nsLineBox* aLine, bool aDrawn) {
6169 0 : if (nsBlockFrame::gNoisyDamageRepair) {
6170 0 : nsFrame::IndentBy(stdout, aDepth+1);
6171 0 : nsRect lineArea = aLine->GetVisualOverflowArea();
6172 : printf("%s line=%p bounds=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
6173 : aDrawn ? "draw" : "skip",
6174 : static_cast<void*>(aLine),
6175 : aLine->mBounds.x, aLine->mBounds.y,
6176 : aLine->mBounds.width, aLine->mBounds.height,
6177 : lineArea.x, lineArea.y,
6178 0 : lineArea.width, lineArea.height);
6179 : }
6180 0 : }
6181 : #endif
6182 :
6183 : static nsresult
6184 0 : DisplayLine(nsDisplayListBuilder* aBuilder, const nsRect& aLineArea,
6185 : const nsRect& aDirtyRect, nsBlockFrame::line_iterator& aLine,
6186 : PRInt32 aDepth, PRInt32& aDrawnLines, const nsDisplayListSet& aLists,
6187 : nsBlockFrame* aFrame, TextOverflow* aTextOverflow) {
6188 : // If the line's combined area (which includes child frames that
6189 : // stick outside of the line's bounding box or our bounding box)
6190 : // intersects the dirty rect then paint the line.
6191 0 : bool intersect = aLineArea.Intersects(aDirtyRect);
6192 : #ifdef DEBUG
6193 0 : if (nsBlockFrame::gLamePaintMetrics) {
6194 0 : aDrawnLines++;
6195 : }
6196 0 : DebugOutputDrawLine(aDepth, aLine.get(), intersect);
6197 : #endif
6198 : // The line might contain a placeholder for a visible out-of-flow, in which
6199 : // case we need to descend into it. If there is such a placeholder, we will
6200 : // have NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO set.
6201 : // In particular, we really want to check ShouldDescendIntoFrame()
6202 : // on all the frames on the line, but that might be expensive. So
6203 : // we approximate it by checking it on aFrame; if it's true for any
6204 : // frame in the line, it's also true for aFrame.
6205 0 : bool lineInline = aLine->IsInline();
6206 0 : bool lineMayHaveTextOverflow = aTextOverflow && lineInline;
6207 0 : if (!intersect && !aBuilder->ShouldDescendIntoFrame(aFrame) &&
6208 0 : !lineMayHaveTextOverflow)
6209 0 : return NS_OK;
6210 :
6211 0 : nsDisplayListCollection collection;
6212 : nsresult rv;
6213 :
6214 : // Block-level child backgrounds go on the blockBorderBackgrounds list ...
6215 : // Inline-level child backgrounds go on the regular child content list.
6216 : nsDisplayListSet childLists(collection,
6217 0 : lineInline ? collection.Content() : collection.BlockBorderBackgrounds());
6218 0 : nsIFrame* kid = aLine->mFirstChild;
6219 0 : PRInt32 n = aLine->GetChildCount();
6220 0 : while (--n >= 0) {
6221 : rv = aFrame->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, childLists,
6222 0 : lineInline ? nsIFrame::DISPLAY_CHILD_INLINE : 0);
6223 0 : NS_ENSURE_SUCCESS(rv, rv);
6224 0 : kid = kid->GetNextSibling();
6225 : }
6226 :
6227 0 : if (lineMayHaveTextOverflow) {
6228 0 : aTextOverflow->ProcessLine(collection, aLine.get());
6229 : }
6230 :
6231 0 : collection.MoveTo(aLists);
6232 0 : return NS_OK;
6233 : }
6234 :
6235 : NS_IMETHODIMP
6236 0 : nsBlockFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
6237 : const nsRect& aDirtyRect,
6238 : const nsDisplayListSet& aLists)
6239 : {
6240 : PRInt32 drawnLines; // Will only be used if set (gLamePaintMetrics).
6241 0 : PRInt32 depth = 0;
6242 : #ifdef DEBUG
6243 0 : if (gNoisyDamageRepair) {
6244 0 : depth = GetDepth();
6245 0 : nsRect ca;
6246 0 : ::ComputeVisualOverflowArea(mLines, mRect.width, mRect.height, ca);
6247 0 : nsFrame::IndentBy(stdout, depth);
6248 0 : ListTag(stdout);
6249 : printf(": bounds=%d,%d,%d,%d dirty(absolute)=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
6250 : mRect.x, mRect.y, mRect.width, mRect.height,
6251 : aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height,
6252 0 : ca.x, ca.y, ca.width, ca.height);
6253 : }
6254 0 : PRTime start = LL_ZERO; // Initialize these variables to silence the compiler.
6255 0 : if (gLamePaintMetrics) {
6256 0 : start = PR_Now();
6257 0 : drawnLines = 0;
6258 : }
6259 : #endif
6260 :
6261 0 : DisplayBorderBackgroundOutline(aBuilder, aLists);
6262 :
6263 0 : if (GetPrevInFlow()) {
6264 0 : DisplayOverflowContainers(aBuilder, aDirtyRect, aLists);
6265 0 : for (nsIFrame* f = mFloats.FirstChild(); f; f = f->GetNextSibling()) {
6266 0 : if (f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT)
6267 0 : BuildDisplayListForChild(aBuilder, f, aDirtyRect, aLists);
6268 : }
6269 : }
6270 :
6271 0 : aBuilder->MarkFramesForDisplayList(this, mFloats, aDirtyRect);
6272 :
6273 : // Prepare for text-overflow processing.
6274 : nsAutoPtr<TextOverflow> textOverflow(
6275 0 : TextOverflow::WillProcessLines(aBuilder, aLists, this));
6276 :
6277 : // Don't use the line cursor if we might have a descendant placeholder ...
6278 : // it might skip lines that contain placeholders but don't themselves
6279 : // intersect with the dirty area.
6280 : // In particular, we really want to check ShouldDescendIntoFrame()
6281 : // on all our child frames, but that might be expensive. So we
6282 : // approximate it by checking it on |this|; if it's true for any
6283 : // frame in our child list, it's also true for |this|.
6284 0 : nsLineBox* cursor = aBuilder->ShouldDescendIntoFrame(this) ?
6285 0 : nsnull : GetFirstLineContaining(aDirtyRect.y);
6286 0 : line_iterator line_end = end_lines();
6287 0 : nsresult rv = NS_OK;
6288 :
6289 0 : if (cursor) {
6290 0 : for (line_iterator line = mLines.begin(cursor);
6291 : line != line_end;
6292 : ++line) {
6293 0 : nsRect lineArea = line->GetVisualOverflowArea();
6294 0 : if (!lineArea.IsEmpty()) {
6295 : // Because we have a cursor, the combinedArea.ys are non-decreasing.
6296 : // Once we've passed aDirtyRect.YMost(), we can never see it again.
6297 0 : if (lineArea.y >= aDirtyRect.YMost()) {
6298 : break;
6299 : }
6300 : rv = DisplayLine(aBuilder, lineArea, aDirtyRect, line, depth, drawnLines,
6301 0 : aLists, this, textOverflow);
6302 0 : if (NS_FAILED(rv))
6303 : break;
6304 : }
6305 : }
6306 : } else {
6307 0 : bool nonDecreasingYs = true;
6308 0 : PRInt32 lineCount = 0;
6309 0 : nscoord lastY = PR_INT32_MIN;
6310 0 : nscoord lastYMost = PR_INT32_MIN;
6311 0 : for (line_iterator line = begin_lines();
6312 : line != line_end;
6313 : ++line) {
6314 0 : nsRect lineArea = line->GetVisualOverflowArea();
6315 : rv = DisplayLine(aBuilder, lineArea, aDirtyRect, line, depth, drawnLines,
6316 0 : aLists, this, textOverflow);
6317 0 : if (NS_FAILED(rv))
6318 : break;
6319 0 : if (!lineArea.IsEmpty()) {
6320 0 : if (lineArea.y < lastY
6321 0 : || lineArea.YMost() < lastYMost) {
6322 0 : nonDecreasingYs = false;
6323 : }
6324 0 : lastY = lineArea.y;
6325 0 : lastYMost = lineArea.YMost();
6326 : }
6327 0 : lineCount++;
6328 : }
6329 :
6330 0 : if (NS_SUCCEEDED(rv) && nonDecreasingYs && lineCount >= MIN_LINES_NEEDING_CURSOR) {
6331 0 : SetupLineCursor();
6332 : }
6333 : }
6334 :
6335 0 : if (NS_SUCCEEDED(rv) && HasOutsideBullet()) {
6336 : // Display outside bullets manually
6337 0 : nsIFrame* bullet = GetOutsideBullet();
6338 0 : rv = BuildDisplayListForChild(aBuilder, bullet, aDirtyRect, aLists);
6339 : }
6340 :
6341 : #ifdef DEBUG
6342 0 : if (gLamePaintMetrics) {
6343 0 : PRTime end = PR_Now();
6344 :
6345 0 : PRInt32 numLines = mLines.size();
6346 0 : if (!numLines) numLines = 1;
6347 : PRTime lines, deltaPerLine, delta;
6348 0 : LL_I2L(lines, numLines);
6349 0 : LL_SUB(delta, end, start);
6350 0 : LL_DIV(deltaPerLine, delta, lines);
6351 :
6352 0 : ListTag(stdout);
6353 : char buf[400];
6354 : PR_snprintf(buf, sizeof(buf),
6355 : ": %lld elapsed (%lld per line) lines=%d drawn=%d skip=%d",
6356 : delta, deltaPerLine,
6357 0 : numLines, drawnLines, numLines - drawnLines);
6358 0 : printf("%s\n", buf);
6359 : }
6360 : #endif
6361 :
6362 0 : return rv;
6363 : }
6364 :
6365 : #ifdef ACCESSIBILITY
6366 : already_AddRefed<nsAccessible>
6367 0 : nsBlockFrame::CreateAccessible()
6368 : {
6369 0 : nsAccessibilityService* accService = nsIPresShell::AccService();
6370 0 : if (!accService) {
6371 0 : return nsnull;
6372 : }
6373 :
6374 0 : nsPresContext* presContext = PresContext();
6375 :
6376 : // block frame may be for <hr>
6377 0 : if (mContent->Tag() == nsGkAtoms::hr) {
6378 : return accService->CreateHTMLHRAccessible(mContent,
6379 0 : presContext->PresShell());
6380 : }
6381 :
6382 0 : if (!HasBullet() || !presContext) {
6383 0 : if (!mContent->GetParent()) {
6384 : // Don't create accessible objects for the root content node, they are redundant with
6385 : // the nsDocAccessible object created with the document node
6386 0 : return nsnull;
6387 : }
6388 :
6389 : nsCOMPtr<nsIDOMHTMLDocument> htmlDoc =
6390 0 : do_QueryInterface(mContent->GetDocument());
6391 0 : if (htmlDoc) {
6392 0 : nsCOMPtr<nsIDOMHTMLElement> body;
6393 0 : htmlDoc->GetBody(getter_AddRefs(body));
6394 0 : if (SameCOMIdentity(body, mContent)) {
6395 : // Don't create accessible objects for the body, they are redundant with
6396 : // the nsDocAccessible object created with the document node
6397 0 : return nsnull;
6398 : }
6399 : }
6400 :
6401 : // Not a bullet, treat as normal HTML container
6402 : return accService->CreateHyperTextAccessible(mContent,
6403 0 : presContext->PresShell());
6404 : }
6405 :
6406 : // Create special list bullet accessible
6407 0 : return accService->CreateHTMLLIAccessible(mContent, presContext->PresShell());
6408 : }
6409 : #endif
6410 :
6411 0 : void nsBlockFrame::ClearLineCursor()
6412 : {
6413 0 : if (!(GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR)) {
6414 0 : return;
6415 : }
6416 :
6417 0 : Properties().Delete(LineCursorProperty());
6418 0 : RemoveStateBits(NS_BLOCK_HAS_LINE_CURSOR);
6419 : }
6420 :
6421 0 : void nsBlockFrame::SetupLineCursor()
6422 : {
6423 0 : if (GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR
6424 0 : || mLines.empty()) {
6425 0 : return;
6426 : }
6427 :
6428 0 : Properties().Set(LineCursorProperty(), mLines.front());
6429 0 : AddStateBits(NS_BLOCK_HAS_LINE_CURSOR);
6430 : }
6431 :
6432 0 : nsLineBox* nsBlockFrame::GetFirstLineContaining(nscoord y)
6433 : {
6434 0 : if (!(GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR)) {
6435 0 : return nsnull;
6436 : }
6437 :
6438 0 : FrameProperties props = Properties();
6439 :
6440 : nsLineBox* property = static_cast<nsLineBox*>
6441 0 : (props.Get(LineCursorProperty()));
6442 0 : line_iterator cursor = mLines.begin(property);
6443 0 : nsRect cursorArea = cursor->GetVisualOverflowArea();
6444 :
6445 0 : while ((cursorArea.IsEmpty() || cursorArea.YMost() > y)
6446 0 : && cursor != mLines.front()) {
6447 0 : cursor = cursor.prev();
6448 0 : cursorArea = cursor->GetVisualOverflowArea();
6449 : }
6450 0 : while ((cursorArea.IsEmpty() || cursorArea.YMost() <= y)
6451 0 : && cursor != mLines.back()) {
6452 0 : cursor = cursor.next();
6453 0 : cursorArea = cursor->GetVisualOverflowArea();
6454 : }
6455 :
6456 0 : if (cursor.get() != property) {
6457 0 : props.Set(LineCursorProperty(), cursor.get());
6458 : }
6459 :
6460 0 : return cursor.get();
6461 : }
6462 :
6463 : /* virtual */ void
6464 0 : nsBlockFrame::ChildIsDirty(nsIFrame* aChild)
6465 : {
6466 : // See if the child is absolutely positioned
6467 0 : if (aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW &&
6468 0 : aChild->GetStyleDisplay()->IsAbsolutelyPositioned()) {
6469 : // do nothing
6470 0 : } else if (aChild == GetOutsideBullet()) {
6471 : // The bullet lives in the first line, unless the first line has
6472 : // height 0 and there is a second line, in which case it lives
6473 : // in the second line.
6474 0 : line_iterator bulletLine = begin_lines();
6475 0 : if (bulletLine != end_lines() && bulletLine->mBounds.height == 0 &&
6476 0 : bulletLine != mLines.back()) {
6477 0 : bulletLine = bulletLine.next();
6478 : }
6479 :
6480 0 : if (bulletLine != end_lines()) {
6481 0 : MarkLineDirty(bulletLine);
6482 : }
6483 : // otherwise we have an empty line list, and ReflowDirtyLines
6484 : // will handle reflowing the bullet.
6485 : } else {
6486 : // Mark the line containing the child frame dirty. We would rather do this
6487 : // in MarkIntrinsicWidthsDirty but that currently won't tell us which
6488 : // child is being dirtied.
6489 : bool isValid;
6490 0 : nsBlockInFlowLineIterator iter(this, aChild, &isValid);
6491 0 : if (isValid) {
6492 0 : iter.GetContainer()->MarkLineDirty(iter.GetLine(), iter.GetLineList());
6493 : }
6494 : }
6495 :
6496 0 : nsBlockFrameSuper::ChildIsDirty(aChild);
6497 0 : }
6498 :
6499 : NS_IMETHODIMP
6500 0 : nsBlockFrame::Init(nsIContent* aContent,
6501 : nsIFrame* aParent,
6502 : nsIFrame* aPrevInFlow)
6503 : {
6504 0 : if (aPrevInFlow) {
6505 : // Copy over the inherited block frame bits from the prev-in-flow.
6506 0 : SetFlags(aPrevInFlow->GetStateBits() &
6507 0 : (NS_BLOCK_FLAGS_MASK & ~NS_BLOCK_FLAGS_NON_INHERITED_MASK));
6508 : }
6509 :
6510 0 : nsresult rv = nsBlockFrameSuper::Init(aContent, aParent, aPrevInFlow);
6511 :
6512 0 : if (!aPrevInFlow ||
6513 0 : aPrevInFlow->GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION)
6514 0 : AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION);
6515 :
6516 0 : return rv;
6517 : }
6518 :
6519 : NS_IMETHODIMP
6520 0 : nsBlockFrame::SetInitialChildList(ChildListID aListID,
6521 : nsFrameList& aChildList)
6522 : {
6523 0 : NS_ASSERTION(aListID != kPrincipalList ||
6524 : (GetStateBits() & (NS_BLOCK_FRAME_HAS_INSIDE_BULLET |
6525 : NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET)) == 0,
6526 : "how can we have a bullet already?");
6527 :
6528 0 : nsresult rv = NS_OK;
6529 :
6530 0 : if (kAbsoluteList == aListID) {
6531 0 : nsContainerFrame::SetInitialChildList(aListID, aChildList);
6532 : }
6533 0 : else if (kFloatList == aListID) {
6534 0 : mFloats.SetFrames(aChildList);
6535 : }
6536 : else {
6537 0 : nsPresContext* presContext = PresContext();
6538 :
6539 : #ifdef DEBUG
6540 : // The only times a block that is an anonymous box is allowed to have a
6541 : // first-letter frame are when it's the block inside a non-anonymous cell,
6542 : // the block inside a fieldset, a scrolled content block, or a column
6543 : // content block. Note that this means that blocks which are the anonymous
6544 : // block in {ib} splits do NOT get first-letter frames. Note that
6545 : // NS_BLOCK_HAS_FIRST_LETTER_STYLE gets set on all continuations of the
6546 : // block.
6547 0 : nsIAtom *pseudo = GetStyleContext()->GetPseudo();
6548 : bool haveFirstLetterStyle =
6549 : (!pseudo ||
6550 : (pseudo == nsCSSAnonBoxes::cellContent &&
6551 0 : mParent->GetStyleContext()->GetPseudo() == nsnull) ||
6552 : pseudo == nsCSSAnonBoxes::fieldsetContent ||
6553 : pseudo == nsCSSAnonBoxes::scrolledContent ||
6554 : pseudo == nsCSSAnonBoxes::columnContent) &&
6555 0 : !IsFrameOfType(eMathML) &&
6556 0 : nsRefPtr<nsStyleContext>(GetFirstLetterStyle(presContext)) != nsnull;
6557 0 : NS_ASSERTION(haveFirstLetterStyle ==
6558 : ((mState & NS_BLOCK_HAS_FIRST_LETTER_STYLE) != 0),
6559 : "NS_BLOCK_HAS_FIRST_LETTER_STYLE state out of sync");
6560 : #endif
6561 :
6562 0 : rv = AddFrames(aChildList, nsnull);
6563 0 : if (NS_FAILED(rv)) {
6564 0 : return rv;
6565 : }
6566 :
6567 : // Create a list bullet if this is a list-item. Note that this is
6568 : // done here so that RenumberLists will work (it needs the bullets
6569 : // to store the bullet numbers). Also note that due to various
6570 : // wrapper frames (scrollframes, columns) we want to use the
6571 : // outermost (primary, ideally, but it's not set yet when we get
6572 : // here) frame of our content for the display check. On the other
6573 : // hand, we look at ourselves for the GetPrevInFlow() check, since
6574 : // for a columnset we don't want a bullet per column. Note that
6575 : // the outermost frame for the content is the primary frame in
6576 : // most cases; the ones when it's not (like tables) can't be
6577 : // NS_STYLE_DISPLAY_LIST_ITEM).
6578 0 : nsIFrame* possibleListItem = this;
6579 0 : while (1) {
6580 0 : nsIFrame* parent = possibleListItem->GetParent();
6581 0 : if (parent->GetContent() != GetContent()) {
6582 : break;
6583 : }
6584 0 : possibleListItem = parent;
6585 : }
6586 0 : if (NS_STYLE_DISPLAY_LIST_ITEM ==
6587 0 : possibleListItem->GetStyleDisplay()->mDisplay &&
6588 0 : !GetPrevInFlow()) {
6589 : // Resolve style for the bullet frame
6590 0 : const nsStyleList* styleList = GetStyleList();
6591 : nsCSSPseudoElements::Type pseudoType;
6592 0 : switch (styleList->mListStyleType) {
6593 : case NS_STYLE_LIST_STYLE_DISC:
6594 : case NS_STYLE_LIST_STYLE_CIRCLE:
6595 : case NS_STYLE_LIST_STYLE_SQUARE:
6596 0 : pseudoType = nsCSSPseudoElements::ePseudo_mozListBullet;
6597 0 : break;
6598 : default:
6599 0 : pseudoType = nsCSSPseudoElements::ePseudo_mozListNumber;
6600 0 : break;
6601 : }
6602 :
6603 0 : nsIPresShell *shell = presContext->PresShell();
6604 :
6605 : nsStyleContext* parentStyle =
6606 : CorrectStyleParentFrame(this,
6607 0 : nsCSSPseudoElements::GetPseudoAtom(pseudoType))->GetStyleContext();
6608 : nsRefPtr<nsStyleContext> kidSC = shell->StyleSet()->
6609 : ResolvePseudoElementStyle(mContent->AsElement(), pseudoType,
6610 0 : parentStyle);
6611 :
6612 : // Create bullet frame
6613 0 : nsBulletFrame* bullet = new (shell) nsBulletFrame(kidSC);
6614 0 : if (!bullet) {
6615 0 : return NS_ERROR_OUT_OF_MEMORY;
6616 : }
6617 0 : bullet->Init(mContent, this, nsnull);
6618 :
6619 : // If the list bullet frame should be positioned inside then add
6620 : // it to the flow now.
6621 0 : if (NS_STYLE_LIST_STYLE_POSITION_INSIDE ==
6622 : styleList->mListStylePosition) {
6623 0 : nsFrameList bulletList(bullet, bullet);
6624 0 : AddFrames(bulletList, nsnull);
6625 0 : Properties().Set(InsideBulletProperty(), bullet);
6626 0 : AddStateBits(NS_BLOCK_FRAME_HAS_INSIDE_BULLET);
6627 : } else {
6628 0 : nsFrameList* bulletList = new nsFrameList(bullet, bullet);
6629 0 : Properties().Set(OutsideBulletProperty(), bulletList);
6630 0 : AddStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET);
6631 : }
6632 : }
6633 : }
6634 :
6635 0 : return NS_OK;
6636 : }
6637 :
6638 : bool
6639 0 : nsBlockFrame::BulletIsEmpty() const
6640 : {
6641 0 : NS_ASSERTION(mContent->GetPrimaryFrame()->GetStyleDisplay()->mDisplay ==
6642 : NS_STYLE_DISPLAY_LIST_ITEM && HasOutsideBullet(),
6643 : "should only care when we have an outside bullet");
6644 0 : const nsStyleList* list = GetStyleList();
6645 : return list->mListStyleType == NS_STYLE_LIST_STYLE_NONE &&
6646 0 : !list->GetListStyleImage();
6647 : }
6648 :
6649 : void
6650 0 : nsBlockFrame::GetBulletText(nsAString& aText) const
6651 : {
6652 0 : aText.Truncate();
6653 :
6654 0 : const nsStyleList* myList = GetStyleList();
6655 0 : if (myList->GetListStyleImage() ||
6656 : myList->mListStyleType == NS_STYLE_LIST_STYLE_DISC) {
6657 0 : aText.Assign(kDiscCharacter);
6658 : }
6659 0 : else if (myList->mListStyleType == NS_STYLE_LIST_STYLE_CIRCLE) {
6660 0 : aText.Assign(kCircleCharacter);
6661 : }
6662 0 : else if (myList->mListStyleType == NS_STYLE_LIST_STYLE_SQUARE) {
6663 0 : aText.Assign(kSquareCharacter);
6664 : }
6665 0 : else if (myList->mListStyleType != NS_STYLE_LIST_STYLE_NONE) {
6666 0 : nsBulletFrame* bullet = GetBullet();
6667 0 : if (bullet) {
6668 0 : nsAutoString text;
6669 0 : bullet->GetListItemText(*myList, text);
6670 0 : aText = text;
6671 : }
6672 : }
6673 0 : }
6674 :
6675 : // static
6676 : bool
6677 0 : nsBlockFrame::FrameStartsCounterScope(nsIFrame* aFrame)
6678 : {
6679 0 : nsIContent* content = aFrame->GetContent();
6680 0 : if (!content || !content->IsHTML())
6681 0 : return false;
6682 :
6683 0 : nsIAtom *localName = content->NodeInfo()->NameAtom();
6684 : return localName == nsGkAtoms::ol ||
6685 : localName == nsGkAtoms::ul ||
6686 : localName == nsGkAtoms::dir ||
6687 0 : localName == nsGkAtoms::menu;
6688 : }
6689 :
6690 : bool
6691 0 : nsBlockFrame::RenumberLists(nsPresContext* aPresContext)
6692 : {
6693 0 : if (!FrameStartsCounterScope(this)) {
6694 : // If this frame doesn't start a counter scope then we don't need
6695 : // to renumber child list items.
6696 0 : return false;
6697 : }
6698 :
6699 : // Setup initial list ordinal value
6700 : // XXX Map html's start property to counter-reset style
6701 0 : PRInt32 ordinal = 1;
6702 :
6703 0 : nsGenericHTMLElement *hc = nsGenericHTMLElement::FromContent(mContent);
6704 :
6705 0 : if (hc) {
6706 0 : const nsAttrValue* attr = hc->GetParsedAttr(nsGkAtoms::start);
6707 0 : if (attr && attr->Type() == nsAttrValue::eInteger) {
6708 0 : ordinal = attr->GetIntegerValue();
6709 : }
6710 : }
6711 :
6712 : // Get to first-in-flow
6713 0 : nsBlockFrame* block = (nsBlockFrame*) GetFirstInFlow();
6714 0 : return RenumberListsInBlock(aPresContext, block, &ordinal, 0);
6715 : }
6716 :
6717 : bool
6718 0 : nsBlockFrame::RenumberListsInBlock(nsPresContext* aPresContext,
6719 : nsBlockFrame* aBlockFrame,
6720 : PRInt32* aOrdinal,
6721 : PRInt32 aDepth)
6722 : {
6723 : // Examine each line in the block
6724 : bool foundValidLine;
6725 0 : nsBlockInFlowLineIterator bifLineIter(aBlockFrame, &foundValidLine);
6726 :
6727 0 : if (!foundValidLine)
6728 0 : return false;
6729 :
6730 0 : bool renumberedABullet = false;
6731 :
6732 0 : do {
6733 0 : nsLineList::iterator line = bifLineIter.GetLine();
6734 0 : nsIFrame* kid = line->mFirstChild;
6735 0 : PRInt32 n = line->GetChildCount();
6736 0 : while (--n >= 0) {
6737 0 : bool kidRenumberedABullet = RenumberListsFor(aPresContext, kid, aOrdinal, aDepth);
6738 0 : if (kidRenumberedABullet) {
6739 0 : line->MarkDirty();
6740 0 : renumberedABullet = true;
6741 : }
6742 0 : kid = kid->GetNextSibling();
6743 : }
6744 : } while (bifLineIter.Next());
6745 :
6746 0 : return renumberedABullet;
6747 : }
6748 :
6749 : bool
6750 0 : nsBlockFrame::RenumberListsFor(nsPresContext* aPresContext,
6751 : nsIFrame* aKid,
6752 : PRInt32* aOrdinal,
6753 : PRInt32 aDepth)
6754 : {
6755 0 : NS_PRECONDITION(aPresContext && aKid && aOrdinal, "null params are immoral!");
6756 :
6757 : // add in a sanity check for absurdly deep frame trees. See bug 42138
6758 0 : if (MAX_DEPTH_FOR_LIST_RENUMBERING < aDepth)
6759 0 : return false;
6760 :
6761 : // if the frame is a placeholder, then get the out of flow frame
6762 0 : nsIFrame* kid = nsPlaceholderFrame::GetRealFrameFor(aKid);
6763 0 : const nsStyleDisplay* display = kid->GetStyleDisplay();
6764 :
6765 : // drill down through any wrappers to the real frame
6766 0 : kid = kid->GetContentInsertionFrame();
6767 :
6768 : // possible there is no content insertion frame
6769 0 : if (!kid)
6770 0 : return false;
6771 :
6772 0 : bool kidRenumberedABullet = false;
6773 :
6774 : // If the frame is a list-item and the frame implements our
6775 : // block frame API then get its bullet and set the list item
6776 : // ordinal.
6777 0 : if (NS_STYLE_DISPLAY_LIST_ITEM == display->mDisplay) {
6778 : // Make certain that the frame is a block frame in case
6779 : // something foreign has crept in.
6780 0 : nsBlockFrame* listItem = nsLayoutUtils::GetAsBlock(kid);
6781 0 : if (listItem) {
6782 0 : nsBulletFrame* bullet = listItem->GetBullet();
6783 0 : if (bullet) {
6784 : bool changed;
6785 0 : *aOrdinal = bullet->SetListItemOrdinal(*aOrdinal, &changed);
6786 0 : if (changed) {
6787 0 : kidRenumberedABullet = true;
6788 :
6789 : // The ordinal changed - mark the bullet frame dirty.
6790 0 : listItem->ChildIsDirty(bullet);
6791 : }
6792 : }
6793 :
6794 : // XXX temporary? if the list-item has child list-items they
6795 : // should be numbered too; especially since the list-item is
6796 : // itself (ASSUMED!) not to be a counter-resetter.
6797 0 : bool meToo = RenumberListsInBlock(aPresContext, listItem, aOrdinal, aDepth + 1);
6798 0 : if (meToo) {
6799 0 : kidRenumberedABullet = true;
6800 : }
6801 : }
6802 : }
6803 0 : else if (NS_STYLE_DISPLAY_BLOCK == display->mDisplay) {
6804 0 : if (FrameStartsCounterScope(kid)) {
6805 : // Don't bother recursing into a block frame that is a new
6806 : // counter scope. Any list-items in there will be handled by
6807 : // it.
6808 : }
6809 : else {
6810 : // If the display=block element is a block frame then go ahead
6811 : // and recurse into it, as it might have child list-items.
6812 0 : nsBlockFrame* kidBlock = nsLayoutUtils::GetAsBlock(kid);
6813 0 : if (kidBlock) {
6814 0 : kidRenumberedABullet = RenumberListsInBlock(aPresContext, kidBlock, aOrdinal, aDepth + 1);
6815 : }
6816 : }
6817 : }
6818 0 : return kidRenumberedABullet;
6819 : }
6820 :
6821 : void
6822 0 : nsBlockFrame::ReflowBullet(nsIFrame* aBulletFrame,
6823 : nsBlockReflowState& aState,
6824 : nsHTMLReflowMetrics& aMetrics,
6825 : nscoord aLineTop)
6826 : {
6827 0 : const nsHTMLReflowState &rs = aState.mReflowState;
6828 :
6829 : // Reflow the bullet now
6830 0 : nsSize availSize;
6831 : // Make up a width since it doesn't really matter (XXX).
6832 0 : availSize.width = aState.mContentArea.width;
6833 0 : availSize.height = NS_UNCONSTRAINEDSIZE;
6834 :
6835 : // Get the reason right.
6836 : // XXXwaterson Should this look just like the logic in
6837 : // nsBlockReflowContext::ReflowBlock and nsLineLayout::ReflowFrame?
6838 : nsHTMLReflowState reflowState(aState.mPresContext, rs,
6839 0 : aBulletFrame, availSize);
6840 : nsReflowStatus status;
6841 0 : aBulletFrame->WillReflow(aState.mPresContext);
6842 0 : aBulletFrame->Reflow(aState.mPresContext, aMetrics, reflowState, status);
6843 :
6844 : // Get the float available space using our saved state from before we
6845 : // started reflowing the block, so that we ignore any floats inside
6846 : // the block.
6847 : // FIXME: aLineTop isn't actually set correctly by some callers, since
6848 : // they reposition the line.
6849 : nsRect floatAvailSpace =
6850 : aState.GetFloatAvailableSpaceWithState(aLineTop,
6851 0 : &aState.mFloatManagerStateBefore)
6852 0 : .mRect;
6853 : // FIXME (bug 25888): need to check the entire region that the first
6854 : // line overlaps, not just the top pixel.
6855 :
6856 : // Place the bullet now. We want to place the bullet relative to the
6857 : // border-box of the associated block (using the right/left margin of
6858 : // the bullet frame as separation). However, if a line box would be
6859 : // displaced by floats that are *outside* the associated block, we
6860 : // want to displace it by the same amount. That is, we act as though
6861 : // the edge of the floats is the content-edge of the block, and place
6862 : // the bullet at a position offset from there by the block's padding,
6863 : // the block's border, and the bullet frame's margin.
6864 : nscoord x;
6865 0 : if (rs.mStyleVisibility->mDirection == NS_STYLE_DIRECTION_LTR) {
6866 : // The floatAvailSpace.x gives us the content/float edge. Then we
6867 : // subtract out the left border/padding and the bullet's width and
6868 : // margin to offset the position.
6869 : x = floatAvailSpace.x - rs.mComputedBorderPadding.left
6870 0 : - reflowState.mComputedMargin.right - aMetrics.width;
6871 : } else {
6872 : // The XMost() of the available space give us offsets from the left
6873 : // border edge. Then we add the right border/padding and the
6874 : // bullet's margin to offset the position.
6875 0 : x = floatAvailSpace.XMost() + rs.mComputedBorderPadding.right
6876 0 : + reflowState.mComputedMargin.left;
6877 : }
6878 :
6879 : // Approximate the bullets position; vertical alignment will provide
6880 : // the final vertical location.
6881 0 : nscoord y = aState.mContentArea.y;
6882 0 : aBulletFrame->SetRect(nsRect(x, y, aMetrics.width, aMetrics.height));
6883 : aBulletFrame->DidReflow(aState.mPresContext, &aState.mReflowState,
6884 0 : NS_FRAME_REFLOW_FINISHED);
6885 0 : }
6886 :
6887 : // This is used to scan frames for any float placeholders, add their
6888 : // floats to the list represented by aList, and remove the
6889 : // floats from whatever list they might be in. We don't search descendants
6890 : // that are float containing blocks. The floats must be children of 'this'.
6891 0 : void nsBlockFrame::CollectFloats(nsIFrame* aFrame, nsFrameList& aList,
6892 : bool aFromOverflow, bool aCollectSiblings) {
6893 0 : while (aFrame) {
6894 : // Don't descend into float containing blocks.
6895 0 : if (!aFrame->IsFloatContainingBlock()) {
6896 : nsIFrame *outOfFlowFrame =
6897 0 : aFrame->GetType() == nsGkAtoms::placeholderFrame ?
6898 0 : nsLayoutUtils::GetFloatFromPlaceholder(aFrame) : nsnull;
6899 0 : if (outOfFlowFrame) {
6900 0 : if (outOfFlowFrame->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT) {
6901 0 : if (outOfFlowFrame->GetParent() == this) {
6902 0 : nsFrameList* list = GetPushedFloats();
6903 0 : if (!list || !list->RemoveFrameIfPresent(outOfFlowFrame)) {
6904 0 : if (aFromOverflow) {
6905 0 : nsAutoOOFFrameList oofs(this);
6906 0 : oofs.mList.RemoveFrame(outOfFlowFrame);
6907 : } else {
6908 0 : mFloats.RemoveFrame(outOfFlowFrame);
6909 : }
6910 : }
6911 0 : aList.AppendFrame(nsnull, outOfFlowFrame);
6912 : }
6913 : // FIXME: By not pulling floats whose parent is one of our
6914 : // later siblings, are we risking the pushed floats getting
6915 : // out-of-order?
6916 : } else {
6917 : // Make sure that its parent is us. Otherwise we don't want
6918 : // to mess around with it because it belongs to someone
6919 : // else. I think this could happen if the overflow lines
6920 : // contain a block descendant which owns its own floats.
6921 0 : NS_ASSERTION(outOfFlowFrame->GetParent() == this,
6922 : "Out of flow frame doesn't have the expected parent");
6923 0 : if (aFromOverflow) {
6924 0 : nsAutoOOFFrameList oofs(this);
6925 0 : oofs.mList.RemoveFrame(outOfFlowFrame);
6926 : } else {
6927 0 : mFloats.RemoveFrame(outOfFlowFrame);
6928 : }
6929 0 : aList.AppendFrame(nsnull, outOfFlowFrame);
6930 : }
6931 : }
6932 :
6933 : CollectFloats(aFrame->GetFirstPrincipalChild(),
6934 0 : aList, aFromOverflow, true);
6935 : // Note: Even though we're calling CollectFloats on aFrame's overflow
6936 : // list, we'll pass down aFromOverflow unchanged because we're still
6937 : // traversing the regular-children subtree of the 'this' frame.
6938 : CollectFloats(aFrame->GetFirstChild(kOverflowList),
6939 0 : aList, aFromOverflow, true);
6940 : }
6941 0 : if (!aCollectSiblings)
6942 0 : break;
6943 0 : aFrame = aFrame->GetNextSibling();
6944 : }
6945 0 : }
6946 :
6947 : void
6948 0 : nsBlockFrame::CheckFloats(nsBlockReflowState& aState)
6949 : {
6950 : #ifdef DEBUG
6951 : // If any line is still dirty, that must mean we're going to reflow this
6952 : // block again soon (e.g. because we bailed out after noticing that
6953 : // clearance was imposed), so don't worry if the floats are out of sync.
6954 0 : bool anyLineDirty = false;
6955 :
6956 : // Check that the float list is what we would have built
6957 0 : nsAutoTArray<nsIFrame*, 8> lineFloats;
6958 0 : for (line_iterator line = begin_lines(), line_end = end_lines();
6959 : line != line_end; ++line) {
6960 0 : if (line->HasFloats()) {
6961 0 : nsFloatCache* fc = line->GetFirstFloat();
6962 0 : while (fc) {
6963 0 : lineFloats.AppendElement(fc->mFloat);
6964 0 : fc = fc->Next();
6965 : }
6966 : }
6967 0 : if (line->IsDirty()) {
6968 0 : anyLineDirty = true;
6969 : }
6970 : }
6971 :
6972 0 : nsAutoTArray<nsIFrame*, 8> storedFloats;
6973 0 : bool equal = true;
6974 0 : PRUint32 i = 0;
6975 0 : for (nsIFrame* f = mFloats.FirstChild(); f; f = f->GetNextSibling()) {
6976 0 : if (f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT)
6977 0 : continue;
6978 0 : storedFloats.AppendElement(f);
6979 0 : if (i < lineFloats.Length() && lineFloats.ElementAt(i) != f) {
6980 0 : equal = false;
6981 : }
6982 0 : ++i;
6983 : }
6984 :
6985 0 : if ((!equal || lineFloats.Length() != storedFloats.Length()) && !anyLineDirty) {
6986 0 : NS_WARNING("nsBlockFrame::CheckFloats: Explicit float list is out of sync with float cache");
6987 : #if defined(DEBUG_roc)
6988 : nsFrame::RootFrameList(PresContext(), stdout, 0);
6989 : for (i = 0; i < lineFloats.Length(); ++i) {
6990 : printf("Line float: %p\n", lineFloats.ElementAt(i));
6991 : }
6992 : for (i = 0; i < storedFloats.Length(); ++i) {
6993 : printf("Stored float: %p\n", storedFloats.ElementAt(i));
6994 : }
6995 : #endif
6996 : }
6997 : #endif
6998 :
6999 0 : const nsFrameList* oofs = GetOverflowOutOfFlows();
7000 0 : if (oofs && oofs->NotEmpty()) {
7001 : // Floats that were pushed should be removed from our float
7002 : // manager. Otherwise the float manager's YMost or XMost might
7003 : // be larger than necessary, causing this block to get an
7004 : // incorrect desired height (or width). Some of these floats
7005 : // may not actually have been added to the float manager because
7006 : // they weren't reflowed before being pushed; that's OK,
7007 : // RemoveRegions will ignore them. It is safe to do this here
7008 : // because we know from here on the float manager will only be
7009 : // used for its XMost and YMost, not to place new floats and
7010 : // lines.
7011 0 : aState.mFloatManager->RemoveTrailingRegions(oofs->FirstChild());
7012 : }
7013 0 : }
7014 :
7015 : /* static */
7016 : bool
7017 0 : nsBlockFrame::BlockIsMarginRoot(nsIFrame* aBlock)
7018 : {
7019 0 : NS_PRECONDITION(aBlock, "Must have a frame");
7020 0 : NS_ASSERTION(nsLayoutUtils::GetAsBlock(aBlock), "aBlock must be a block");
7021 :
7022 0 : nsIFrame* parent = aBlock->GetParent();
7023 0 : return (aBlock->GetStateBits() & NS_BLOCK_MARGIN_ROOT) ||
7024 0 : (parent && !parent->IsFloatContainingBlock() &&
7025 0 : parent->GetType() != nsGkAtoms::columnSetFrame);
7026 : }
7027 :
7028 : /* static */
7029 : bool
7030 0 : nsBlockFrame::BlockNeedsFloatManager(nsIFrame* aBlock)
7031 : {
7032 0 : NS_PRECONDITION(aBlock, "Must have a frame");
7033 0 : NS_ASSERTION(nsLayoutUtils::GetAsBlock(aBlock), "aBlock must be a block");
7034 :
7035 0 : nsIFrame* parent = aBlock->GetParent();
7036 0 : return (aBlock->GetStateBits() & NS_BLOCK_FLOAT_MGR) ||
7037 0 : (parent && !parent->IsFloatContainingBlock());
7038 : }
7039 :
7040 : /* static */
7041 : bool
7042 0 : nsBlockFrame::BlockCanIntersectFloats(nsIFrame* aFrame)
7043 : {
7044 0 : return aFrame->IsFrameOfType(nsIFrame::eBlockFrame) &&
7045 0 : !aFrame->IsFrameOfType(nsIFrame::eReplaced) &&
7046 0 : !(aFrame->GetStateBits() & NS_BLOCK_FLOAT_MGR);
7047 : }
7048 :
7049 : // Note that this width can vary based on the vertical position.
7050 : // However, the cases where it varies are the cases where the width fits
7051 : // in the available space given, which means that variation shouldn't
7052 : // matter.
7053 : /* static */
7054 : nsBlockFrame::ReplacedElementWidthToClear
7055 0 : nsBlockFrame::WidthToClearPastFloats(nsBlockReflowState& aState,
7056 : const nsRect& aFloatAvailableSpace,
7057 : nsIFrame* aFrame)
7058 : {
7059 : nscoord leftOffset, rightOffset;
7060 : nsCSSOffsetState offsetState(aFrame, aState.mReflowState.rendContext,
7061 0 : aState.mContentArea.width);
7062 :
7063 : ReplacedElementWidthToClear result;
7064 : aState.ComputeReplacedBlockOffsetsForFloats(aFrame, aFloatAvailableSpace,
7065 0 : leftOffset, rightOffset);
7066 0 : nscoord availWidth = aState.mContentArea.width - leftOffset - rightOffset;
7067 :
7068 : // We actually don't want the min width here; see bug 427782; we only
7069 : // want to displace if the width won't compute to a value small enough
7070 : // to fit.
7071 : // All we really need here is the result of ComputeSize, and we
7072 : // could *almost* get that from an nsCSSOffsetState, except for the
7073 : // last argument.
7074 0 : nsSize availSpace(availWidth, NS_UNCONSTRAINEDSIZE);
7075 : nsHTMLReflowState reflowState(aState.mPresContext, aState.mReflowState,
7076 0 : aFrame, availSpace);
7077 0 : result.borderBoxWidth = reflowState.ComputedWidth() +
7078 0 : reflowState.mComputedBorderPadding.LeftRight();
7079 : // Use the margins from offsetState rather than reflowState so that
7080 : // they aren't reduced by ignoring margins in overconstrained cases.
7081 0 : result.marginLeft = offsetState.mComputedMargin.left;
7082 0 : result.marginRight = offsetState.mComputedMargin.right;
7083 : return result;
7084 : }
7085 :
7086 : /* static */
7087 : nsBlockFrame*
7088 0 : nsBlockFrame::GetNearestAncestorBlock(nsIFrame* aCandidate)
7089 : {
7090 0 : nsBlockFrame* block = nsnull;
7091 0 : while(aCandidate) {
7092 0 : block = nsLayoutUtils::GetAsBlock(aCandidate);
7093 0 : if (block) {
7094 : // yay, candidate is a block!
7095 0 : return block;
7096 : }
7097 : // Not a block. Check its parent next.
7098 0 : aCandidate = aCandidate->GetParent();
7099 : }
7100 0 : NS_NOTREACHED("Fell off frame tree looking for ancestor block!");
7101 0 : return nsnull;
7102 : }
7103 :
7104 : nscoord
7105 0 : nsBlockFrame::GetEffectiveComputedHeight(const nsHTMLReflowState& aReflowState) const
7106 : {
7107 0 : nscoord height = aReflowState.ComputedHeight();
7108 0 : NS_ABORT_IF_FALSE(height != NS_UNCONSTRAINEDSIZE, "Don't call me!");
7109 :
7110 0 : if (GetPrevInFlow()) {
7111 : // Reduce the height by the computed height of prev-in-flows.
7112 0 : for (nsIFrame* prev = GetPrevInFlow(); prev; prev = prev->GetPrevInFlow()) {
7113 0 : height -= prev->GetRect().height;
7114 : }
7115 : // We just subtracted our top-border padding, since it was included in the
7116 : // first frame's height. Add it back to get the content height.
7117 0 : height += aReflowState.mComputedBorderPadding.top;
7118 : // We may have stretched the frame beyond its computed height. Oh well.
7119 0 : height = NS_MAX(0, height);
7120 : }
7121 0 : return height;
7122 : }
7123 :
7124 : #ifdef IBMBIDI
7125 : nsresult
7126 0 : nsBlockFrame::ResolveBidi()
7127 : {
7128 0 : NS_ASSERTION(!GetPrevInFlow(),
7129 : "ResolveBidi called on non-first continuation");
7130 :
7131 0 : nsPresContext* presContext = PresContext();
7132 0 : if (!presContext->BidiEnabled()) {
7133 0 : return NS_OK;
7134 : }
7135 :
7136 0 : return nsBidiPresUtils::Resolve(this);
7137 : }
7138 : #endif
7139 :
7140 : #ifdef DEBUG
7141 : void
7142 0 : nsBlockFrame::VerifyLines(bool aFinalCheckOK)
7143 : {
7144 0 : if (!gVerifyLines) {
7145 0 : return;
7146 : }
7147 0 : if (mLines.empty()) {
7148 0 : return;
7149 : }
7150 :
7151 0 : nsLineBox* cursor = GetLineCursor();
7152 :
7153 : // Add up the counts on each line. Also validate that IsFirstLine is
7154 : // set properly.
7155 0 : PRInt32 count = 0;
7156 0 : line_iterator line, line_end;
7157 0 : for (line = begin_lines(), line_end = end_lines();
7158 : line != line_end;
7159 : ++line) {
7160 0 : if (line == cursor) {
7161 0 : cursor = nsnull;
7162 : }
7163 0 : if (aFinalCheckOK) {
7164 0 : NS_ABORT_IF_FALSE(line->GetChildCount(), "empty line");
7165 0 : if (line->IsBlock()) {
7166 0 : NS_ASSERTION(1 == line->GetChildCount(), "bad first line");
7167 : }
7168 : }
7169 0 : count += line->GetChildCount();
7170 : }
7171 :
7172 : // Then count the frames
7173 0 : PRInt32 frameCount = 0;
7174 0 : nsIFrame* frame = mLines.front()->mFirstChild;
7175 0 : while (frame) {
7176 0 : frameCount++;
7177 0 : frame = frame->GetNextSibling();
7178 : }
7179 0 : NS_ASSERTION(count == frameCount, "bad line list");
7180 :
7181 : // Next: test that each line has right number of frames on it
7182 0 : for (line = begin_lines(), line_end = end_lines();
7183 : line != line_end;
7184 : ) {
7185 0 : count = line->GetChildCount();
7186 0 : frame = line->mFirstChild;
7187 0 : while (--count >= 0) {
7188 0 : frame = frame->GetNextSibling();
7189 : }
7190 0 : ++line;
7191 0 : if ((line != line_end) && (0 != line->GetChildCount())) {
7192 0 : NS_ASSERTION(frame == line->mFirstChild, "bad line list");
7193 : }
7194 : }
7195 :
7196 0 : if (cursor) {
7197 0 : FrameLines* overflowLines = GetOverflowLines();
7198 0 : if (overflowLines) {
7199 0 : line_iterator line = overflowLines->mLines.begin();
7200 0 : line_iterator line_end = overflowLines->mLines.end();
7201 0 : for (; line != line_end; ++line) {
7202 0 : if (line == cursor) {
7203 0 : cursor = nsnull;
7204 0 : break;
7205 : }
7206 : }
7207 : }
7208 : }
7209 0 : NS_ASSERTION(!cursor, "stale LineCursorProperty");
7210 : }
7211 :
7212 : void
7213 0 : nsBlockFrame::VerifyOverflowSituation()
7214 : {
7215 0 : nsBlockFrame* flow = static_cast<nsBlockFrame*>(GetFirstInFlow());
7216 0 : while (flow) {
7217 0 : FrameLines* overflowLines = flow->GetOverflowLines();
7218 0 : if (overflowLines) {
7219 0 : NS_ASSERTION(!overflowLines->mLines.empty(),
7220 : "should not be empty if present");
7221 0 : NS_ASSERTION(overflowLines->mLines.front()->mFirstChild,
7222 : "bad overflow lines");
7223 0 : NS_ASSERTION(overflowLines->mLines.front()->mFirstChild ==
7224 : overflowLines->mFrames.FirstChild(),
7225 : "bad overflow frames / lines");
7226 : }
7227 0 : nsLineBox* cursor = flow->GetLineCursor();
7228 0 : if (cursor) {
7229 0 : line_iterator line = flow->begin_lines();
7230 0 : line_iterator line_end = flow->end_lines();
7231 0 : for (; line != line_end && line != cursor; ++line)
7232 : ;
7233 0 : if (line == line_end && overflowLines) {
7234 0 : line = overflowLines->mLines.begin();
7235 0 : line_end = overflowLines->mLines.end();
7236 0 : for (; line != line_end && line != cursor; ++line)
7237 : ;
7238 : }
7239 0 : MOZ_ASSERT(line != line_end, "stale LineCursorProperty");
7240 : }
7241 0 : flow = static_cast<nsBlockFrame*>(flow->GetNextInFlow());
7242 : }
7243 0 : }
7244 :
7245 : PRInt32
7246 0 : nsBlockFrame::GetDepth() const
7247 : {
7248 0 : PRInt32 depth = 0;
7249 0 : nsIFrame* parent = mParent;
7250 0 : while (parent) {
7251 0 : parent = parent->GetParent();
7252 0 : depth++;
7253 : }
7254 0 : return depth;
7255 : }
7256 : #endif
|