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