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: NPL 1.1/GPL 2.0/LGPL 2.1
6
* The contents of this file are subject to the Netscape Public License
7
* Version 1.1 (the "License"); you may not use this file except in
8
* compliance with the License. You may obtain a copy of the License at
9
* http://www.mozilla.org/NPL/
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
16
* The Original Code is Mozilla Communicator client code.
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.
24
* Steve Clark <buster@netscape.com>
25
* Robert O'Callahan <roc+moz@cs.cmu.edu>
26
* L. David Baron <dbaron@dbaron.org>
29
* Alternatively, the contents of this file may be used under the terms of
30
* either the GNU General Public License Version 2 or later (the "GPL"), or
31
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32
* in which case the provisions of the GPL or the LGPL are applicable instead
33
* of those above. If you wish to allow use of your version of this file only
34
* under the terms of either the GPL or the LGPL, and not to allow others to
35
* use your version of this file under the terms of the NPL, indicate your
36
* decision by deleting the provisions above and replace them with the notice
37
* and other provisions required by the GPL or the LGPL. If you do not delete
38
* the provisions above, a recipient may use your version of this file under
39
* the terms of any one of the NPL, the GPL or the LGPL.
41
* ***** END LICENSE BLOCK ***** */
43
#include "nsBlockFrame.h"
44
#include "nsBlockReflowContext.h"
45
#include "nsBlockReflowState.h"
46
#include "nsBlockBandData.h"
47
#include "nsBulletFrame.h"
48
#include "nsLineBox.h"
49
#include "nsInlineFrame.h"
50
#include "nsLineLayout.h"
51
#include "nsPlaceholderFrame.h"
52
#include "nsStyleConsts.h"
53
#include "nsFrameManager.h"
54
#include "nsIPresContext.h"
55
#include "nsIPresShell.h"
56
#include "nsReflowPath.h"
57
#include "nsStyleContext.h"
59
#include "nsIFontMetrics.h"
60
#include "nsHTMLParts.h"
61
#include "nsHTMLAtoms.h"
62
#include "nsHTMLValue.h"
63
#include "nsIDOMEvent.h"
64
#include "nsIHTMLContent.h"
66
#include "nsLayoutAtoms.h"
67
#include "nsITextContent.h"
68
#include "nsStyleChangeList.h"
69
#include "nsIFocusTracker.h"
70
#include "nsIFrameSelection.h"
71
#include "nsSpaceManager.h"
72
#include "nsIntervalSet.h"
75
#include "nsGUIEvent.h"
76
#include "nsLayoutErrors.h"
77
#include "nsAutoPtr.h"
78
#ifdef MOZ_ACCESSIBILITY_ATK
79
#include "nsIAccessibilityService.h"
80
#include "nsIServiceManager.h"
84
#include "nsBidiPresUtils.h"
87
#include "nsIDOMHTMLBodyElement.h"
88
#include "nsIDOMHTMLHtmlElement.h"
90
static const int MIN_LINES_NEEDING_CURSOR = 20;
93
#include "nsPrintfCString.h"
94
#include "nsBlockDebugFlags.h"
96
PRBool nsBlockFrame::gLamePaintMetrics;
97
PRBool nsBlockFrame::gLameReflowMetrics;
98
PRBool nsBlockFrame::gNoisy;
99
PRBool nsBlockFrame::gNoisyDamageRepair;
100
PRBool nsBlockFrame::gNoisyMaxElementWidth;
101
PRBool nsBlockFrame::gNoisyReflow;
102
PRBool nsBlockFrame::gReallyNoisyReflow;
103
PRBool nsBlockFrame::gNoisySpaceManager;
104
PRBool nsBlockFrame::gVerifyLines;
105
PRBool nsBlockFrame::gDisableResizeOpt;
107
PRInt32 nsBlockFrame::gNoiseIndent;
109
struct BlockDebugFlags {
114
static const BlockDebugFlags gFlags[] = {
115
{ "reflow", &nsBlockFrame::gNoisyReflow },
116
{ "really-noisy-reflow", &nsBlockFrame::gReallyNoisyReflow },
117
{ "max-element-width", &nsBlockFrame::gNoisyMaxElementWidth },
118
{ "space-manager", &nsBlockFrame::gNoisySpaceManager },
119
{ "verify-lines", &nsBlockFrame::gVerifyLines },
120
{ "damage-repair", &nsBlockFrame::gNoisyDamageRepair },
121
{ "lame-paint-metrics", &nsBlockFrame::gLamePaintMetrics },
122
{ "lame-reflow-metrics", &nsBlockFrame::gLameReflowMetrics },
123
{ "disable-resize-opt", &nsBlockFrame::gDisableResizeOpt },
125
#define NUM_DEBUG_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
130
printf("Here are the available GECKO_BLOCK_DEBUG_FLAGS:\n");
131
const BlockDebugFlags* bdf = gFlags;
132
const BlockDebugFlags* end = gFlags + NUM_DEBUG_FLAGS;
133
for (; bdf < end; bdf++) {
134
printf(" %s\n", bdf->name);
136
printf("Note: GECKO_BLOCK_DEBUG_FLAGS is a comma separated list of flag\n");
137
printf("names (no whitespace)\n");
141
nsBlockFrame::InitDebugFlags()
143
static PRBool firstTime = PR_TRUE;
145
firstTime = PR_FALSE;
146
char* flags = PR_GetEnv("GECKO_BLOCK_DEBUG_FLAGS");
148
PRBool error = PR_FALSE;
150
char* cm = PL_strchr(flags, ',');
153
PRBool found = PR_FALSE;
154
const BlockDebugFlags* bdf = gFlags;
155
const BlockDebugFlags* end = gFlags + NUM_DEBUG_FLAGS;
156
for (; bdf < end; bdf++) {
157
if (PL_strcasecmp(bdf->name, flags) == 0) {
158
*(bdf->on) = PR_TRUE;
159
printf("nsBlockFrame: setting %s debug flag on\n", bdf->name);
182
// add in a sanity check for absurdly deep frame trees. See bug 42138
183
// can't just use IsFrameTreeTooDeep() because that method has side effects we don't want
184
#define MAX_DEPTH_FOR_LIST_RENUMBERING 200 // 200 open displayable tags is pretty unrealistic
186
//----------------------------------------------------------------------
188
// Debugging support code
191
const char* nsBlockFrame::kReflowCommandType[] = {
200
#ifdef REALLY_NOISY_FIRST_LINE
202
DumpStyleGeneaology(nsIFrame* aFrame, const char* gap)
205
nsFrame::ListTag(stdout, aFrame);
207
nsStyleContext* sc = aFrame->GetStyleContext();
208
while (nsnull != sc) {
211
psc = sc->GetParent();
218
#ifdef REFLOW_STATUS_COVERAGE
220
RecordReflowStatus(PRBool aChildIsBlock, nsReflowStatus aFrameReflowStatus)
222
static PRUint32 record[2];
225
// 1: child-is-inline
227
if (!aChildIsBlock) index |= 1;
229
// Compute new status
230
PRUint32 newS = record[index];
231
if (NS_INLINE_IS_BREAK(aFrameReflowStatus)) {
232
if (NS_INLINE_IS_BREAK_BEFORE(aFrameReflowStatus)) {
235
else if (NS_FRAME_IS_NOT_COMPLETE(aFrameReflowStatus)) {
242
else if (NS_FRAME_IS_NOT_COMPLETE(aFrameReflowStatus)) {
249
// Log updates to the status that yield different values
250
if (record[index] != newS) {
251
record[index] = newS;
252
printf("record(%d): %02x %02x\n", index, record[0], record[1]);
257
//----------------------------------------------------------------------
259
const nsIID kBlockFrameCID = NS_BLOCK_FRAME_CID;
262
NS_NewBlockFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame, PRUint32 aFlags)
264
NS_PRECONDITION(aNewFrame, "null OUT ptr");
265
if (nsnull == aNewFrame) {
266
return NS_ERROR_NULL_POINTER;
268
nsBlockFrame* it = new (aPresShell) nsBlockFrame;
270
return NS_ERROR_OUT_OF_MEMORY;
272
it->SetFlags(aFlags);
277
nsBlockFrame::nsBlockFrame()
284
nsBlockFrame::~nsBlockFrame()
289
nsBlockFrame::Destroy(nsIPresContext* aPresContext)
291
mAbsoluteContainer.DestroyFrames(this, aPresContext);
292
// Outside bullets are not in our child-list so check for them here
293
// and delete them when present.
294
if (mBullet && HaveOutsideBullet()) {
295
mBullet->Destroy(aPresContext);
299
mFloats.DestroyFrames(aPresContext);
301
nsLineBox::DeleteLineList(aPresContext, mLines);
303
// destroy overflow lines now
304
nsLineList* overflowLines = GetOverflowLines(aPresContext, PR_TRUE);
306
nsLineBox::DeleteLineList(aPresContext, *overflowLines);
308
nsFrameList* overflowOutOfFlows = GetOverflowOutOfFlows(PR_TRUE);
309
if (overflowOutOfFlows) {
310
overflowOutOfFlows->DestroyFrames(aPresContext);
311
delete overflowOutOfFlows;
314
return nsBlockFrameSuper::Destroy(aPresContext);
318
nsBlockFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr)
320
NS_PRECONDITION(aInstancePtr, "null out param");
321
if (aIID.Equals(kBlockFrameCID)) {
322
*aInstancePtr = NS_STATIC_CAST(void*, NS_STATIC_CAST(nsBlockFrame*, this));
325
if (aIID.Equals(NS_GET_IID(nsILineIterator)) ||
326
aIID.Equals(NS_GET_IID(nsILineIteratorNavigator)))
328
nsLineIterator* it = new nsLineIterator;
330
*aInstancePtr = nsnull;
331
return NS_ERROR_OUT_OF_MEMORY;
333
NS_ADDREF(it); // reference passed to caller
334
const nsStyleVisibility* visibility = GetStyleVisibility();
335
nsresult rv = it->Init(mLines,
336
visibility->mDirection == NS_STYLE_DIRECTION_RTL);
341
*aInstancePtr = NS_STATIC_CAST(void*,
342
NS_STATIC_CAST(nsILineIteratorNavigator*, it));
345
return nsBlockFrameSuper::QueryInterface(aIID, aInstancePtr);
349
nsBlockFrame::IsSplittable(nsSplittableType& aIsSplittable) const
351
aIsSplittable = NS_FRAME_SPLITTABLE_NON_RECTANGULAR;
357
nsBlockFrame::List(nsIPresContext* aPresContext, FILE* out, PRInt32 aIndent) const
359
IndentBy(out, aIndent);
361
#ifdef DEBUG_waterson
362
fprintf(out, " [parent=%p]", mParent);
365
fprintf(out, " [view=%p]", NS_STATIC_CAST(void*, GetView()));
367
if (nsnull != mNextSibling) {
368
fprintf(out, " next=%p", NS_STATIC_CAST(void*, mNextSibling));
371
// Output the flow linkage
372
if (nsnull != mPrevInFlow) {
373
fprintf(out, " prev-in-flow=%p", NS_STATIC_CAST(void*, mPrevInFlow));
375
if (nsnull != mNextInFlow) {
376
fprintf(out, " next-in-flow=%p", NS_STATIC_CAST(void*, mNextInFlow));
379
// Output the rect and state
380
fprintf(out, " {%d,%d,%d,%d}", mRect.x, mRect.y, mRect.width, mRect.height);
382
fprintf(out, " [state=%08x]", mState);
384
PRInt32 numInlineLines = 0;
385
PRInt32 numBlockLines = 0;
386
if (! mLines.empty()) {
387
for (const_line_iterator line = begin_lines(), line_end = end_lines();
397
fprintf(out, " sc=%p(i=%d,b=%d)<\n",
398
NS_STATIC_CAST(void*, mStyleContext), numInlineLines, numBlockLines);
402
if (! mLines.empty()) {
403
for (const_line_iterator line = begin_lines(), line_end = end_lines();
407
line->List(aPresContext, out, aIndent);
411
nsIAtom* listName = nsnull;
412
PRInt32 listIndex = 0;
414
listName = GetAdditionalChildListName(listIndex++);
415
if (nsnull == listName) {
418
nsIFrame* kid = GetFirstChild(listName);
420
IndentBy(out, aIndent);
422
if (nsnull != listName) {
423
listName->ToString(tmp);
424
fputs(NS_LossyConvertUCS2toASCII(tmp).get(), out);
428
nsIFrameDebug* frameDebug;
430
if (NS_SUCCEEDED(CallQueryInterface(kid, &frameDebug))) {
431
frameDebug->List(aPresContext, out, aIndent + 1);
433
kid = kid->GetNextSibling();
435
IndentBy(out, aIndent);
441
IndentBy(out, aIndent);
447
NS_IMETHODIMP_(nsFrameState)
448
nsBlockFrame::GetDebugStateBits() const
450
// We don't want to include our cursor flag in the bits the
451
// regression tester looks at
452
return nsBlockFrameSuper::GetDebugStateBits() & ~NS_BLOCK_HAS_LINE_CURSOR;
456
nsBlockFrame::GetFrameName(nsAString& aResult) const
458
return MakeFrameName(NS_LITERAL_STRING("Block"), aResult);
463
nsBlockFrame::GetType() const
465
return nsLayoutAtoms::blockFrame;
468
/////////////////////////////////////////////////////////////////////////////
469
// Child frame enumeration
472
nsBlockFrame::GetFirstChild(nsIAtom* aListName) const
474
if (mAbsoluteContainer.GetChildListName() == aListName) {
475
nsIFrame* result = nsnull;
476
mAbsoluteContainer.FirstChild(this, aListName, &result);
479
else if (nsnull == aListName) {
480
return (mLines.empty()) ? nsnull : mLines.front()->mFirstChild;
482
else if (aListName == nsLayoutAtoms::overflowList) {
483
nsLineList* overflowLines = GetOverflowLines(GetPresContext(), PR_FALSE);
484
return overflowLines ? overflowLines->front()->mFirstChild : nsnull;
486
else if (aListName == nsLayoutAtoms::overflowOutOfFlowList) {
487
nsFrameList* oof = GetOverflowOutOfFlows(PR_FALSE);
488
return oof ? oof->FirstChild() : nsnull;
490
else if (aListName == nsLayoutAtoms::floatList) {
491
return mFloats.FirstChild();
493
else if (aListName == nsLayoutAtoms::bulletList) {
494
if (HaveOutsideBullet()) {
502
nsBlockFrame::GetAdditionalChildListName(PRInt32 aIndex) const
505
case NS_BLOCK_FRAME_FLOAT_LIST_INDEX:
506
return nsLayoutAtoms::floatList;
507
case NS_BLOCK_FRAME_BULLET_LIST_INDEX:
508
return nsLayoutAtoms::bulletList;
509
case NS_BLOCK_FRAME_OVERFLOW_LIST_INDEX:
510
return nsLayoutAtoms::overflowList;
511
case NS_BLOCK_FRAME_OVERFLOW_OOF_LIST_INDEX:
512
return nsLayoutAtoms::overflowOutOfFlowList;
513
case NS_BLOCK_FRAME_ABSOLUTE_LIST_INDEX:
514
return mAbsoluteContainer.GetChildListName();
521
nsBlockFrame::IsContainingBlock() const
526
//////////////////////////////////////////////////////////////////////
527
// Frame structure methods
529
//////////////////////////////////////////////////////////////////////
533
CalculateContainingBlock(const nsHTMLReflowState& aReflowState,
535
nscoord aFrameHeight,
536
nscoord& aContainingBlockWidth,
537
nscoord& aContainingBlockHeight)
539
aContainingBlockWidth = -1; // have reflow state calculate
540
aContainingBlockHeight = -1; // have reflow state calculate
542
// The issue there is that for a 'height' of 'auto' the reflow state code
543
// won't know how to calculate the containing block height because it's
544
// calculated bottom up. We don't really want to do this for the initial
545
// containing block so that's why we have the check for if the element
546
// is absolutely or relatively positioned
547
if (aReflowState.mStyleDisplay->IsAbsolutelyPositioned() ||
548
(NS_STYLE_POSITION_RELATIVE == aReflowState.mStyleDisplay->mPosition)) {
549
aContainingBlockWidth = aFrameWidth;
550
aContainingBlockHeight = aFrameHeight;
552
// Containing block is relative to the padding edge
554
if (!aReflowState.mStyleBorder->GetBorder(border)) {
555
NS_NOTYETIMPLEMENTED("percentage border");
557
aContainingBlockWidth -= border.left + border.right;
558
aContainingBlockHeight -= border.top + border.bottom;
563
nsBlockFrame::Reflow(nsIPresContext* aPresContext,
564
nsHTMLReflowMetrics& aMetrics,
565
const nsHTMLReflowState& aReflowState,
566
nsReflowStatus& aStatus)
568
DO_GLOBAL_REFLOW_COUNT("nsBlockFrame", aReflowState.reason);
569
DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
572
nsCAutoString reflow;
573
reflow.Append(nsHTMLReflowState::ReasonToString(aReflowState.reason));
575
if (aReflowState.reason == eReflowReason_Incremental) {
576
nsHTMLReflowCommand *command = aReflowState.path->mReflowCommand;
583
command->GetType(type);
584
reflow += kReflowCommandType[type];
590
IndentBy(stdout, gNoiseIndent);
592
printf(": begin %s reflow availSize=%d,%d computedSize=%d,%d\n",
594
aReflowState.availableWidth, aReflowState.availableHeight,
595
aReflowState.mComputedWidth, aReflowState.mComputedHeight);
600
PRTime start = LL_ZERO; // Initialize these variablies to silence the compiler.
601
PRInt32 ctc = 0; // We only use these if they are set (gLameReflowMetrics).
602
if (gLameReflowMetrics) {
604
ctc = nsLineBox::GetCtorCount();
608
nsRect oldRect(mRect);
610
// Should we create a space manager?
611
nsAutoSpaceManager autoSpaceManager(NS_CONST_CAST(nsHTMLReflowState &, aReflowState));
613
// XXXldb If we start storing the space manager in the frame rather
614
// than keeping it around only during reflow then we should create it
615
// only when there are actually floats to manage. Otherwise things
616
// like tables will gain significant bloat.
617
if (NS_BLOCK_SPACE_MGR & mState)
618
autoSpaceManager.CreateSpaceManagerFor(aPresContext, this);
620
// See if it's an incremental reflow command
621
if (mAbsoluteContainer.HasAbsoluteFrames() &&
622
eReflowReason_Incremental == aReflowState.reason) {
623
// Give the absolute positioning code a chance to handle it
624
nscoord containingBlockWidth;
625
nscoord containingBlockHeight;
628
CalculateContainingBlock(aReflowState, mRect.width, mRect.height,
629
containingBlockWidth, containingBlockHeight);
631
mAbsoluteContainer.IncrementalReflow(this, aPresContext, aReflowState,
632
containingBlockWidth,
633
containingBlockHeight,
636
// If the incremental reflow command was handled by the absolute
637
// positioning code, then we're all done.
638
if (handled && !aMetrics.mComputeMEW) {
639
// Just return our current size as our desired size.
640
aMetrics.width = mRect.width;
641
aMetrics.height = mRect.height;
642
aMetrics.ascent = mAscent;
643
aMetrics.descent = aMetrics.height - aMetrics.ascent;
645
// Whether or not we're complete hasn't changed
646
aStatus = (nsnull != mNextInFlow) ? NS_FRAME_NOT_COMPLETE : NS_FRAME_COMPLETE;
648
// Factor the absolutely positioned child bounds into the overflow area
649
ComputeCombinedArea(aReflowState, aMetrics);
651
mAbsoluteContainer.CalculateChildBounds(aPresContext, childBounds);
652
aMetrics.mOverflowArea.UnionRect(aMetrics.mOverflowArea, childBounds);
654
StoreOverflow(aPresContext, aMetrics);
660
// OK, some lines may be reflowed. Blow away any saved line cursor because
661
// we may invalidate the nondecreasing combinedArea.y/yMost invariant,
662
// and we may even delete the line with the line cursor.
665
if (IsFrameTreeTooDeep(aReflowState, aMetrics)) {
668
extern char* nsPresShell_ReflowStackPointerTop;
670
char* newsp = (char*) ▮
671
printf("XXX: frame tree is too deep; approx stack size = %d\n",
672
nsPresShell_ReflowStackPointerTop - newsp);
675
aStatus = NS_FRAME_COMPLETE;
679
nsBlockReflowState state(aReflowState, aPresContext, this, aMetrics,
680
NS_BLOCK_MARGIN_ROOT & mState);
682
// The condition for doing Bidi resolutions includes a test for the
683
// dirtiness flags, because blocks sometimes send a resize reflow
684
// even though they have dirty children, An example where this can
685
// occur is when adding lines to a text control (bugs 95228 and 95400
686
// were caused by not doing Bidi resolution in these cases)
687
if (eReflowReason_Resize != aReflowState.reason ||
688
mState & NS_FRAME_IS_DIRTY || mState & NS_FRAME_HAS_DIRTY_CHILDREN) {
690
if (! mLines.empty()) {
692
aPresContext->GetBidiEnabled(&bidiEnabled);
694
nsBidiPresUtils* bidiUtils;
695
aPresContext->GetBidiUtils(&bidiUtils);
698
nsresult rc = bidiUtils->Resolve(aPresContext, this,
699
mLines.front()->mFirstChild,
701
aReflowState.mFlags.mVisualBidiFormControl);
702
if (NS_SUCCEEDED(rc) && forceReflow) {
703
// Mark everything dirty
704
// XXXldb This should be done right.
705
for (line_iterator line = begin_lines(), line_end = end_lines();
716
RenumberLists(aPresContext);
721
switch (aReflowState.reason) {
722
case eReflowReason_Initial:
723
#ifdef NOISY_REFLOW_REASON
725
printf(": reflow=initial\n");
727
DrainOverflowLines(aPresContext);
728
rv = PrepareInitialReflow(state);
729
mState &= ~NS_FRAME_FIRST_REFLOW;
732
case eReflowReason_Dirty:
733
// Do nothing; the dirty lines will already have been marked.
736
case eReflowReason_Incremental: {
737
#ifdef NOISY_REFLOW_REASON
739
printf(": reflow=incremental ");
741
nsReflowPath *path = aReflowState.path;
742
nsHTMLReflowCommand *command = path->mReflowCommand;
745
command->GetType(type);
746
#ifdef NOISY_REFLOW_REASON
747
printf("type=%s ", kReflowCommandType[type]);
750
case eReflowType_StyleChanged:
751
rv = PrepareStyleChangedReflow(state);
753
case eReflowType_ReflowDirty:
754
// Do nothing; the dirty lines will already have been marked.
756
case eReflowType_ContentChanged:
757
// Perform a full reflow.
758
rv = PrepareResizeReflow(state);
761
// We shouldn't get here. But, if we do, perform a full reflow.
762
NS_ERROR("unexpected reflow type");
763
rv = PrepareResizeReflow(state);
768
if (path->FirstChild() != path->EndChildren()) {
769
// We're along the reflow path, but not necessarily the target
771
#ifdef NOISY_REFLOW_REASON
775
for (nsReflowPath::iterator iter = path->FirstChild();
776
iter != path->EndChildren();
778
nsFrame::ListTag(stdout, *iter);
785
rv = PrepareChildIncrementalReflow(state);
788
#ifdef NOISY_REFLOW_REASON
795
case eReflowReason_StyleChange:
796
DrainOverflowLines(aPresContext);
797
rv = PrepareStyleChangedReflow(state);
800
case eReflowReason_Resize:
802
#ifdef NOISY_REFLOW_REASON
804
printf(": reflow=resize (%d)\n", aReflowState.reason);
806
DrainOverflowLines(aPresContext);
807
rv = PrepareResizeReflow(state);
811
NS_ASSERTION(NS_SUCCEEDED(rv), "setting up reflow failed");
812
if (NS_FAILED(rv)) return rv;
815
rv = ReflowDirtyLines(state);
816
NS_ASSERTION(NS_SUCCEEDED(rv), "reflow dirty lines failed");
817
if (NS_FAILED(rv)) return rv;
819
// If the block is complete, put continuted floats in the closest ancestor
820
// block that uses the same space manager and leave the block complete; this
821
// allows subsequent lines on the page to be impacted by floats. If the
822
// block is incomplete or there is no ancestor using the same space manager,
823
// put continued floats at the beginning of the first overflow line.
824
nsFrameList* overflowPlace = nsnull;
825
if ((NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight) &&
826
(overflowPlace = GetOverflowPlaceholders(aPresContext, PR_TRUE))) {
827
PRBool gaveToAncestor = PR_FALSE;
828
if (NS_FRAME_IS_COMPLETE(state.mReflowStatus)) {
829
// find the nearest block ancestor that uses the same space manager
830
for (const nsHTMLReflowState* ancestorRS = aReflowState.parentReflowState;
832
ancestorRS = ancestorRS->parentReflowState) {
833
nsIFrame* ancestor = ancestorRS->frame;
834
nsIAtom* fType = ancestor->GetType();
835
if ((nsLayoutAtoms::blockFrame == fType) || (nsLayoutAtoms::areaFrame == fType)) {
836
if (aReflowState.mSpaceManager == ancestorRS->mSpaceManager) {
837
// Put the continued floats in ancestor since it uses the same space manager
838
nsFrameList* ancestorPlace =
839
((nsBlockFrame*)ancestor)->GetOverflowPlaceholders(aPresContext, PR_FALSE);
841
ancestorPlace->AppendFrames(ancestor, overflowPlace->FirstChild());
844
// ancestor doesn't have an overflow place holder list, so
845
// create one. Note that we use AppendFrames() to add the
846
// frames, instead of passing them into the constructor, so
847
// we can levarage the code in AppendFrames() which updates
848
// the parent for each frame in the list.
850
ancestorPlace = new nsFrameList();
852
ancestorPlace->AppendFrames(ancestor, overflowPlace->FirstChild());
853
((nsBlockFrame*)ancestor)->SetOverflowPlaceholders(aPresContext, ancestorPlace);
856
return NS_ERROR_OUT_OF_MEMORY;
858
gaveToAncestor = PR_TRUE;
864
if (!gaveToAncestor) {
865
PRInt32 numOverflowPlace = overflowPlace->GetLength();
866
nsLineList* overflowLines = GetOverflowLines(aPresContext, PR_FALSE);
868
line_iterator firstLine = overflowLines->begin();
869
if (firstLine->IsBlock()) {
870
// Create a new line as the first line and put the floats there;
871
nsLineBox* newLine = state.NewLineBox(overflowPlace->FirstChild(), numOverflowPlace, PR_FALSE);
872
firstLine = mLines.before_insert(firstLine, newLine);
874
else { // floats go on 1st overflow line
875
nsIFrame* firstFrame = firstLine->mFirstChild;
876
firstLine->mFirstChild = overflowPlace->FirstChild();
877
// hook up the last placeholder with the original frames
878
nsPlaceholderFrame* lastPlaceholder =
879
(nsPlaceholderFrame*)overflowPlace->LastChild();
880
lastPlaceholder->SetNextSibling(firstFrame);
881
NS_ASSERTION(firstFrame != lastPlaceholder, "trying to set next sibling to self");
882
firstLine->SetChildCount(firstLine->GetChildCount() + numOverflowPlace);
886
// Create a line, put the floats in it, and then push.
887
nsLineBox* newLine = state.NewLineBox(overflowPlace->FirstChild(), numOverflowPlace, PR_FALSE);
889
return NS_ERROR_OUT_OF_MEMORY;
890
mLines.push_back(newLine);
891
nsLineList::iterator nextToLastLine = ----end_lines();
892
PushLines(state, nextToLastLine);
894
state.mReflowStatus = NS_FRAME_NOT_COMPLETE;
896
delete overflowPlace;
899
if (NS_FRAME_IS_NOT_COMPLETE(state.mReflowStatus)) {
900
if (NS_STYLE_OVERFLOW_HIDDEN == aReflowState.mStyleDisplay->mOverflow) {
901
state.mReflowStatus = NS_FRAME_COMPLETE;
905
ListTag(stdout); printf(": block is not complete\n");
910
// XXX_perf get rid of this! This is one of the things that makes
911
// incremental reflow O(N^2).
914
// Compute our final size
915
ComputeFinalSize(aReflowState, state, aMetrics);
916
StoreOverflow(aPresContext, aMetrics);
918
// see if verifyReflow is enabled, and if so store off the space manager pointer
920
PRInt32 verifyReflowFlags = nsIPresShell::GetVerifyReflowFlags();
921
if (VERIFY_REFLOW_INCLUDE_SPACE_MANAGER & verifyReflowFlags)
923
// this is a leak of the space manager, but it's only in debug if verify reflow is enabled, so not a big deal
924
nsIPresShell *shell = aPresContext->GetPresShell();
926
nsHTMLReflowState& reflowState = (nsHTMLReflowState&)aReflowState;
927
rv = shell->FrameManager()->SetFrameProperty(
928
this, nsLayoutAtoms::spaceManagerProperty,
929
reflowState.mSpaceManager,
930
nsnull /* should be nsSpaceManagerDestroyer*/);
932
autoSpaceManager.DebugOrphanSpaceManager();
937
// Determine if we need to repaint our border, background or outline
938
CheckInvalidateSizeChange(aPresContext, aMetrics, aReflowState);
940
// Let the absolutely positioned container reflow any absolutely positioned
941
// child frames that need to be reflowed, e.g., elements with a percentage
942
// based width/height
943
// We want to do this under either of two conditions:
944
// 1. If we didn't do the incremental reflow above.
945
// 2. If our size changed.
946
// Even though it's the padding edge that's the containing block, we
947
// can use our rect (the border edge) since if the border style
948
// changed, the reflow would have been targeted at us so we'd satisfy
950
if (NS_SUCCEEDED(rv) && mAbsoluteContainer.HasAbsoluteFrames()) {
952
if (eReflowReason_Incremental != aReflowState.reason ||
953
aReflowState.path->mReflowCommand ||
955
nscoord containingBlockWidth;
956
nscoord containingBlockHeight;
958
CalculateContainingBlock(aReflowState, aMetrics.width, aMetrics.height,
959
containingBlockWidth, containingBlockHeight);
961
rv = mAbsoluteContainer.Reflow(this, aPresContext, aReflowState,
962
containingBlockWidth,
963
containingBlockHeight,
966
mAbsoluteContainer.CalculateChildBounds(aPresContext, childBounds);
969
// Factor the absolutely positioned child bounds into the overflow area
970
aMetrics.mOverflowArea.UnionRect(aMetrics.mOverflowArea, childBounds);
972
StoreOverflow(aPresContext, aMetrics);
974
// Clear the space manager pointer in the block reflow state so we
975
// don't waste time translating the coordinate system back on a dead
977
if (NS_BLOCK_SPACE_MGR & mState)
978
state.mSpaceManager = nsnull;
980
aStatus = state.mReflowStatus;
987
IndentBy(stdout, gNoiseIndent);
989
printf(": status=%x (%scomplete) metrics=%d,%d carriedMargin=%d",
990
aStatus, NS_FRAME_IS_COMPLETE(aStatus) ? "" : "not ",
991
aMetrics.width, aMetrics.height,
992
aMetrics.mCarriedOutBottomMargin.get());
993
if (mState & NS_FRAME_OUTSIDE_CHILDREN) {
994
printf(" combinedArea={%d,%d,%d,%d}",
995
aMetrics.mOverflowArea.x,
996
aMetrics.mOverflowArea.y,
997
aMetrics.mOverflowArea.width,
998
aMetrics.mOverflowArea.height);
1000
if (aMetrics.mComputeMEW) {
1001
printf(" maxElementWidth=%d", aMetrics.mMaxElementWidth);
1006
if (gLameReflowMetrics) {
1007
PRTime end = PR_Now();
1009
PRInt32 ectc = nsLineBox::GetCtorCount();
1010
PRInt32 numLines = mLines.size();
1011
if (!numLines) numLines = 1;
1012
PRTime delta, perLineDelta, lines;
1013
LL_I2L(lines, numLines);
1014
LL_SUB(delta, end, start);
1015
LL_DIV(perLineDelta, delta, lines);
1019
PR_snprintf(buf, sizeof(buf),
1020
": %lld elapsed (%lld per line) (%d lines; %d new lines)",
1021
delta, perLineDelta, numLines, ectc - ctc);
1022
printf("%s\n", buf);
1024
if (gNoisyMaxElementWidth) {
1025
if (aMetrics.mComputeMEW) {
1026
IndentBy(stdout, gNoiseIndent);
1027
printf("block %p returning with maxElementWidth=%d\n",
1028
NS_STATIC_CAST(void*, this),
1029
aMetrics.mMaxElementWidth);
1034
NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics);
1039
HaveAutoWidth(const nsHTMLReflowState& aReflowState)
1041
return NS_UNCONSTRAINEDSIZE == aReflowState.mComputedWidth ||
1042
eStyleUnit_Auto == aReflowState.mStylePosition->mWidth.GetUnit();
1046
// XXXldb why do we check vertical and horizontal at the same time? Don't
1047
// we usually care about one or the other?
1049
IsPercentageAwareChild(const nsIFrame* aFrame)
1051
NS_ASSERTION(aFrame, "null frame is not allowed");
1053
const nsStyleMargin* margin = aFrame->GetStyleMargin();
1054
if (nsLineLayout::IsPercentageUnitSides(&margin->mMargin)) {
1058
const nsStylePadding* padding = aFrame->GetStylePadding();
1059
if (nsLineLayout::IsPercentageUnitSides(&padding->mPadding)) {
1063
const nsStyleBorder* border = aFrame->GetStyleBorder();
1064
if (nsLineLayout::IsPercentageUnitSides(&border->mBorder)) {
1068
const nsStylePosition* pos = aFrame->GetStylePosition();
1070
if (eStyleUnit_Percent == pos->mWidth.GetUnit()
1071
|| eStyleUnit_Percent == pos->mMaxWidth.GetUnit()
1072
|| eStyleUnit_Percent == pos->mMinWidth.GetUnit()
1073
|| eStyleUnit_Percent == pos->mHeight.GetUnit()
1074
|| eStyleUnit_Percent == pos->mMinHeight.GetUnit()
1075
|| eStyleUnit_Percent == pos->mMaxHeight.GetUnit()
1076
|| nsLineLayout::IsPercentageUnitSides(&pos->mOffset)) { // XXX need more here!!!
1084
nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState,
1085
nsBlockReflowState& aState,
1086
nsHTMLReflowMetrics& aMetrics)
1088
const nsMargin& borderPadding = aState.BorderPadding();
1089
#ifdef NOISY_FINAL_SIZE
1091
printf(": mY=%d mIsBottomMarginRoot=%s mPrevBottomMargin=%d bp=%d,%d\n",
1092
aState.mY, aState.GetFlag(BRS_ISBOTTOMMARGINROOT) ? "yes" : "no",
1093
aState.mPrevBottomMargin,
1094
borderPadding.top, borderPadding.bottom);
1097
// XXXldb Handling min-width/max-width stuff after reflowing children
1098
// seems wrong. But IIRC this function only does more than a little
1099
// bit in rare cases (or something like that, I'm not really sure).
1100
// What are those cases, and do we get the wrong behavior?
1102
// Compute final width
1103
nscoord maxElementWidth = 0;
1104
#ifdef NOISY_KIDXMOST
1105
printf("%p aState.mKidXMost=%d\n", this, aState.mKidXMost);
1107
if (!HaveAutoWidth(aReflowState)) {
1108
// Use style defined width
1109
aMetrics.width = borderPadding.left + aReflowState.mComputedWidth +
1110
borderPadding.right;
1112
if (aState.GetFlag(BRS_COMPUTEMAXELEMENTWIDTH)) {
1113
if (GetStylePosition()->mWidth.GetUnit() == eStyleUnit_Percent) {
1114
// for percentage widths, |HaveAutoWidth| is sometimes true and
1115
// sometimes false (XXXldb check that this is really true), and
1116
// we want the max-element-width to be the same either way
1117
// (i.e., whether it's an uncontsrained reflow or a fixed-width
1118
// reflow). Thus, do the same thing we do below.
1119
maxElementWidth = aState.mMaxElementWidth +
1120
borderPadding.left + borderPadding.right;
1122
// When style defines the width use it for the max-element-width
1123
// because we can't shrink any smaller.
1124
maxElementWidth = aMetrics.width;
1129
nscoord computedWidth;
1131
// XXX Misleading comment:
1132
// There are two options here. We either shrink wrap around our
1133
// contents or we fluff out to the maximum block width. Note:
1134
// We always shrink wrap when given an unconstrained width.
1135
if ((0 == (NS_BLOCK_SHRINK_WRAP & mState)) &&
1136
!aState.GetFlag(BRS_UNCONSTRAINEDWIDTH) &&
1137
!aState.GetFlag(BRS_SHRINKWRAPWIDTH)) {
1138
// XXX Misleading comment:
1139
// Set our width to the max width if we aren't already that
1140
// wide. Note that the max-width has nothing to do with our
1141
// contents (CSS2 section XXX)
1142
// XXXldb In what cases do we reach this code?
1143
computedWidth = borderPadding.left + aState.mContentArea.width +
1144
borderPadding.right;
1146
computedWidth = aState.mKidXMost;
1147
if (NS_BLOCK_SPACE_MGR & mState) {
1148
// Include the space manager's state to properly account for the
1149
// extent of floated elements.
1151
if (aReflowState.mSpaceManager->XMost(xmost) &&
1152
computedWidth < xmost)
1153
computedWidth = xmost;
1155
computedWidth += borderPadding.right;
1158
if (aState.GetFlag(BRS_COMPUTEMAXELEMENTWIDTH)) {
1159
// Add in border and padding dimensions to already computed
1160
// max-element-width values.
1161
maxElementWidth = aState.mMaxElementWidth +
1162
borderPadding.left + borderPadding.right;
1163
if (computedWidth < maxElementWidth) {
1164
// XXXldb It's *compute* max-element-width, not *change size
1165
// based on* max-element-width...
1166
computedWidth = maxElementWidth;
1170
// Apply min/max values
1171
if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMaxWidth) {
1172
nscoord computedMaxWidth = aReflowState.mComputedMaxWidth +
1173
borderPadding.left + borderPadding.right;
1174
if (computedWidth > computedMaxWidth) {
1175
computedWidth = computedMaxWidth;
1178
if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMinWidth) {
1179
nscoord computedMinWidth = aReflowState.mComputedMinWidth +
1180
borderPadding.left + borderPadding.right;
1181
if (computedWidth < computedMinWidth) {
1182
computedWidth = computedMinWidth;
1185
aMetrics.width = computedWidth;
1187
// If we're shrink wrapping, then now that we know our final width we
1188
// need to do horizontal alignment of the inline lines and make sure
1189
// blocks are correctly sized and positioned. Any lines that need
1190
// final adjustment will have been marked as dirty
1191
if (aState.GetFlag(BRS_SHRINKWRAPWIDTH) && aState.GetFlag(BRS_NEEDRESIZEREFLOW)) {
1192
// If the parent reflow state is also shrink wrap width, then
1193
// we don't need to do this, because it will reflow us after it
1194
// calculates the final width
1195
const nsHTMLReflowState *prs = aReflowState.parentReflowState;
1196
if (!prs || NS_SHRINKWRAPWIDTH != prs->mComputedWidth) {
1197
// XXX Is this only used on things that are already NS_BLOCK_SPACE_MGR
1198
// and NS_BLOCK_MARGIN_ROOT?
1199
nsHTMLReflowState reflowState(aReflowState);
1201
reflowState.mComputedWidth = aMetrics.width - borderPadding.left -
1202
borderPadding.right;
1203
reflowState.reason = eReflowReason_Resize;
1204
reflowState.mSpaceManager->ClearRegions();
1207
nscoord oldDesiredWidth = aMetrics.width;
1209
nsBlockReflowState state(reflowState, aState.mPresContext, this,
1210
aMetrics, NS_BLOCK_MARGIN_ROOT & mState);
1211
ReflowDirtyLines(state);
1212
aState.mY = state.mY;
1213
NS_ASSERTION(oldDesiredWidth == aMetrics.width, "bad desired width");
1218
// Compute final height
1219
if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedHeight) {
1220
if (NS_FRAME_IS_COMPLETE(aState.mReflowStatus)) {
1221
// Calculate the total unconstrained height including borders and padding. A continuation
1222
// will have the same value as the first-in-flow, since the reflow state logic is based on
1223
// style and doesn't alter mComputedHeight based on prev-in-flows.
1224
aMetrics.height = borderPadding.top + aReflowState.mComputedHeight + borderPadding.bottom;
1226
// Reduce the height by the height of prev-in-flows. The last-in-flow will automatically
1227
// pick up the bottom border/padding, since it was part of the original aMetrics.height.
1228
for (nsIFrame* prev = mPrevInFlow; prev; prev->GetPrevInFlow(&prev)) {
1229
aMetrics.height -= prev->GetRect().height;
1230
// XXX: All block level next-in-flows have borderPadding.top applied to them (bug 174688).
1231
// The following should be removed when this gets fixed. bug 174688 prevents us from honoring
1232
// a style height (exactly) and this hack at least compensates by increasing the height by the
1233
// excessive borderPadding.top.
1234
aMetrics.height += borderPadding.top;
1236
aMetrics.height = PR_MAX(0, aMetrics.height);
1238
if (aMetrics.height > aReflowState.availableHeight) {
1239
// Take up the available height; continuations will take up the rest.
1240
aMetrics.height = aReflowState.availableHeight;
1241
aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
1245
// Use the current height; continuations will take up the rest.
1246
aMetrics.height = aState.mY;
1249
// Don't carry out a bottom margin when our height is fixed.
1250
aState.mPrevBottomMargin.Zero();
1253
nscoord autoHeight = aState.mY;
1255
// Shrink wrap our height around our contents.
1256
if (aState.GetFlag(BRS_ISBOTTOMMARGINROOT)) {
1257
// When we are a bottom-margin root make sure that our last
1258
// childs bottom margin is fully applied.
1259
// XXX check for a fit
1260
autoHeight += aState.mPrevBottomMargin.get();
1263
if (NS_BLOCK_SPACE_MGR & mState) {
1264
// Include the space manager's state to properly account for the
1265
// bottom margin of any floated elements; e.g., inside a table cell.
1267
if (aReflowState.mSpaceManager->YMost(ymost) &&
1271
autoHeight += borderPadding.bottom;
1273
// Apply min/max values
1274
if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMaxHeight) {
1275
nscoord computedMaxHeight = aReflowState.mComputedMaxHeight +
1276
borderPadding.top + borderPadding.bottom;
1277
if (autoHeight > computedMaxHeight) {
1278
autoHeight = computedMaxHeight;
1281
if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMinHeight) {
1282
nscoord computedMinHeight = aReflowState.mComputedMinHeight +
1283
borderPadding.top + borderPadding.bottom;
1284
if (autoHeight < computedMinHeight) {
1285
autoHeight = computedMinHeight;
1288
aMetrics.height = autoHeight;
1292
aMetrics.ascent = mAscent;
1293
aMetrics.descent = aMetrics.height - aMetrics.ascent;
1295
if (aState.GetFlag(BRS_COMPUTEMAXELEMENTWIDTH)) {
1296
// Store away the final value
1297
aMetrics.mMaxElementWidth = maxElementWidth;
1299
if (gNoisyMaxElementWidth) {
1300
IndentBy(stdout, gNoiseIndent);
1301
printf ("nsBlockFrame::CFS: %p returning MEW %d\n",
1302
NS_STATIC_CAST(void*, this), aMetrics.mMaxElementWidth);
1307
// Return bottom margin information
1308
// rbs says he hit this assertion occasionally (see bug 86947), so
1309
// just set the margin to zero and we'll figure out why later
1310
//NS_ASSERTION(aMetrics.mCarriedOutBottomMargin.IsZero(),
1311
// "someone else set the margin");
1312
if (!aState.GetFlag(BRS_ISBOTTOMMARGINROOT))
1313
aMetrics.mCarriedOutBottomMargin = aState.mPrevBottomMargin;
1315
aMetrics.mCarriedOutBottomMargin.Zero();
1318
if (CRAZY_WIDTH(aMetrics.width) || CRAZY_HEIGHT(aMetrics.height)) {
1320
printf(": WARNING: desired:%d,%d\n", aMetrics.width, aMetrics.height);
1322
if (aState.GetFlag(BRS_COMPUTEMAXELEMENTWIDTH) &&
1323
(maxElementWidth > aMetrics.width))) {
1325
printf(": WARNING: max-element-width:%d desired:%d,%d maxSize:%d,%d\n",
1326
maxElementWidth, aMetrics.width, aMetrics.height,
1327
aState.mReflowState.availableWidth,
1328
aState.mReflowState.availableHeight);
1332
if (gNoisyMaxElementWidth) {
1333
if (aState.GetFlag(BRS_COMPUTEMAXELEMENTWIDTH)) {
1334
IndentBy(stdout, gNoiseIndent);
1335
if (NS_UNCONSTRAINEDSIZE == aState.mReflowState.availableWidth) {
1339
printf(": max-element-width:%d desired:%d,%d maxSize:%d,%d\n",
1340
maxElementWidth, aMetrics.width, aMetrics.height,
1341
aState.mReflowState.availableWidth,
1342
aState.mReflowState.availableHeight);
1347
// If we're requested to update our maximum width, then compute it
1348
if (aState.GetFlag(BRS_COMPUTEMAXWIDTH)) {
1349
if (!HaveAutoWidth(aReflowState) &&
1350
aReflowState.mStylePosition->mWidth.GetUnit() != eStyleUnit_Percent) {
1351
aMetrics.mMaximumWidth = aMetrics.width;
1353
// We need to add in for the right border/padding
1354
// XXXldb Why right and not left?
1355
aMetrics.mMaximumWidth = aState.mMaximumWidth + borderPadding.right;
1357
#ifdef NOISY_MAXIMUM_WIDTH
1358
printf("nsBlockFrame::ComputeFinalSize block %p setting aMetrics.mMaximumWidth to %d\n", this, aMetrics.mMaximumWidth);
1362
ComputeCombinedArea(aReflowState, aMetrics);
1366
nsBlockFrame::ComputeCombinedArea(const nsHTMLReflowState& aReflowState,
1367
nsHTMLReflowMetrics& aMetrics)
1369
// Compute the combined area of our children
1370
// XXX_perf: This can be done incrementally. It is currently one of
1371
// the things that makes incremental reflow O(N^2).
1372
nscoord xa = 0, ya = 0, xb = aMetrics.width, yb = aMetrics.height;
1373
if (NS_STYLE_OVERFLOW_HIDDEN != aReflowState.mStyleDisplay->mOverflow) {
1374
for (line_iterator line = begin_lines(), line_end = end_lines();
1378
// Compute min and max x/y values for the reflowed frame's
1380
nsRect lineCombinedArea(line->GetCombinedArea());
1381
nscoord x = lineCombinedArea.x;
1382
nscoord y = lineCombinedArea.y;
1383
nscoord xmost = x + lineCombinedArea.width;
1384
nscoord ymost = y + lineCombinedArea.height;
1399
// Factor the bullet in; normally the bullet will be factored into
1400
// the line-box's combined area. However, if the line is a block
1401
// line then it won't; if there are no lines, it won't. So just
1402
// factor it in anyway (it can't hurt if it was already done).
1403
// XXXldb Can we just fix GetCombinedArea instead?
1405
nsRect r = mBullet->GetRect();
1406
if (r.x < xa) xa = r.x;
1407
if (r.y < ya) ya = r.y;
1408
nscoord xmost = r.XMost();
1409
if (xmost > xb) xb = xmost;
1410
nscoord ymost = r.YMost();
1411
if (ymost > yb) yb = ymost;
1414
#ifdef NOISY_COMBINED_AREA
1416
printf(": ca=%d,%d,%d,%d\n", xa, ya, xb-xa, yb-ya);
1419
aMetrics.mOverflowArea.x = xa;
1420
aMetrics.mOverflowArea.y = ya;
1421
aMetrics.mOverflowArea.width = xb - xa;
1422
aMetrics.mOverflowArea.height = yb - ya;
1427
nsBlockFrame::PrepareInitialReflow(nsBlockReflowState& aState)
1429
PrepareResizeReflow(aState);
1434
nsBlockFrame::PrepareChildIncrementalReflow(nsBlockReflowState& aState)
1436
// XXXwaterson this is non-optimal. We'd rather do this in
1437
// ReflowDirtyLines; however, I'm not quite ready to figure out how
1438
// to deal with reflow path retargeting yet.
1439
nsReflowPath *path = aState.mReflowState.path;
1441
nsReflowPath::iterator iter = path->FirstChild();
1442
nsReflowPath::iterator end = path->EndChildren();
1444
for ( ; iter != end; ++iter) {
1445
// Determine the line being impacted
1446
line_iterator line = FindLineFor(*iter);
1447
if (line == end_lines()) {
1448
// This assertion actually fires on lots of pages
1449
// (e.g., bugzilla, bugzilla query page), so limit it
1450
// to a few people until we fix the problem causing it.
1452
// I think waterson explained once why it was happening -- I think
1453
// it has something to do with the interaction of the unconstrained
1454
// reflow in multi-pass reflow with the reflow command's chain, but
1455
// I don't remember the details.
1456
#if defined(DEBUG_dbaron) || defined(DEBUG_waterson)
1457
NS_NOTREACHED("We don't have a line for the target of the reflow. "
1458
"Being inefficient");
1460
// This can't happen, but just in case it does...
1461
PrepareResizeReflow(aState);
1465
if (line->IsInline()) {
1466
if (aState.GetFlag(BRS_COMPUTEMAXWIDTH)) {
1467
// We've been asked to compute the maximum width of the block
1468
// frame, which ReflowLine() will handle by performing an
1469
// unconstrained reflow on the line. If this incremental
1470
// reflow is targeted at a continuing frame, we may have to
1471
// retarget it, as the unconstrained reflow can destroy some
1472
// of the continuations. This will allow the incremental
1473
// reflow to arrive at the target frame during the first-pass
1474
// unconstrained reflow.
1475
nsIFrame *prevInFlow;
1476
(*iter)->GetPrevInFlow(&prevInFlow);
1478
RetargetInlineIncrementalReflow(iter, line, prevInFlow);
1482
// Just mark this line dirty. We never need to mark the
1483
// previous line dirty since either:
1484
// * the line is a block, and there would never be a chance to pull
1486
// * It's an incremental reflow to something within an inline, which
1487
// we know must be very limited.
1494
nsBlockFrame::RetargetInlineIncrementalReflow(nsReflowPath::iterator &aTarget,
1495
line_iterator &aLine,
1496
nsIFrame *aPrevInFlow)
1498
// To retarget the reflow, we'll walk back through the continuations
1499
// until we reach the primary frame, or we reach a continuation that
1500
// is preceded by a ``hard'' line break.
1501
NS_ASSERTION(aLine->Contains(*aTarget),
1502
"line doesn't contain the target of the incremental reflow");
1504
// Now fix the iterator, keeping track of how many lines we walk
1506
PRInt32 lineCount = 0;
1508
// XXX this might happen if the block is split; e.g.,
1509
// printing or print preview. For now, panic.
1510
NS_ASSERTION(aLine != begin_lines(),
1511
"ran out of lines before we ran out of prev-in-flows");
1513
// Is the previous line a ``hard'' break? If so, stop: these
1514
// continuations will be preserved during an unconstrained reflow.
1515
// XXXwaterson should this be `!= NS_STYLE_CLEAR_NONE'?
1517
if (aLine->GetBreakType() == NS_STYLE_CLEAR_LINE)
1520
*aTarget = aPrevInFlow;
1521
aPrevInFlow->GetPrevInFlow(&aPrevInFlow);
1524
// Paranoia. Ensure that the prev-in-flow is really in the
1526
line_iterator check = FindLineFor(*aTarget);
1527
NS_ASSERTION(check == aLine, "prev-in-flow not in previous linebox");
1531
} while (aPrevInFlow);
1533
if (lineCount > 0) {
1534
// Fix any frames deeper in the reflow path.
1536
// XXXwaterson fix me! we've got to recurse through the iterator's
1537
// kids. This really shouldn't matter unless we want to implement
1538
// `display: inline-block' or do XBL form controls. Why, you ask?
1539
// Because what will happen is that inline frames will get flowed
1540
// with a resize reflow, which will be sufficient to mask any
1541
// glitches that would otherwise occur. However, as soon as boxes
1542
// or blocks end up in the flow here (and aren't explicit reflow
1543
// roots), they may optimize away the resize reflow.
1545
// Get the reflow path, which is stored as a stack (i.e., the next
1546
// frame in the reflow is at the _end_ of the array).
1547
nsVoidArray *path = aState.mReflowState.reflowCommand->GetPath();
1549
for (PRInt32 i = path->Count() - 1; i >= 0; --i) {
1550
nsIFrame *frame = NS_STATIC_CAST(nsIFrame *, path->ElementAt(i));
1552
// Stop if we encounter a non-inline frame in the reflow path.
1553
const nsStyleDisplay* display = frame->GetStyleDisplay();
1555
if (NS_STYLE_DISPLAY_INLINE != display->mDisplay)
1558
// Walk back to the primary frame.
1559
PRInt32 count = lineCount;
1560
nsIFrame *prevInFlow;
1562
frame->GetPrevInFlow(&prevInFlow);
1563
} while (--count >= 0 && prevInFlow && (frame = prevInFlow));
1565
path->ReplaceElementAt(frame, i);
1568
NS_WARNING("blowing an incremental reflow targeted at a nested inline");
1574
nsBlockFrame::MarkLineDirty(line_iterator aLine)
1580
IndentBy(stdout, gNoiseIndent);
1582
printf(": mark line %p dirty\n", NS_STATIC_CAST(void*, aLine.get()));
1586
// Mark previous line dirty if its an inline line so that it can
1587
// maybe pullup something from the line just affected.
1588
// XXX We don't need to do this if aPrevLine ends in a break-after...
1589
if (aLine != mLines.front() &&
1590
aLine->IsInline() &&
1591
aLine.prev()->IsInline()) {
1592
aLine.prev()->MarkDirty();
1595
IndentBy(stdout, gNoiseIndent);
1597
printf(": mark prev-line %p dirty\n",
1598
NS_STATIC_CAST(void*, aLine.prev().get()));
1607
nsBlockFrame::UpdateBulletPosition(nsBlockReflowState& aState)
1609
if (nsnull == mBullet) {
1610
// Don't bother if there is no bullet
1613
const nsStyleList* styleList = GetStyleList();
1614
if (NS_STYLE_LIST_STYLE_POSITION_INSIDE == styleList->mListStylePosition) {
1615
if (mBullet && HaveOutsideBullet()) {
1616
// We now have an inside bullet, but used to have an outside
1617
// bullet. Adjust the frame line list
1618
if (! mLines.empty()) {
1619
// if we have a line already, then move the bullet to the front of the
1621
nsIFrame* child = nsnull;
1622
nsLineBox* firstLine = mLines.front();
1624
// bullet should not have any siblings if it was an outside bullet
1625
NS_ASSERTION(!mBullet->GetNextSibling(), "outside bullet should not have siblings");
1627
// move bullet to front and chain the previous frames, and update the line count
1628
child = firstLine->mFirstChild;
1629
firstLine->mFirstChild = mBullet;
1630
mBullet->SetNextSibling(child);
1631
PRInt32 count = firstLine->GetChildCount();
1632
firstLine->SetChildCount(count+1);
1633
// dirty it here in case the caller does not
1634
firstLine->MarkDirty();
1636
// no prior lines, just create a new line for the bullet
1637
nsLineBox* line = aState.NewLineBox(mBullet, 1, PR_FALSE);
1639
return NS_ERROR_OUT_OF_MEMORY;
1641
mLines.push_back(line);
1644
mState &= ~NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET;
1647
if (!HaveOutsideBullet()) {
1648
// We now have an outside bullet, but used to have an inside
1649
// bullet. Take the bullet frame out of the first lines frame
1651
if ((! mLines.empty()) && (mBullet == mLines.front()->mFirstChild)) {
1652
nsIFrame* next = mBullet->GetNextSibling();
1653
mBullet->SetNextSibling(nsnull);
1654
PRInt32 count = mLines.front()->GetChildCount() - 1;
1655
NS_ASSERTION(count >= 0, "empty line w/o bullet");
1656
mLines.front()->SetChildCount(count);
1658
nsLineBox* oldFront = mLines.front();
1660
aState.FreeLineBox(oldFront);
1661
if (! mLines.empty()) {
1662
mLines.front()->MarkDirty();
1666
mLines.front()->mFirstChild = next;
1667
mLines.front()->MarkDirty();
1671
mState |= NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET;
1674
VerifyLines(PR_TRUE);
1680
nsBlockFrame::PrepareStyleChangedReflow(nsBlockReflowState& aState)
1682
nsresult rv = UpdateBulletPosition(aState);
1684
// Mark everything dirty
1685
for (line_iterator line = begin_lines(), line_end = end_lines();
1695
nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState)
1697
// See if we can try and avoid marking all the lines as dirty
1698
PRBool tryAndSkipLines = PR_FALSE;
1700
// we need to calculate if any part of then block itself
1701
// is impacted by a float (bug 19579)
1702
aState.GetAvailableSpace();
1704
// See if this is this a constrained resize reflow that is not impacted by floats
1705
if ((! aState.IsImpactedByFloat()) &&
1706
(aState.mReflowState.reason == eReflowReason_Resize) &&
1707
(NS_UNCONSTRAINEDSIZE != aState.mReflowState.availableWidth)) {
1709
// If the text is left-aligned, then we try and avoid reflowing the lines
1710
const nsStyleText* styleText = GetStyleText();
1712
if ((NS_STYLE_TEXT_ALIGN_LEFT == styleText->mTextAlign) ||
1713
((NS_STYLE_TEXT_ALIGN_DEFAULT == styleText->mTextAlign) &&
1714
(NS_STYLE_DIRECTION_LTR == aState.mReflowState.mStyleVisibility->mDirection))) {
1715
tryAndSkipLines = PR_TRUE;
1720
if (gDisableResizeOpt) {
1721
tryAndSkipLines = PR_FALSE;
1724
if (!tryAndSkipLines) {
1725
const nsStyleText* styleText = GetStyleText();
1726
IndentBy(stdout, gNoiseIndent);
1728
printf(": marking all lines dirty: reason=%d availWidth=%d textAlign=%d\n",
1729
aState.mReflowState.reason,
1730
aState.mReflowState.availableWidth,
1731
styleText->mTextAlign);
1736
if (tryAndSkipLines) {
1737
nscoord newAvailWidth = aState.mReflowState.mComputedBorderPadding.left;
1739
if (NS_SHRINKWRAPWIDTH == aState.mReflowState.mComputedWidth) {
1740
if (NS_UNCONSTRAINEDSIZE != aState.mReflowState.mComputedMaxWidth) {
1741
newAvailWidth += aState.mReflowState.mComputedMaxWidth;
1744
newAvailWidth += aState.mReflowState.availableWidth;
1747
if (NS_UNCONSTRAINEDSIZE != aState.mReflowState.mComputedWidth) {
1748
newAvailWidth += aState.mReflowState.mComputedWidth;
1751
newAvailWidth += aState.mReflowState.availableWidth;
1754
NS_ASSERTION(NS_UNCONSTRAINEDSIZE != newAvailWidth, "bad math, newAvailWidth is infinite");
1758
IndentBy(stdout, gNoiseIndent);
1760
printf(": trying to avoid marking all lines dirty\n");
1764
for (line_iterator line = begin_lines(), line_end = end_lines();
1768
// We let child blocks make their own decisions the same
1770
if (line->IsBlock() ||
1771
line->HasPercentageChild() ||
1772
line->HasFloats() ||
1773
(line != mLines.back() && !line->HasBreak()) ||
1774
line->ResizeReflowOptimizationDisabled() ||
1775
line->IsImpactedByFloat() ||
1776
(line->mBounds.XMost() > newAvailWidth)) {
1780
#ifdef REALLY_NOISY_REFLOW
1781
if (!line->IsBlock()) {
1782
printf("PrepareResizeReflow thinks line %p is %simpacted by floats\n",
1783
line, line->IsImpactedByFloat() ? "" : "not ");
1787
if (gNoisyReflow && !line->IsDirty()) {
1788
IndentBy(stdout, gNoiseIndent + 1);
1789
printf("skipped: line=%p next=%p %s %s%s%s breakType=%d xmost=%d\n",
1790
NS_STATIC_CAST(void*, line.get()),
1791
NS_STATIC_CAST(void*, (line.next() != end_lines() ? line.next().get() : nsnull)),
1792
line->IsBlock() ? "block" : "inline",
1793
line->HasBreak() ? "has-break " : "",
1794
line->HasFloats() ? "has-floats " : "",
1795
line->IsImpactedByFloat() ? "impacted " : "",
1796
line->GetBreakType(),
1797
line->mBounds.XMost());
1803
// Mark everything dirty
1804
for (line_iterator line = begin_lines(), line_end = end_lines();
1814
//----------------------------------------
1816
nsBlockFrame::line_iterator
1817
nsBlockFrame::FindLineFor(nsIFrame* aFrame)
1819
// This assertion actually fires on lots of pages (e.g., bugzilla,
1820
// bugzilla query page), so limit it to a few people until we fix the
1821
// problem causing it. It's related to the similarly |#ifdef|ed
1822
// assertion in |PrepareChildIncrementalReflow|.
1823
#if defined(DEBUG_dbaron) || defined(DEBUG_waterson)
1824
NS_PRECONDITION(aFrame, "why pass a null frame?");
1827
line_iterator line = begin_lines(),
1828
line_end = end_lines();
1829
for ( ; line != line_end; ++line) {
1830
// If the target frame is in-flow, and this line contains the it,
1831
// then we've found our line.
1832
if (line->Contains(aFrame))
1835
// If the target frame is floated, and this line contains the
1836
// float's placeholder, then we've found our line.
1837
if (line->HasFloats()) {
1838
for (nsFloatCache *fc = line->GetFirstFloat();
1841
if (aFrame == fc->mPlaceholder->GetOutOfFlowFrame())
1850
// SEC: added GetCurrentLine() for bug 45152
1851
// we need a way for line layout to know what line is being reflowed,
1852
// but we don't want to expose the innards of nsBlockReflowState.
1854
nsBlockFrame::GetCurrentLine(nsBlockReflowState *aState, nsLineBox ** aOutCurrentLine)
1856
if (!aState || !aOutCurrentLine) return NS_ERROR_FAILURE;
1857
*aOutCurrentLine = aState->mCurrentLine;
1862
* Propagate reflow "damage" from from earlier lines to the current
1863
* line. The reflow damage comes from the following sources:
1864
* 1. The regions of float damage remembered during reflow.
1865
* 2. The combination of nonzero |aDeltaY| and any impact by a float,
1866
* either the previous reflow or now.
1868
* When entering this function, |aLine| is still at its old position and
1869
* |aDeltaY| indicates how much it will later be slid (assuming it
1870
* doesn't get marked dirty and reflowed entirely).
1873
nsBlockFrame::PropagateFloatDamage(nsBlockReflowState& aState,
1877
NS_PRECONDITION(!aLine->IsDirty(), "should never be called on dirty lines");
1879
// Check the damage region recorded in the float damage.
1880
nsSpaceManager *spaceManager = aState.mReflowState.mSpaceManager;
1881
if (spaceManager->HasFloatDamage()) {
1882
nscoord lineYA = aLine->mBounds.y + aDeltaY;
1883
nscoord lineYB = lineYA + aLine->mBounds.height;
1884
if (spaceManager->IntersectsDamage(lineYA, lineYB)) {
1891
// Cases we need to find:
1893
// 1. the line was impacted by a float and now isn't
1894
// 2. the line wasn't impacted by a float and now is
1895
// 3. the line is impacted by a float both before and after and
1896
// the float has changed position relative to the line (or it's
1897
// a different float). (XXXPerf we don't currently
1898
// check whether the float changed size. We currently just
1899
// mark blocks dirty and ignore any possibility of damage to
1900
// inlines by it being a different float with a different
1903
// XXXPerf: An optimization: if the line was and is completely
1904
// impacted by a float and the float hasn't changed size,
1905
// then we don't need to mark the line dirty.
1906
aState.GetAvailableSpace(aLine->mBounds.y + aDeltaY);
1907
PRBool wasImpactedByFloat = aLine->IsImpactedByFloat();
1908
PRBool isImpactedByFloat = aState.IsImpactedByFloat();
1909
#ifdef REALLY_NOISY_REFLOW
1910
printf("nsBlockFrame::PropagateFloatDamage %p was = %d, is=%d\n",
1911
this, wasImpactedByFloat, isImpactedByFloat);
1913
// Mark the line dirty if:
1914
// 1. It used to be impacted by a float and now isn't, or vice
1916
// 2. It is impacted by a float and it is a block, which means
1917
// that more or less of the line could be impacted than was in
1918
// the past. (XXXPerf This could be optimized further, since
1919
// we're marking the whole line dirty.)
1920
if ((wasImpactedByFloat != isImpactedByFloat) ||
1921
(isImpactedByFloat && aLine->IsBlock())) {
1927
// NOTE: The first parameter *must* be passed by value.
1929
WrappedLinesAreDirty(nsLineList::iterator aLine,
1930
const nsLineList::iterator aLineEnd)
1932
if (aLine->IsInline()) {
1933
while (aLine->IsLineWrapped()) {
1935
if (aLine == aLineEnd) {
1939
NS_ASSERTION(!aLine->IsBlock(), "didn't expect a block line");
1940
if (aLine->IsDirty()) {
1941
// we found a continuing line that is dirty
1950
static void PlaceFrameView(nsIPresContext* aPresContext, nsIFrame* aFrame);
1953
* Reflow the dirty lines
1956
nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
1958
nsresult rv = NS_OK;
1959
PRBool keepGoing = PR_TRUE;
1960
PRBool repositionViews = PR_FALSE; // should we really need this?
1964
if (aState.mReflowState.reason == eReflowReason_Incremental) {
1965
IndentBy(stdout, gNoiseIndent);
1967
printf(": incrementally reflowing dirty lines");
1969
nsHTMLReflowCommand *command = aState.mReflowState.path->mReflowCommand;
1972
command->GetType(type);
1973
printf(": type=%s(%d)", kReflowCommandType[type], type);
1977
IndentBy(stdout, gNoiseIndent);
1979
printf(": reflowing dirty lines");
1981
printf(" computedWidth=%d\n", aState.mReflowState.mComputedWidth);
1986
// Check whether we need to do invalidation for the child block
1987
PRBool doInvalidate =
1988
aState.mReflowState.reason == eReflowReason_Incremental ||
1989
aState.mReflowState.reason == eReflowReason_Dirty ||
1990
aState.mReflowState.reason == eReflowReason_Resize;
1992
// the amount by which we will slide the current line if it is not
1996
// whether we did NOT reflow the previous line and thus we need to
1997
// recompute the carried out margin before the line if we want to
1998
// reflow it or if its previous margin is dirty
1999
PRBool needToRecoverState = PR_FALSE;
2001
// Reflow the lines that are already ours
2002
line_iterator line = begin_lines(), line_end = end_lines();
2003
for ( ; line != line_end; ++line, aState.AdvanceToNextLine()) {
2006
nsRect lca(line->GetCombinedArea());
2007
IndentBy(stdout, gNoiseIndent);
2008
printf("line=%p mY=%d dirty=%s oldBounds={%d,%d,%d,%d} oldCombinedArea={%d,%d,%d,%d} deltaY=%d mPrevBottomMargin=%d\n",
2009
NS_STATIC_CAST(void*, line.get()), aState.mY,
2010
line->IsDirty() ? "yes" : "no",
2011
line->mBounds.x, line->mBounds.y,
2012
line->mBounds.width, line->mBounds.height,
2013
lca.x, lca.y, lca.width, lca.height,
2014
deltaY, aState.mPrevBottomMargin.get());
2019
// If we're supposed to update our maximum width, then we'll also need to
2020
// reflow this line if it's line wrapped and any of the continuing lines
2021
// are dirty. If we are printing (constrained height), always reflow
2023
if ((NS_UNCONSTRAINEDSIZE != aState.mReflowState.availableHeight) ||
2024
(!line->IsDirty() &&
2025
aState.GetFlag(BRS_COMPUTEMAXWIDTH) &&
2026
::WrappedLinesAreDirty(line, line_end))) {
2030
// Make sure |aState.mPrevBottomMargin| is at the correct position
2031
// before calling PropagateFloatDamage.
2032
if (needToRecoverState &&
2033
(line->IsDirty() || line->IsPreviousMarginDirty())) {
2035
// If the previous line had a 'clear' in it, we need to do the
2036
// same thing that we do in |PlaceLine|.
2038
if (line->IsInline()) {
2039
PRUint8 breakType = line->GetBreakType();
2040
if (breakType == NS_STYLE_CLEAR_LEFT ||
2041
breakType == NS_STYLE_CLEAR_RIGHT ||
2042
breakType == NS_STYLE_CLEAR_LEFT_AND_RIGHT)
2043
aState.ClearFloats(aState.mY, breakType);
2047
// We need to reconstruct the bottom margin only if we didn't
2048
// reflow the previous line and we do need to reflow (or repair
2049
// the top position of) the next line.
2050
aState.ReconstructMarginAbove(line);
2053
if (line->IsPreviousMarginDirty() && !line->IsDirty()) {
2054
// If the previous margin is dirty and we're not going to reflow
2055
// the line we need to pull out the correct top margin and set
2056
// |deltaY| correctly.
2057
// If there's float damage we might end up doing this work twice,
2059
if (line->IsBlock()) {
2060
// We could actually make this faster by stealing code from the
2061
// top of nsBlockFrame::ReflowBlockFrame, but it's an edge case
2062
// that will generally happen at most once per reflow.
2065
deltaY = aState.mY + aState.mPrevBottomMargin.get() - line->mBounds.y;
2068
line->ClearPreviousMarginDirty();
2070
// See if there's any reflow damage that requires that we mark the
2072
if (!line->IsDirty()) {
2073
PropagateFloatDamage(aState, line, deltaY);
2076
if (needToRecoverState) {
2077
needToRecoverState = PR_FALSE;
2079
// Update aState.mPrevChild as if we had reflowed all of the frames in
2080
// this line. This is expensive in some cases, since it requires
2081
// walking |GetNextSibling|.
2082
if (line->IsDirty())
2083
aState.mPrevChild = line.prev()->LastChild();
2086
// Now repair the line and update |aState.mY| by calling
2087
// |ReflowLine| or |SlideLine|.
2088
if (line->IsDirty()) {
2089
// Compute the dirty lines "before" YMost, after factoring in
2090
// the running deltaY value - the running value is implicit in
2092
nscoord oldY = line->mBounds.y;
2093
nscoord oldYMost = line->mBounds.YMost();
2095
// Reflow the dirty line. If it's an incremental reflow, then force
2096
// it to invalidate the dirty area if necessary
2097
rv = ReflowLine(aState, line, &keepGoing, doInvalidate);
2098
if (NS_FAILED(rv)) {
2102
if (0 == line->GetChildCount()) {
2103
DeleteLine(aState, line, line_end);
2107
if (oldY == 0 && deltaY != line->mBounds.y) {
2108
// This means the current line was just reflowed for the first
2109
// time. Thus we must mark the the previous margin of the next
2111
// XXXldb Move this into where we insert the line! (or will
2112
// that mess up deltaY manipulation?)
2113
if (line.next() != end_lines()) {
2114
line.next()->MarkPreviousMarginDirty();
2115
// since it's marked dirty, nobody will care about |deltaY|
2118
deltaY = line->mBounds.YMost() - oldYMost;
2122
SlideLine(aState, line, deltaY);
2124
repositionViews = PR_TRUE;
2126
// XXX EVIL O(N^2) EVIL
2127
aState.RecoverStateFrom(line, deltaY);
2129
// Keep mY up to date in case we're propagating reflow damage.
2130
aState.mY = line->mBounds.YMost();
2131
needToRecoverState = PR_TRUE;
2137
nsRect lca(line->GetCombinedArea());
2138
IndentBy(stdout, gNoiseIndent);
2139
printf("line=%p mY=%d newBounds={%d,%d,%d,%d} newCombinedArea={%d,%d,%d,%d} deltaY=%d mPrevBottomMargin=%d\n",
2140
NS_STATIC_CAST(void*, line.get()), aState.mY,
2141
line->mBounds.x, line->mBounds.y,
2142
line->mBounds.width, line->mBounds.height,
2143
lca.x, lca.y, lca.width, lca.height,
2144
deltaY, aState.mPrevBottomMargin.get());
2149
if (needToRecoverState) {
2150
// Is this expensive?
2151
aState.ReconstructMarginAbove(line);
2153
// Update aState.mPrevChild as if we had reflowed all of the frames in
2154
// this line. This is expensive in some cases, since it requires
2155
// walking |GetNextSibling|.
2156
aState.mPrevChild = line.prev()->LastChild();
2159
// Should we really have to do this?
2160
if (repositionViews)
2161
::PlaceFrameView(aState.mPresContext, this);
2163
// Pull data from a next-in-flow if there's still room for more
2165
while (keepGoing && (nsnull != aState.mNextInFlow)) {
2166
// Grab first line from our next-in-flow
2167
nsBlockFrame* nextInFlow = aState.mNextInFlow;
2168
line_iterator nifLine = nextInFlow->begin_lines();
2169
if (nifLine == nextInFlow->end_lines()) {
2170
aState.mNextInFlow = (nsBlockFrame*) aState.mNextInFlow->mNextInFlow;
2173
// XXX See if the line is not dirty; if it's not maybe we can
2174
// avoid the pullup if it can't fit?
2175
nsLineBox *toMove = nifLine;
2176
nextInFlow->mLines.erase(nifLine);
2177
if (0 == toMove->GetChildCount()) {
2178
// The line is empty. Try the next one.
2179
NS_ASSERTION(nsnull == toMove->mFirstChild, "bad empty line");
2180
aState.FreeLineBox(toMove);
2184
// XXX move to a subroutine: run-in, overflow, pullframe and this do this
2185
// Make the children in the line ours.
2186
nsIFrame* frame = toMove->mFirstChild;
2187
nsIFrame* lastFrame = nsnull;
2188
PRInt32 n = toMove->GetChildCount();
2190
frame->SetParent(this);
2191
// When pushing and pulling frames we need to check for whether any
2192
// views need to be reparented
2193
nsHTMLContainerFrame::ReparentFrameView(aState.mPresContext, frame, mNextInFlow, this);
2195
frame = frame->GetNextSibling();
2197
lastFrame->SetNextSibling(nsnull);
2199
// Add line to our line list
2200
if (aState.mPrevChild)
2201
aState.mPrevChild->SetNextSibling(toMove->mFirstChild);
2202
line = mLines.before_insert(end_lines(), toMove);
2204
// If line contains floats, remove them from aState.mNextInFlow's
2205
// float list. They will be pushed onto this blockframe's float
2206
// list, via BuildFloatList(), when we are done reflowing dirty lines.
2208
// XXX: If the call to BuildFloatList() is removed from
2209
// nsBlockFrame::Reflow(), we'll probably need to manually
2210
// append the floats to |this|'s float list.
2212
if (line->HasFloats()) {
2213
nsFloatCache* fc = line->GetFirstFloat();
2215
if (fc->mPlaceholder) {
2216
nsIFrame* floatFrame = fc->mPlaceholder->GetOutOfFlowFrame();
2218
aState.mNextInFlow->mFloats.RemoveFrame(floatFrame);
2224
// Now reflow it and any lines that it makes during it's reflow
2225
// (we have to loop here because reflowing the line may case a new
2226
// line to be created; see SplitLine's callers for examples of
2227
// when this happens).
2228
while (line != end_lines()) {
2229
rv = ReflowLine(aState, line, &keepGoing, doInvalidate);
2230
if (NS_FAILED(rv)) {
2234
if (0 == line->GetChildCount()) {
2235
DeleteLine(aState, line, line_end);
2240
// If this is an inline frame then its time to stop
2242
aState.AdvanceToNextLine();
2246
// Handle an odd-ball case: a list-item with no lines
2247
if (mBullet && HaveOutsideBullet() && mLines.empty()) {
2248
nsHTMLReflowMetrics metrics(nsnull);
2249
ReflowBullet(aState, metrics);
2251
// There are no lines so we have to fake up some y motion so that
2252
// we end up with *some* height.
2253
aState.mY += metrics.height;
2259
IndentBy(stdout, gNoiseIndent);
2261
printf(": done reflowing dirty lines (status=%x)\n",
2262
aState.mReflowStatus);
2270
nsBlockFrame::DeleteLine(nsBlockReflowState& aState,
2271
nsLineList::iterator aLine,
2272
nsLineList::iterator aLineEnd)
2274
NS_PRECONDITION(0 == aLine->GetChildCount(), "can't delete !empty line");
2275
if (0 == aLine->GetChildCount()) {
2276
NS_ASSERTION(aState.mCurrentLine == aLine,
2277
"using function more generally than designed, "
2278
"but perhaps OK now");
2279
nsLineBox *line = aLine;
2280
aLine = mLines.erase(aLine);
2281
aState.FreeLineBox(line);
2282
// Mark the previous margin of the next line dirty since we need to
2283
// recompute its top position.
2284
if (aLine != aLineEnd)
2285
aLine->MarkPreviousMarginDirty();
2290
* Takes two rectangles whose origins must be the same, and computes
2291
* the difference between their union and their intersection as two
2292
* rectangles. (This difference is a superset of the difference
2293
* between the two rectangles.)
2295
static void GetRectDifferenceStrips(const nsRect& aR1, const nsRect& aR2,
2296
nsRect* aHStrip, nsRect* aVStrip) {
2297
NS_ASSERTION(aR1.TopLeft() == aR2.TopLeft(),
2298
"expected rects at the same position");
2299
nsRect unionRect(aR1.x, aR1.y, PR_MAX(aR1.width, aR2.width),
2300
PR_MAX(aR1.height, aR2.height));
2301
nscoord VStripStart = PR_MIN(aR1.width, aR2.width);
2302
nscoord HStripStart = PR_MIN(aR1.height, aR2.height);
2303
*aVStrip = unionRect;
2304
aVStrip->x += VStripStart;
2305
aVStrip->width -= VStripStart;
2306
*aHStrip = unionRect;
2307
aHStrip->y += HStripStart;
2308
aHStrip->height -= HStripStart;
2312
* Reflow a line. The line will either contain a single block frame
2313
* or contain 1 or more inline frames. aKeepReflowGoing indicates
2314
* whether or not the caller should continue to reflow more lines.
2317
nsBlockFrame::ReflowLine(nsBlockReflowState& aState,
2318
line_iterator aLine,
2319
PRBool* aKeepReflowGoing,
2320
PRBool aDamageDirtyArea)
2322
nsresult rv = NS_OK;
2324
NS_ABORT_IF_FALSE(aLine->GetChildCount(), "reflowing empty line");
2326
// Setup the line-layout for the new line
2327
aState.mCurrentLine = aLine;
2328
aLine->ClearDirty();
2330
// Now that we know what kind of line we have, reflow it
2331
if (aLine->IsBlock()) {
2332
nsRect oldBounds = aLine->mFirstChild->GetRect();
2333
nsRect oldCombinedArea(aLine->GetCombinedArea());
2334
rv = ReflowBlockFrame(aState, aLine, aKeepReflowGoing);
2335
nsRect newBounds = aLine->mFirstChild->GetRect();
2337
// We expect blocks to damage any area inside their bounds that is
2338
// dirty; however, if the frame changes size or position then we
2339
// need to do some repainting.
2340
// XXX roc --- the above statement is ambiguous about whether 'bounds'
2341
// means the frame's bounds or overflowArea, and in fact this is a source
2342
// of much confusion and bugs. Thus the following hack considers *both*
2343
// overflowArea and bounds. This should be considered a temporary hack
2344
// until we decide how it's really supposed to work.
2345
if (aDamageDirtyArea) {
2346
nsRect lineCombinedArea(aLine->GetCombinedArea());
2347
if (oldCombinedArea.TopLeft() != lineCombinedArea.TopLeft() ||
2348
oldBounds.TopLeft() != newBounds.TopLeft()) {
2349
// The block has moved, and so to be safe we need to repaint
2350
// XXX We need to improve on this...
2352
dirtyRect.UnionRect(oldCombinedArea, lineCombinedArea);
2353
#ifdef NOISY_BLOCK_INVALIDATE
2354
printf("%p invalidate 6 (%d, %d, %d, %d)\n",
2355
this, dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height);
2357
Invalidate(dirtyRect);
2359
nsRect combinedAreaHStrip, combinedAreaVStrip;
2360
nsRect boundsHStrip, boundsVStrip;
2361
GetRectDifferenceStrips(oldBounds, newBounds,
2362
&boundsHStrip, &boundsVStrip);
2363
GetRectDifferenceStrips(oldCombinedArea, lineCombinedArea,
2364
&combinedAreaHStrip, &combinedAreaVStrip);
2366
#ifdef NOISY_BLOCK_INVALIDATE
2367
printf("%p invalidate boundsVStrip (%d, %d, %d, %d)\n",
2368
this, boundsVStrip.x, boundsVStrip.y, boundsVStrip.width, boundsVStrip.height);
2369
printf("%p invalidate boundsHStrip (%d, %d, %d, %d)\n",
2370
this, boundsHStrip.x, boundsHStrip.y, boundsHStrip.width, boundsHStrip.height);
2371
printf("%p invalidate combinedAreaVStrip (%d, %d, %d, %d)\n",
2372
this, combinedAreaVStrip.x, combinedAreaVStrip.y, combinedAreaVStrip.width, combinedAreaVStrip.height);
2373
printf("%p invalidate combinedAreaHStrip (%d, %d, %d, %d)\n",
2374
this, combinedAreaHStrip.x, combinedAreaHStrip.y, combinedAreaHStrip.width, combinedAreaHStrip.height);
2376
// The first thing Invalidate does is check if the rect is empty, so
2377
// don't bother doing that here.
2378
Invalidate(boundsVStrip);
2379
Invalidate(boundsHStrip);
2380
Invalidate(combinedAreaVStrip);
2381
Invalidate(combinedAreaHStrip);
2386
nsRect oldCombinedArea(aLine->GetCombinedArea());
2387
aLine->SetLineWrapped(PR_FALSE);
2389
// If we're supposed to update the maximum width, then we'll need to reflow
2390
// the line with an unconstrained width (which will give us the new maximum
2391
// width), then we'll reflow it again with the constrained width.
2392
// We only do this if this is a beginning line, i.e., don't do this for
2393
// lines associated with content that line wrapped (see ReflowDirtyLines()
2395
// XXX This approach doesn't work when floats are involved in which case
2396
// we'll either need to recover the float state that applies to the
2397
// unconstrained reflow or keep it around in a separate space manager...
2398
PRBool isBeginningLine = aState.mCurrentLine == begin_lines() ||
2399
!aState.mCurrentLine.prev()->IsLineWrapped();
2400
// XXXldb Add &&!aState.GetFlag(BRS_UNCONSTRAINEDWIDTH)
2401
if (aState.GetFlag(BRS_COMPUTEMAXWIDTH) && isBeginningLine) {
2402
// First reflow the line with an unconstrained width.
2403
nscoord oldY = aState.mY;
2404
nsCollapsingMargin oldPrevBottomMargin(aState.mPrevBottomMargin);
2405
PRBool oldUnconstrainedWidth = aState.GetFlag(BRS_UNCONSTRAINEDWIDTH);
2407
#if defined(DEBUG_waterson) || defined(DEBUG_dbaron)
2408
// XXXwaterson if oldUnconstrainedWidth was set, why do we need
2409
// to do the second reflow, below?
2411
if (oldUnconstrainedWidth)
2412
printf("*** oldUnconstrainedWidth was already set.\n"
2413
"*** This code (%s:%d) could be optimized a lot!\n"
2414
"+++ possibly doing an unnecessary second-pass unconstrained "
2416
__FILE__, __LINE__);
2419
// When doing this we need to set the block reflow state's
2420
// "mUnconstrainedWidth" variable to PR_TRUE so if we encounter
2421
// a placeholder and then reflow its associated float we don't
2422
// end up resetting the line's right edge and have it think the
2423
// width is unconstrained...
2424
aState.mSpaceManager->PushState();
2425
aState.SetFlag(BRS_UNCONSTRAINEDWIDTH, PR_TRUE);
2426
ReflowInlineFrames(aState, aLine, aKeepReflowGoing, aDamageDirtyArea, PR_TRUE);
2428
aState.mPrevBottomMargin = oldPrevBottomMargin;
2429
aState.SetFlag(BRS_UNCONSTRAINEDWIDTH, oldUnconstrainedWidth);
2430
aState.mSpaceManager->PopState();
2432
// Update the line's maximum width
2433
aLine->mMaximumWidth = aLine->mBounds.XMost();
2434
#ifdef NOISY_MAXIMUM_WIDTH
2435
printf("nsBlockFrame::ReflowLine block %p line %p setting aLine.mMaximumWidth to %d\n",
2436
this, NS_STATIC_CAST(void*, aLine.get()), aLine->mMaximumWidth);
2438
aState.UpdateMaximumWidth(aLine->mMaximumWidth);
2440
// Now reflow the line again this time without having it compute
2441
// the maximum width or max-element-width.
2442
// Note: we need to reset both member variables, because the inline
2443
// code examines mComputeMaxElementWidth and if there is a placeholder
2444
// on this line the code to reflow the float looks at both...
2445
nscoord oldComputeMaxElementWidth = aState.GetFlag(BRS_COMPUTEMAXELEMENTWIDTH);
2446
nscoord oldComputeMaximumWidth = aState.GetFlag(BRS_COMPUTEMAXWIDTH);
2448
aState.SetFlag(BRS_COMPUTEMAXELEMENTWIDTH, PR_FALSE);
2449
aState.SetFlag(BRS_COMPUTEMAXWIDTH, PR_FALSE);
2450
rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing, aDamageDirtyArea);
2451
aState.SetFlag(BRS_COMPUTEMAXELEMENTWIDTH, oldComputeMaxElementWidth);
2452
aState.SetFlag(BRS_COMPUTEMAXWIDTH, oldComputeMaximumWidth);
2455
rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing, aDamageDirtyArea);
2456
if (NS_SUCCEEDED(rv))
2458
if (aState.GetFlag(BRS_COMPUTEMAXWIDTH))
2460
#ifdef NOISY_MAXIMUM_WIDTH
2461
printf("nsBlockFrame::ReflowLine block %p line %p setting aLine.mMaximumWidth to %d\n",
2462
this, NS_STATIC_CAST(void*, aLine.get()), aLine->mMaximumWidth);
2464
aState.UpdateMaximumWidth(aLine->mMaximumWidth);
2466
if (aState.GetFlag(BRS_COMPUTEMAXELEMENTWIDTH))
2469
if (gNoisyMaxElementWidth) {
2470
IndentBy(stdout, gNoiseIndent);
2471
printf("nsBlockFrame::ReflowLine block %p line %p setting aLine.mMaxElementWidth to %d\n",
2472
NS_STATIC_CAST(void*, this), NS_STATIC_CAST(void*, aLine.get()),
2473
aLine->mMaxElementWidth);
2476
aState.UpdateMaxElementWidth(aLine->mMaxElementWidth);
2481
// We don't really know what changed in the line, so use the union
2482
// of the old and new combined areas
2483
// SEC: added "aLine->IsForceInvalidate()" for bug 45152
2484
if (aDamageDirtyArea || aLine->IsForceInvalidate()) {
2485
aLine->SetForceInvalidate(PR_FALSE); // doing the invalidate now, force flag to off
2488
dirtyRect.UnionRect(oldCombinedArea, aLine->GetCombinedArea());
2489
#ifdef NOISY_BLOCK_INVALIDATE
2490
printf("%p invalidate because %s is true (%d, %d, %d, %d)\n",
2491
this, aDamageDirtyArea ? "aDamageDirtyArea" : "aLine->IsForceInvalidate",
2492
dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height);
2493
if (aLine->IsForceInvalidate())
2494
printf(" dirty line is %p\n", NS_STATIC_CAST(void*, aLine.get());
2496
Invalidate(dirtyRect);
2504
* Pull frame from the next available location (one of our lines or
2505
* one of our next-in-flows lines).
2508
nsBlockFrame::PullFrame(nsBlockReflowState& aState,
2509
line_iterator aLine,
2510
PRBool aDamageDeletedLines,
2511
nsIFrame*& aFrameResult)
2513
aFrameResult = nsnull;
2515
// First check our remaining lines
2516
if (end_lines() != aLine.next()) {
2517
return PullFrameFrom(aState, aLine, mLines, aLine.next(), PR_FALSE,
2518
aDamageDeletedLines, aFrameResult);
2521
// Pull frames from the next-in-flow(s) until we can't
2522
nsBlockFrame* nextInFlow = aState.mNextInFlow;
2523
while (nsnull != nextInFlow) {
2524
if (! nextInFlow->mLines.empty()) {
2525
return PullFrameFrom(aState, aLine, nextInFlow->mLines,
2526
nextInFlow->mLines.begin(), PR_TRUE,
2527
aDamageDeletedLines, aFrameResult);
2530
nextInFlow = (nsBlockFrame*) nextInFlow->mNextInFlow;
2531
aState.mNextInFlow = nextInFlow;
2538
* Try to pull a frame out of a line pointed at by aFromLine. If a frame
2539
* is pulled then aPulled will be set to PR_TRUE. In addition, if
2540
* aUpdateGeometricParent is set then the pulled frames geometric parent
2541
* will be updated (e.g. when pulling from a next-in-flows line list).
2543
* Note: pulling a frame from a line that is a place-holder frame
2544
* doesn't automatically remove the corresponding float from the
2545
* line's float array. This happens indirectly: either the line gets
2546
* emptied (and destroyed) or the line gets reflowed (because we mark
2547
* it dirty) and the code at the top of ReflowLine empties the
2548
* array. So eventually, it will be removed, just not right away.
2551
nsBlockFrame::PullFrameFrom(nsBlockReflowState& aState,
2553
nsLineList& aFromContainer,
2554
nsLineList::iterator aFromLine,
2555
PRBool aUpdateGeometricParent,
2556
PRBool aDamageDeletedLines,
2557
nsIFrame*& aFrameResult)
2559
nsLineBox* fromLine = aFromLine;
2560
NS_ABORT_IF_FALSE(fromLine, "bad line to pull from");
2561
NS_ABORT_IF_FALSE(fromLine->GetChildCount(), "empty line");
2562
NS_ABORT_IF_FALSE(aLine->GetChildCount(), "empty line");
2564
if (fromLine->IsBlock()) {
2565
// If our line is not empty and the child in aFromLine is a block
2566
// then we cannot pull up the frame into this line. In this case
2568
aFrameResult = nsnull;
2571
// Take frame from fromLine
2572
nsIFrame* frame = fromLine->mFirstChild;
2573
aLine->SetChildCount(aLine->GetChildCount() + 1);
2575
PRInt32 fromLineChildCount = fromLine->GetChildCount();
2576
if (0 != --fromLineChildCount) {
2577
// Mark line dirty now that we pulled a child
2578
fromLine->SetChildCount(fromLineChildCount);
2579
fromLine->MarkDirty();
2580
fromLine->mFirstChild = frame->GetNextSibling();
2583
// Free up the fromLine now that it's empty
2584
// Its bounds might need to be redrawn, though.
2585
// XXX WHY do we invalidate the bounds AND the combined area? doesn't
2586
// the combined area always enclose the bounds?
2587
if (aDamageDeletedLines) {
2588
Invalidate(fromLine->mBounds);
2590
if (aFromLine.next() != end_lines())
2591
aFromLine.next()->MarkPreviousMarginDirty();
2593
Invalidate(fromLine->GetCombinedArea());
2594
aFromContainer.erase(aFromLine);
2595
aState.FreeLineBox(fromLine);
2598
// Change geometric parents
2599
if (aUpdateGeometricParent) {
2600
// Before we set the new parent frame get the current parent
2601
nsIFrame* oldParentFrame = frame->GetParent();
2602
frame->SetParent(this);
2604
// When pushing and pulling frames we need to check for whether any
2605
// views need to be reparented
2606
NS_ASSERTION(oldParentFrame != this, "unexpected parent frame");
2607
nsHTMLContainerFrame::ReparentFrameView(aState.mPresContext, frame, oldParentFrame, this);
2609
// The frame is being pulled from a next-in-flow; therefore we
2610
// need to add it to our sibling list.
2611
if (nsnull != aState.mPrevChild) {
2612
aState.mPrevChild->SetNextSibling(frame);
2614
frame->SetNextSibling(nsnull);
2617
// Stop pulling because we found a frame to pull
2618
aFrameResult = frame;
2620
VerifyLines(PR_TRUE);
2627
PlaceFrameView(nsIPresContext* aPresContext,
2630
if (aFrame->HasView())
2631
nsContainerFrame::PositionFrameView(aPresContext, aFrame);
2633
nsContainerFrame::PositionChildViews(aPresContext, aFrame);
2637
nsBlockFrame::SlideLine(nsBlockReflowState& aState,
2638
nsLineBox* aLine, nscoord aDY)
2640
NS_PRECONDITION(aDY != 0, "why slide a line nowhere?");
2642
Invalidate(aLine->GetCombinedArea());
2643
// Adjust line state
2644
aLine->SlideBy(aDY);
2645
Invalidate(aLine->GetCombinedArea());
2647
// Adjust the frames in the line
2648
nsIFrame* kid = aLine->mFirstChild;
2653
if (aLine->IsBlock()) {
2655
nsPoint p = kid->GetPosition();
2657
kid->SetPosition(p);
2660
// Make sure the frame's view and any child views are updated
2661
::PlaceFrameView(aState.mPresContext, kid);
2664
// Adjust the Y coordinate of the frames in the line.
2665
// Note: we need to re-position views even if aDY is 0, because
2666
// one of our parent frames may have moved and so the view's position
2667
// relative to its parent may have changed
2668
PRInt32 n = aLine->GetChildCount();
2671
nsPoint p = kid->GetPosition();
2673
kid->SetPosition(p);
2675
// Make sure the frame's view and any child views are updated
2676
::PlaceFrameView(aState.mPresContext, kid);
2677
kid = kid->GetNextSibling();
2683
nsBlockFrame::AttributeChanged(nsIPresContext* aPresContext,
2685
PRInt32 aNameSpaceID,
2686
nsIAtom* aAttribute,
2689
nsresult rv = nsBlockFrameSuper::AttributeChanged(aPresContext, aChild,
2690
aNameSpaceID, aAttribute,
2693
if (NS_FAILED(rv)) {
2696
if (nsHTMLAtoms::start == aAttribute) {
2697
// XXX Not sure if this is necessary anymore
2698
RenumberLists(aPresContext);
2700
nsHTMLReflowCommand* reflowCmd;
2701
rv = NS_NewHTMLReflowCommand(&reflowCmd, this,
2702
eReflowType_ContentChanged,
2705
if (NS_SUCCEEDED(rv))
2706
aPresContext->PresShell()->AppendReflowCommand(reflowCmd);
2708
else if (nsHTMLAtoms::value == aAttribute) {
2709
const nsStyleDisplay* styleDisplay = GetStyleDisplay();
2710
if (NS_STYLE_DISPLAY_LIST_ITEM == styleDisplay->mDisplay) {
2711
nsIFrame* nextAncestor = mParent;
2712
nsBlockFrame* blockParent = nsnull;
2714
// Search for the closest ancestor that's a block frame. We
2715
// make the assumption that all related list items share a
2716
// common block parent.
2717
// XXXldb I think that's a bad assumption.
2718
while (nextAncestor) {
2719
if (NS_OK == nextAncestor->QueryInterface(kBlockFrameCID,
2720
(void**)&blockParent)) {
2723
nextAncestor = nextAncestor->GetParent();
2726
// Tell the enclosing block frame to renumber list items within
2728
if (nsnull != blockParent) {
2729
// XXX Not sure if this is necessary anymore
2730
blockParent->RenumberLists(aPresContext);
2732
nsHTMLReflowCommand* reflowCmd;
2733
rv = NS_NewHTMLReflowCommand(&reflowCmd, blockParent,
2734
eReflowType_ContentChanged,
2737
if (NS_SUCCEEDED(rv))
2738
aPresContext->PresShell()->AppendReflowCommand(reflowCmd);
2747
IsBorderZero(nsStyleUnit aUnit, nsStyleCoord &aCoord)
2749
return ((aUnit == eStyleUnit_Coord && aCoord.GetCoordValue() == 0));
2753
IsPaddingZero(nsStyleUnit aUnit, nsStyleCoord &aCoord)
2755
return (aUnit == eStyleUnit_Null ||
2756
(aUnit == eStyleUnit_Coord && aCoord.GetCoordValue() == 0) ||
2757
(aUnit == eStyleUnit_Percent && aCoord.GetPercentValue() == 0.0));
2761
IsMarginZero(nsStyleUnit aUnit, nsStyleCoord &aCoord)
2763
return (aUnit == eStyleUnit_Null ||
2764
aUnit == eStyleUnit_Auto ||
2765
(aUnit == eStyleUnit_Coord && aCoord.GetCoordValue() == 0) ||
2766
(aUnit == eStyleUnit_Percent && aCoord.GetPercentValue() == 0.0));
2769
/* virtual */ PRBool
2770
nsBlockFrame::IsEmpty()
2772
const nsStylePosition* position = GetStylePosition();
2774
switch (position->mMinHeight.GetUnit()) {
2775
case eStyleUnit_Coord:
2776
if (position->mMinHeight.GetCoordValue() != 0)
2779
case eStyleUnit_Percent:
2780
if (position->mMinHeight.GetPercentValue() != 0.0f)
2787
switch (position->mHeight.GetUnit()) {
2788
case eStyleUnit_Auto:
2790
case eStyleUnit_Coord:
2791
if (position->mHeight.GetCoordValue() != 0)
2794
case eStyleUnit_Percent:
2795
if (position->mHeight.GetPercentValue() != 0.0f)
2802
const nsStyleBorder* border = GetStyleBorder();
2803
const nsStylePadding* padding = GetStylePadding();
2805
if ((border->IsBorderSideVisible(NS_SIDE_TOP) &&
2806
!IsBorderZero(border->mBorder.GetTopUnit(),
2807
border->mBorder.GetTop(coord))) ||
2808
(border->IsBorderSideVisible(NS_SIDE_BOTTOM) &&
2809
!IsBorderZero(border->mBorder.GetBottomUnit(),
2810
border->mBorder.GetBottom(coord))) ||
2811
!IsPaddingZero(padding->mPadding.GetTopUnit(),
2812
padding->mPadding.GetTop(coord)) ||
2813
!IsPaddingZero(padding->mPadding.GetBottomUnit(),
2814
padding->mPadding.GetBottom(coord))) {
2818
for (line_iterator line = begin_lines(), line_end = end_lines();
2822
if (!line->IsEmpty())
2829
nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState,
2832
if (aState.GetFlag(BRS_APPLYTOPMARGIN)) {
2833
// Apply short-circuit check to avoid searching the line list
2837
if (!aState.IsAdjacentWithTop()) {
2838
// If we aren't at the top Y coordinate then something of non-zero
2839
// height must have been placed. Therefore the childs top-margin
2841
aState.SetFlag(BRS_APPLYTOPMARGIN, PR_TRUE);
2845
// Determine if this line is "essentially" the first line
2846
for (line_iterator line = begin_lines(); line != aLine; ++line) {
2847
if (!line->IsEmpty()) {
2848
// A line which preceeds aLine is non-empty, so therefore the
2849
// top margin applies.
2850
aState.SetFlag(BRS_APPLYTOPMARGIN, PR_TRUE);
2853
// No need to apply the top margin if the line has floats. We
2854
// should collapse anyway (bug 44419)
2857
// The line being reflowed is "essentially" the first line in the
2858
// block. Therefore its top-margin will be collapsed by the
2859
// generational collapsing logic with its parent (us).
2864
nsBlockFrame::GetTopBlockChild(nsIPresContext* aPresContext)
2869
nsLineBox *firstLine = mLines.front();
2870
if (firstLine->IsBlock())
2871
return firstLine->mFirstChild;
2873
if (!firstLine->IsEmpty())
2876
line_iterator secondLine = begin_lines();
2878
if (secondLine == end_lines() || !secondLine->IsBlock())
2881
return secondLine->mFirstChild;
2884
// If placeholders/floats split during reflowing a line, but that line will
2885
// be put on the next page, then put the placeholders/floats back the way
2886
// they were before the line was reflowed.
2888
nsBlockFrame::UndoSplitPlaceholders(nsBlockReflowState& aState,
2889
nsIFrame* aLastPlaceholder)
2891
nsIFrame* undoPlaceholder = nsnull;
2892
if (aLastPlaceholder) {
2893
undoPlaceholder = aLastPlaceholder->GetNextSibling();
2894
aLastPlaceholder->SetNextSibling(nsnull);
2897
// just remove the property
2898
nsFrameList* overflowPlace = GetOverflowPlaceholders(aState.mPresContext, PR_TRUE);
2899
delete overflowPlace;
2901
// remove the next in flows of the placeholders that need to be removed
2902
for (nsIFrame* placeholder = undoPlaceholder; placeholder; ) {
2903
nsSplittableFrame::RemoveFromFlow(placeholder);
2904
nsIFrame* savePlaceholder = placeholder;
2905
placeholder = placeholder->GetNextSibling();
2906
savePlaceholder->Destroy(aState.mPresContext);
2910
// Combine aNewBreakType with aOrigBreakType, but limit the break types
2911
// to NS_STYLE_CLEAR_LEFT, RIGHT, LEFT_AND_RIGHT. When there is a <BR> right
2912
// after a float and the float splits, then the <BR>'s break type is combined
2913
// with the break type of the frame right after the floats next-in-flow.
2915
CombineBreakType(PRUint8 aOrigBreakType,
2916
PRUint8 aNewBreakType)
2918
PRUint8 breakType = aOrigBreakType;
2920
case NS_STYLE_CLEAR_LEFT:
2921
if ((NS_STYLE_CLEAR_RIGHT == aNewBreakType) ||
2922
(NS_STYLE_CLEAR_LEFT_AND_RIGHT == aNewBreakType)) {
2923
breakType = NS_STYLE_CLEAR_LEFT_AND_RIGHT;
2926
case NS_STYLE_CLEAR_RIGHT:
2927
if ((NS_STYLE_CLEAR_LEFT == aNewBreakType) ||
2928
(NS_STYLE_CLEAR_LEFT_AND_RIGHT == aNewBreakType)) {
2929
breakType = NS_STYLE_CLEAR_LEFT_AND_RIGHT;
2932
case NS_STYLE_CLEAR_NONE:
2933
if ((NS_STYLE_CLEAR_LEFT == aNewBreakType) ||
2934
(NS_STYLE_CLEAR_RIGHT == aNewBreakType) ||
2935
(NS_STYLE_CLEAR_LEFT_AND_RIGHT == aNewBreakType)) {
2936
breakType = NS_STYLE_CLEAR_LEFT_AND_RIGHT;
2943
nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
2944
line_iterator aLine,
2945
PRBool* aKeepReflowGoing)
2947
NS_PRECONDITION(*aKeepReflowGoing, "bad caller");
2949
nsresult rv = NS_OK;
2951
nsIFrame* frame = aLine->mFirstChild;
2953
NS_ASSERTION(PR_FALSE, "program error - unexpected empty line");
2954
return NS_ERROR_NULL_POINTER;
2957
// Prepare the block reflow engine
2958
const nsStyleDisplay* display = frame->GetStyleDisplay();
2959
nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState,
2960
aState.GetFlag(BRS_COMPUTEMAXELEMENTWIDTH),
2961
aState.GetFlag(BRS_COMPUTEMAXWIDTH));
2963
// See if we should apply the top margin. If the block frame being
2964
// reflowed is a continuation (non-null prev-in-flow) then we don't
2965
// apply its top margin because its not significant. Otherwise, dig
2967
PRBool applyTopMargin = PR_FALSE;
2968
nsIFrame* framePrevInFlow;
2969
frame->GetPrevInFlow(&framePrevInFlow);
2970
if (nsnull == framePrevInFlow) {
2971
applyTopMargin = ShouldApplyTopMargin(aState, aLine);
2974
PRUint8 breakType = display->mBreakType;
2975
// If a float split and its prev-in-flow was followed by a <BR>, then combine
2976
// the <BR>'s break type with the block's break type (the block will be the very
2977
// next frame after the split float).
2978
if (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType) {
2979
breakType = ::CombineBreakType(breakType, aState.mFloatBreakType);
2980
aState.mFloatBreakType = NS_STYLE_CLEAR_NONE;
2982
// Clear past floats before the block if the clear style is not none
2983
aLine->SetBreakType(breakType);
2984
if (NS_STYLE_CLEAR_NONE != breakType) {
2985
PRBool alsoApplyTopMargin = aState.ClearPastFloats(breakType);
2986
if (alsoApplyTopMargin) {
2987
applyTopMargin = PR_TRUE;
2989
#ifdef NOISY_VERTICAL_MARGINS
2991
printf(": y=%d child ", aState.mY);
2992
ListTag(stdout, frame);
2993
printf(" has clear of %d => %s, mPrevBottomMargin=%d\n",
2995
applyTopMargin ? "applyTopMargin" : "nope",
2996
aState.mPrevBottomMargin);
3000
nscoord topMargin = 0;
3001
if (applyTopMargin) {
3002
// Precompute the blocks top margin value so that we can get the
3003
// correct available space (there might be a float that's
3004
// already been placed below the aState.mPrevBottomMargin
3006
// Setup a reflowState to get the style computed margin-top
3007
// value. We'll use a reason of `resize' so that we don't fudge
3008
// any incremental reflow state.
3010
// The availSpace here is irrelevant to our needs - all we want
3011
// out if this setup is the margin-top value which doesn't depend
3012
// on the childs available space.
3013
nsSize availSpace(aState.mContentArea.width, NS_UNCONSTRAINEDSIZE);
3014
nsHTMLReflowState reflowState(aState.mPresContext, aState.mReflowState,
3015
frame, availSpace, eReflowReason_Resize);
3017
// Now compute the collapsed margin-top value into aState.mPrevBottomMargin
3018
nsCollapsingMargin oldPrevBottomMargin = aState.mPrevBottomMargin;
3019
nsBlockReflowContext::ComputeCollapsedTopMargin(aState.mPresContext,
3021
aState.mPrevBottomMargin);
3022
topMargin = aState.mPrevBottomMargin.get();
3023
aState.mPrevBottomMargin = oldPrevBottomMargin; // perhaps not needed
3025
// Temporarily advance the running Y value so that the
3026
// GetAvailableSpace method will return the right available
3027
// space. This undone as soon as the margin is computed.
3028
aState.mY += topMargin;
3031
// Compute the available space for the block
3032
aState.GetAvailableSpace();
3033
#ifdef REALLY_NOISY_REFLOW
3034
printf("setting line %p isImpacted to %s\n", aLine, aState.IsImpactedByFloat()?"true":"false");
3036
PRBool isImpacted = aState.IsImpactedByFloat() ? PR_TRUE : PR_FALSE;
3037
aLine->SetLineIsImpactedByFloat(isImpacted);
3038
nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE;
3039
frame->IsSplittable(splitType);
3041
aState.ComputeBlockAvailSpace(frame, splitType, display, availSpace);
3043
// Now put the Y coordinate back and flow the block letting the
3044
// block reflow context compute the same top margin value we just
3047
aState.mY -= topMargin;
3048
availSpace.y -= topMargin;
3049
if (NS_UNCONSTRAINEDSIZE != availSpace.height) {
3050
availSpace.height += topMargin;
3054
// keep track of the last overflow float in case we need to undo any new additions
3055
nsFrameList* overflowPlace = GetOverflowPlaceholders(aState.mPresContext, PR_FALSE);
3056
nsIFrame* lastPlaceholder = (overflowPlace) ? overflowPlace->LastChild() : nsnull;
3058
// Reflow the block into the available space
3059
nsReflowStatus frameReflowStatus=NS_FRAME_COMPLETE;
3060
nsMargin computedOffsets;
3061
// construct the html reflow state for the block. ReflowBlock
3062
// will initialize it and set its reason.
3063
nsHTMLReflowState blockHtmlRS(aState.mPresContext, aState.mReflowState, frame,
3064
nsSize(availSpace.width, availSpace.height),
3065
aState.mReflowState.reason, PR_FALSE);
3066
rv = brc.ReflowBlock(availSpace, applyTopMargin, aState.mPrevBottomMargin,
3067
aState.IsAdjacentWithTop(), computedOffsets,
3068
blockHtmlRS, frameReflowStatus);
3070
// Remove the frame from the reflow tree.
3071
if (aState.mReflowState.path)
3072
aState.mReflowState.path->RemoveChild(frame);
3074
if (NS_FAILED(rv)) {
3077
aState.mPrevChild = frame;
3079
#if defined(REFLOW_STATUS_COVERAGE)
3080
RecordReflowStatus(PR_TRUE, frameReflowStatus);
3083
if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus)) {
3084
// None of the child block fits.
3085
UndoSplitPlaceholders(aState, lastPlaceholder);
3086
PushLines(aState, aLine.prev());
3087
*aKeepReflowGoing = PR_FALSE;
3088
aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
3091
// Note: line-break-after a block is a nop
3093
// Try to place the child block
3094
PRBool isAdjacentWithTop = aState.IsAdjacentWithTop();
3095
nsCollapsingMargin collapsedBottomMargin;
3096
nsRect combinedArea(0,0,0,0);
3097
*aKeepReflowGoing = brc.PlaceBlock(blockHtmlRS, isAdjacentWithTop,
3098
computedOffsets, collapsedBottomMargin,
3099
aLine->mBounds, combinedArea);
3100
aLine->SetCarriedOutBottomMargin(collapsedBottomMargin);
3102
if (aState.GetFlag(BRS_SHRINKWRAPWIDTH)) {
3103
// Mark the line as dirty so once we known the final shrink wrap width
3104
// we can reflow the block to the correct size
3105
// XXX We don't always need to do this...
3107
aState.SetFlag(BRS_NEEDRESIZEREFLOW, PR_TRUE);
3109
if (aState.GetFlag(BRS_UNCONSTRAINEDWIDTH) || aState.GetFlag(BRS_SHRINKWRAPWIDTH)) {
3110
// Add the right margin to the line's bounds. That way it will be
3111
// taken into account when we compute our shrink wrap size.
3112
nscoord marginRight = brc.GetMargin().right;
3113
if (marginRight != NS_UNCONSTRAINEDSIZE) {
3114
aLine->mBounds.width += marginRight;
3117
aLine->SetCombinedArea(combinedArea);
3118
if (*aKeepReflowGoing) {
3119
// Some of the child block fit
3121
// Advance to new Y position
3122
nscoord newY = aLine->mBounds.YMost();
3125
// Continue the block frame now if it didn't completely fit in
3126
// the available space.
3127
if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus)) {
3128
PRBool madeContinuation;
3129
rv = CreateContinuationFor(aState, nsnull, frame, madeContinuation);
3133
// Push continuation to a new line, but only if we actually made one.
3134
if (madeContinuation) {
3135
frame = frame->GetNextSibling();
3136
nsLineBox* line = aState.NewLineBox(frame, 1, PR_TRUE);
3137
if (nsnull == line) {
3138
return NS_ERROR_OUT_OF_MEMORY;
3140
mLines.after_insert(aLine, line);
3143
// Advance to next line since some of the block fit. That way
3144
// only the following lines will be pushed.
3145
PushLines(aState, aLine);
3146
aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
3147
*aKeepReflowGoing = PR_FALSE;
3149
// The bottom margin for a block is only applied on the last
3150
// flow block. Since we just continued the child block frame,
3151
// we know that line->mFirstChild is not the last flow block
3152
// therefore zero out the running margin value.
3153
#ifdef NOISY_VERTICAL_MARGINS
3155
printf(": reflow incomplete, frame=");
3156
nsFrame::ListTag(stdout, frame);
3157
printf(" prevBottomMargin=%d, setting to zero\n",
3158
aState.mPrevBottomMargin);
3160
aState.mPrevBottomMargin.Zero();
3163
#ifdef NOISY_VERTICAL_MARGINS
3165
printf(": reflow complete for ");
3166
nsFrame::ListTag(stdout, frame);
3167
printf(" prevBottomMargin=%d collapsedBottomMargin=%d\n",
3168
aState.mPrevBottomMargin, collapsedBottomMargin.get());
3170
aState.mPrevBottomMargin = collapsedBottomMargin;
3172
#ifdef NOISY_VERTICAL_MARGINS
3175
nsFrame::ListTag(stdout, frame);
3176
printf(" carriedOutBottomMargin=%d collapsedBottomMargin=%d => %d\n",
3177
brc.GetCarriedOutBottomMargin(), collapsedBottomMargin.get(),
3178
aState.mPrevBottomMargin);
3181
// Post-process the "line"
3182
nscoord maxElementWidth = 0;
3183
if (aState.GetFlag(BRS_COMPUTEMAXELEMENTWIDTH)) {
3184
maxElementWidth = brc.GetMaxElementWidth();
3186
// If we asked the block to update its maximum width, then record the
3187
// updated value in the line, and update the current maximum width
3188
if (aState.GetFlag(BRS_COMPUTEMAXWIDTH)) {
3189
aLine->mMaximumWidth = brc.GetMaximumWidth();
3190
aState.UpdateMaximumWidth(aLine->mMaximumWidth);
3192
PostPlaceLine(aState, aLine, maxElementWidth);
3194
// If the block frame that we just reflowed happens to be our
3195
// first block, then its computed ascent is ours
3196
if (frame == GetTopBlockChild(aState.mPresContext)) {
3197
const nsHTMLReflowMetrics& metrics = brc.GetMetrics();
3198
mAscent = metrics.ascent;
3201
// Place the "marker" (bullet) frame.
3203
// According to the CSS2 spec, section 12.6.1, the "marker" box
3204
// participates in the height calculation of the list-item box's
3207
// There are exactly two places a bullet can be placed: near the
3208
// first or second line. Its only placed on the second line in a
3209
// rare case: an empty first line followed by a second line that
3210
// contains a block (example: <LI>\n<P>... ). This is where
3211
// the second case can happen.
3212
if (mBullet && HaveOutsideBullet() &&
3213
((aLine == mLines.front()) ||
3214
((0 == mLines.front()->mBounds.height) &&
3215
(aLine == begin_lines().next())))) {
3216
// Reflow the bullet
3217
nsHTMLReflowMetrics metrics(nsnull);
3218
ReflowBullet(aState, metrics);
3220
// Doing the alignment using |mAscent| will also cater for bullets
3221
// that are placed next to a child block (bug 92896)
3222
// (Note that mAscent should be set by now, otherwise why would
3223
// we be placing the bullet yet?)
3225
// Tall bullets won't look particularly nice here...
3226
nsRect bbox = mBullet->GetRect();
3227
nscoord bulletTopMargin = applyTopMargin
3228
? collapsedBottomMargin.get()
3230
bbox.y = aState.BorderPadding().top + mAscent -
3231
metrics.ascent + bulletTopMargin;
3232
mBullet->SetRect(bbox);
3236
// None of the block fits. Determine the correct reflow status.
3237
if (aLine == mLines.front()) {
3238
// If it's our very first line then we need to be pushed to
3239
// our parents next-in-flow. Therefore, return break-before
3240
// status for our reflow status.
3241
aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE();
3244
// Push the line that didn't fit and any lines that follow it
3245
// to our next-in-flow.
3246
UndoSplitPlaceholders(aState, lastPlaceholder);
3247
PushLines(aState, aLine.prev());
3248
aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
3253
VerifyLines(PR_TRUE);
3258
#define LINE_REFLOW_OK 0
3259
#define LINE_REFLOW_STOP 1
3260
#define LINE_REFLOW_REDO 2
3261
// a frame was complete, but truncated and not at the top of a page
3262
#define LINE_REFLOW_TRUNCATED 3
3265
nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState,
3266
line_iterator aLine,
3267
PRBool* aKeepReflowGoing,
3268
PRBool aDamageDirtyArea,
3269
PRBool aUpdateMaximumWidth)
3271
nsresult rv = NS_OK;
3272
*aKeepReflowGoing = PR_TRUE;
3277
PRUint8 lineReflowStatus = LINE_REFLOW_REDO;
3278
while (LINE_REFLOW_REDO == lineReflowStatus) {
3279
// Prevent overflowing limited thread stacks by creating
3280
// nsLineLayout from the heap when the frame tree depth gets
3282
if (aState.mReflowState.mReflowDepth > 30) {//XXX layout-tune.h?
3283
rv = DoReflowInlineFramesMalloc(aState, aLine, aKeepReflowGoing,
3285
aUpdateMaximumWidth, aDamageDirtyArea);
3288
rv = DoReflowInlineFramesAuto(aState, aLine, aKeepReflowGoing,
3290
aUpdateMaximumWidth, aDamageDirtyArea);
3292
if (NS_FAILED(rv)) {
3297
if (1000 == spins) {
3299
printf(": yikes! spinning on a line over 1000 times!\n");
3308
nsBlockFrame::DoReflowInlineFramesMalloc(nsBlockReflowState& aState,
3309
line_iterator aLine,
3310
PRBool* aKeepReflowGoing,
3311
PRUint8* aLineReflowStatus,
3312
PRBool aUpdateMaximumWidth,
3313
PRBool aDamageDirtyArea)
3315
// XXXldb Using the PresShell arena here would be nice.
3316
nsLineLayout* ll = new nsLineLayout(aState.mPresContext,
3317
aState.mReflowState.mSpaceManager,
3318
&aState.mReflowState,
3319
aState.GetFlag(BRS_COMPUTEMAXELEMENTWIDTH));
3321
return NS_ERROR_OUT_OF_MEMORY;
3323
ll->Init(&aState, aState.mMinLineHeight, aState.mLineNumber);
3324
nsresult rv = DoReflowInlineFrames(aState, *ll, aLine, aKeepReflowGoing,
3325
aLineReflowStatus, aUpdateMaximumWidth, aDamageDirtyArea);
3326
ll->EndLineReflow();
3332
nsBlockFrame::DoReflowInlineFramesAuto(nsBlockReflowState& aState,
3333
line_iterator aLine,
3334
PRBool* aKeepReflowGoing,
3335
PRUint8* aLineReflowStatus,
3336
PRBool aUpdateMaximumWidth,
3337
PRBool aDamageDirtyArea)
3339
nsLineLayout lineLayout(aState.mPresContext,
3340
aState.mReflowState.mSpaceManager,
3341
&aState.mReflowState,
3342
aState.GetFlag(BRS_COMPUTEMAXELEMENTWIDTH));
3343
lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber);
3344
nsresult rv = DoReflowInlineFrames(aState, lineLayout, aLine,
3345
aKeepReflowGoing, aLineReflowStatus,
3346
aUpdateMaximumWidth, aDamageDirtyArea);
3347
lineLayout.EndLineReflow();
3351
// If at least one float on the line was complete, not at the top of
3352
// page, but was truncated, then restore the overflow floats to what
3353
// they were before and push the line. The floats that will be removed
3354
// from the list aren't yet known by the block's next in flow.
3356
nsBlockFrame::PushTruncatedPlaceholderLine(nsBlockReflowState& aState,
3357
line_iterator aLine,
3358
nsIFrame* aLastPlaceholder,
3359
PRBool& aKeepReflowGoing)
3361
UndoSplitPlaceholders(aState, aLastPlaceholder);
3363
line_iterator prevLine = aLine;
3365
PushLines(aState, prevLine);
3366
aKeepReflowGoing = PR_FALSE;
3367
aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
3371
nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState,
3372
nsLineLayout& aLineLayout,
3373
line_iterator aLine,
3374
PRBool* aKeepReflowGoing,
3375
PRUint8* aLineReflowStatus,
3376
PRBool aUpdateMaximumWidth,
3377
PRBool aDamageDirtyArea)
3379
// Forget all of the floats on the line
3380
aLine->FreeFloats(aState.mFloatCacheFreeList);
3381
aState.mFloatCombinedArea.SetRect(0, 0, 0, 0);
3383
// Setup initial coordinate system for reflowing the inline frames
3384
// into. Apply a previous block frame's bottom margin first.
3385
if (ShouldApplyTopMargin(aState, aLine)) {
3386
aState.mY += aState.mPrevBottomMargin.get();
3388
aState.GetAvailableSpace();
3389
PRBool impactedByFloats = aState.IsImpactedByFloat() ? PR_TRUE : PR_FALSE;
3390
aLine->SetLineIsImpactedByFloat(impactedByFloats);
3391
#ifdef REALLY_NOISY_REFLOW
3392
printf("nsBlockFrame::DoReflowInlineFrames %p impacted = %d\n",
3393
this, impactedByFloats);
3396
const nsMargin& borderPadding = aState.BorderPadding();
3397
nscoord x = aState.mAvailSpaceRect.x + borderPadding.left;
3398
nscoord availWidth = aState.mAvailSpaceRect.width;
3399
nscoord availHeight;
3400
if (aState.GetFlag(BRS_UNCONSTRAINEDHEIGHT)) {
3401
availHeight = NS_UNCONSTRAINEDSIZE;
3404
/* XXX get the height right! */
3405
availHeight = aState.mAvailSpaceRect.height;
3407
if (aUpdateMaximumWidth) {
3408
availWidth = NS_UNCONSTRAINEDSIZE;
3412
nscoord rightEdge = aState.mReflowState.mRightEdge;
3413
if ( (rightEdge != NS_UNCONSTRAINEDSIZE)
3414
&& (availWidth < rightEdge) ) {
3415
availWidth = rightEdge;
3419
aLineLayout.BeginLineReflow(x, aState.mY,
3420
availWidth, availHeight,
3422
PR_FALSE /*XXX isTopOfPage*/);
3424
// XXX Unfortunately we need to know this before reflowing the first
3425
// inline frame in the line. FIX ME.
3426
if ((0 == aLineLayout.GetLineNumber()) &&
3427
(NS_BLOCK_HAS_FIRST_LETTER_STYLE & mState)) {
3428
aLineLayout.SetFirstLetterStyleOK(PR_TRUE);
3431
// keep track of the last overflow float in case we need to undo any new additions
3432
nsFrameList* overflowPlace = GetOverflowPlaceholders(aState.mPresContext, PR_FALSE);
3433
nsIFrame* lastPlaceholder = (overflowPlace) ? overflowPlace->LastChild() : nsnull;
3435
// Reflow the frames that are already on the line first
3436
nsresult rv = NS_OK;
3437
PRUint8 lineReflowStatus = LINE_REFLOW_OK;
3439
nsIFrame* frame = aLine->mFirstChild;
3440
aLine->SetHasPercentageChild(PR_FALSE); // To be set by ReflowInlineFrame below
3441
// need to repeatedly call GetChildCount here, because the child
3442
// count can change during the loop!
3443
for (i = 0; i < aLine->GetChildCount(); i++) {
3444
rv = ReflowInlineFrame(aState, aLineLayout, aLine, frame,
3446
if (NS_FAILED(rv)) {
3449
if (LINE_REFLOW_OK != lineReflowStatus) {
3450
// It is possible that one or more of next lines are empty
3451
// (because of DeleteNextInFlowChild). If so, delete them now
3452
// in case we are finished.
3454
while ((aLine != end_lines()) && (0 == aLine->GetChildCount())) {
3455
// XXX Is this still necessary now that DeleteNextInFlowChild
3456
// uses DoRemoveFrame?
3457
nsLineBox *toremove = aLine;
3458
aLine = mLines.erase(aLine);
3459
NS_ASSERTION(nsnull == toremove->mFirstChild, "bad empty line");
3460
aState.FreeLineBox(toremove);
3464
if (LINE_REFLOW_TRUNCATED == lineReflowStatus) {
3465
// Push the line with the truncated float
3466
PushTruncatedPlaceholderLine(aState, aLine, lastPlaceholder, *aKeepReflowGoing);
3470
frame = frame->GetNextSibling();
3473
// Pull frames and reflow them until we can't
3474
while (LINE_REFLOW_OK == lineReflowStatus) {
3475
rv = PullFrame(aState, aLine, aDamageDirtyArea, frame);
3476
if (NS_FAILED(rv)) {
3479
if (nsnull == frame) {
3482
while (LINE_REFLOW_OK == lineReflowStatus) {
3483
PRInt32 oldCount = aLine->GetChildCount();
3484
rv = ReflowInlineFrame(aState, aLineLayout, aLine, frame,
3486
if (NS_FAILED(rv)) {
3489
if (aLine->GetChildCount() != oldCount) {
3490
// We just created a continuation for aFrame AND its going
3491
// to end up on this line (e.g. :first-letter
3492
// situation). Therefore we have to loop here before trying
3493
// to pull another frame.
3494
frame = frame->GetNextSibling();
3501
if (LINE_REFLOW_REDO == lineReflowStatus) {
3502
// This happens only when we have a line that is impacted by
3503
// floats and the first element in the line doesn't fit with
3506
// What we do is to advance past the first float we find and
3507
// then reflow the line all over again.
3508
NS_ASSERTION(aState.IsImpactedByFloat(),
3509
"redo line on totally empty line");
3510
NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mAvailSpaceRect.height,
3511
"unconstrained height on totally empty line");
3513
aState.mY += aState.mAvailSpaceRect.height;
3515
// We don't want to advance by the bottom margin anymore (we did it
3516
// once at the beginning of this function, which will just be called
3517
// again), and we certainly don't want to go back if it's negative
3518
// (infinite loop, bug 153429).
3519
aState.mPrevBottomMargin.Zero();
3521
// XXX: a small optimization can be done here when paginating:
3522
// if the new Y coordinate is past the end of the block then
3523
// push the line and return now instead of later on after we are
3526
else if (LINE_REFLOW_TRUNCATED != lineReflowStatus) {
3527
// If we are propagating out a break-before status then there is
3528
// no point in placing the line.
3529
if (!NS_INLINE_IS_BREAK_BEFORE(aState.mReflowStatus)) {
3530
if (PlaceLine(aState, aLineLayout, aLine, aKeepReflowGoing, aUpdateMaximumWidth)) {
3531
UndoSplitPlaceholders(aState, lastPlaceholder); // undo since we pushed the current line
3535
*aLineReflowStatus = lineReflowStatus;
3541
* Reflow an inline frame. The reflow status is mapped from the frames
3542
* reflow status to the lines reflow status (not to our reflow status).
3543
* The line reflow status is simple: PR_TRUE means keep placing frames
3544
* on the line; PR_FALSE means don't (the line is done). If the line
3545
* has some sort of breaking affect then aLine's break-type will be set
3546
* to something other than NS_STYLE_CLEAR_NONE.
3549
nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
3550
nsLineLayout& aLineLayout,
3551
line_iterator aLine,
3553
PRUint8* aLineReflowStatus)
3555
NS_ENSURE_ARG_POINTER(aFrame);
3557
*aLineReflowStatus = LINE_REFLOW_OK;
3559
// If it's currently ok to be reflowing in first-letter style then
3560
// we must be about to reflow a frame that has first-letter style.
3561
PRBool reflowingFirstLetter = aLineLayout.GetFirstLetterStyleOK();
3562
#ifdef NOISY_FIRST_LETTER
3564
printf(": reflowing ");
3565
nsFrame::ListTag(stdout, aFrame);
3566
printf(" reflowingFirstLetter=%s\n", reflowingFirstLetter ? "on" : "off");
3569
// Remember if we have a percentage aware child on this line
3570
if (IsPercentageAwareChild(aFrame)) {
3571
aLine->SetHasPercentageChild(PR_TRUE);
3574
// Reflow the inline frame
3575
nsReflowStatus frameReflowStatus;
3577
nsresult rv = aLineLayout.ReflowFrame(aFrame, frameReflowStatus,
3578
nsnull, pushedFrame);
3580
// If this is an incremental reflow, prune the child from the path
3581
// so we don't incrementally reflow it again.
3582
// XXX note that we don't currently have any incremental reflows
3583
// that trace a path through an inline frame (thanks to reflow roots
3584
// dealing with text inputs), but we may need to deal with this
3586
if (aState.mReflowState.path)
3587
aState.mReflowState.path->RemoveChild(aFrame);
3589
if (NS_FAILED(rv)) {
3592
#ifdef REALLY_NOISY_REFLOW_CHILD
3593
nsFrame::ListTag(stdout, aFrame);
3594
printf(": status=%x\n", frameReflowStatus);
3597
#if defined(REFLOW_STATUS_COVERAGE)
3598
RecordReflowStatus(PR_FALSE, frameReflowStatus);
3601
// Send post-reflow notification
3602
aState.mPrevChild = aFrame;
3605
This is where we need to add logic to handle some odd behavior.
3606
For one thing, we should usually place at least one thing next
3607
to a left float, even when that float takes up all the width on a line.
3611
// Process the child frames reflow status. There are 5 cases:
3612
// complete, not-complete, break-before, break-after-complete,
3613
// break-after-not-complete. There are two situations: we are a
3614
// block or we are an inline. This makes a total of 10 cases
3615
// (fortunately, there is some overlap).
3616
aLine->SetBreakType(NS_STYLE_CLEAR_NONE);
3617
if (NS_INLINE_IS_BREAK(frameReflowStatus) ||
3618
(NS_STYLE_CLEAR_NONE != aState.mFloatBreakType)) {
3619
// Always abort the line reflow (because a line break is the
3620
// minimal amount of break we do).
3621
*aLineReflowStatus = LINE_REFLOW_STOP;
3623
// XXX what should aLine's break-type be set to in all these cases?
3624
PRUint8 breakType = NS_INLINE_GET_BREAK_TYPE(frameReflowStatus);
3625
NS_ASSERTION((NS_STYLE_CLEAR_NONE != breakType) ||
3626
(NS_STYLE_CLEAR_NONE != aState.mFloatBreakType), "bad break type");
3627
NS_ASSERTION(NS_STYLE_CLEAR_PAGE != breakType, "no page breaks yet");
3629
if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus)) {
3630
// Break-before cases.
3631
if (aFrame == aLine->mFirstChild) {
3632
// If we break before the first frame on the line then we must
3633
// be trying to place content where theres no room (e.g. on a
3634
// line with wide floats). Inform the caller to reflow the
3635
// line after skipping past a float.
3636
*aLineReflowStatus = LINE_REFLOW_REDO;
3639
// It's not the first child on this line so go ahead and split
3640
// the line. We will see the frame again on the next-line.
3641
rv = SplitLine(aState, aLineLayout, aLine, aFrame);
3642
if (NS_FAILED(rv)) {
3646
// If we're splitting the line because the frame didn't fit and it
3647
// was pushed, then mark the line as having word wrapped. We need to
3648
// know that if we're shrink wrapping our width
3650
aLine->SetLineWrapped(PR_TRUE);
3655
// If a float split and its prev-in-flow was followed by a <BR>, then combine
3656
// the <BR>'s break type with the inline's break type (the inline will be the very
3657
// next frame after the split float).
3658
if (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType) {
3659
breakType = ::CombineBreakType(breakType, aState.mFloatBreakType);
3660
aState.mFloatBreakType = NS_STYLE_CLEAR_NONE;
3662
// Break-after cases
3663
if (breakType == NS_STYLE_CLEAR_LINE) {
3664
if (!aLineLayout.GetLineEndsInBR()) {
3665
breakType = NS_STYLE_CLEAR_NONE;
3668
aLine->SetBreakType(breakType);
3669
if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus)) {
3670
// Create a continuation for the incomplete frame. Note that the
3671
// frame may already have a continuation.
3672
PRBool madeContinuation;
3673
rv = CreateContinuationFor(aState, aLine, aFrame, madeContinuation);
3676
// Remember that the line has wrapped
3677
aLine->SetLineWrapped(PR_TRUE);
3680
// Split line, but after the frame just reflowed
3681
rv = SplitLine(aState, aLineLayout, aLine, aFrame->GetNextSibling());
3682
if (NS_FAILED(rv)) {
3686
if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus)) {
3687
// Mark next line dirty in case SplitLine didn't end up
3688
// pushing any frames.
3689
nsLineList_iterator next = aLine.next();
3690
if (next != end_lines() && !next->IsBlock()) {
3696
else if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus)) {
3697
// Frame is not-complete, no special breaking status
3699
nsIAtom* frameType = aFrame->GetType();
3701
// Create a continuation for the incomplete frame. Note that the
3702
// frame may already have a continuation.
3703
PRBool madeContinuation;
3704
rv = (nsLayoutAtoms::placeholderFrame == frameType)
3705
? SplitPlaceholder(*aState.mPresContext, *aFrame)
3706
: CreateContinuationFor(aState, aLine, aFrame, madeContinuation);
3710
// Remember that the line has wrapped
3711
aLine->SetLineWrapped(PR_TRUE);
3713
// If we are reflowing the first letter frame or a placeholder then
3714
// don't split the line and don't stop the line reflow...
3715
PRBool splitLine = !reflowingFirstLetter &&
3716
(nsLayoutAtoms::placeholderFrame != frameType);
3717
if (reflowingFirstLetter) {
3718
if ((nsLayoutAtoms::inlineFrame == frameType) ||
3719
(nsLayoutAtoms::lineFrame == frameType)) {
3720
splitLine = PR_TRUE;
3725
// Split line after the current frame
3726
*aLineReflowStatus = LINE_REFLOW_STOP;
3727
rv = SplitLine(aState, aLineLayout, aLine, aFrame->GetNextSibling());
3728
if (NS_FAILED(rv)) {
3732
// Mark next line dirty in case SplitLine didn't end up
3733
// pushing any frames.
3734
nsLineList_iterator next = aLine.next();
3735
if (next != end_lines() && !next->IsBlock()) {
3740
else if (NS_FRAME_IS_TRUNCATED(frameReflowStatus)) {
3741
// if the frame is a placeholder and was complete but truncated (and not at the top
3742
// of page), the entire line will be pushed to give it another chance to not truncate.
3743
if (nsLayoutAtoms::placeholderFrame == aFrame->GetType()) {
3744
*aLineReflowStatus = LINE_REFLOW_TRUNCATED;
3752
* Create a continuation, if necessary, for aFrame. Place it in aLine
3753
* if aLine is not null. Set aMadeNewFrame to PR_TRUE if a new frame is created.
3756
nsBlockFrame::CreateContinuationFor(nsBlockReflowState& aState,
3759
PRBool& aMadeNewFrame)
3761
aMadeNewFrame = PR_FALSE;
3763
nsIFrame* nextInFlow;
3764
rv = CreateNextInFlow(aState.mPresContext, this, aFrame, nextInFlow);
3765
if (NS_FAILED(rv)) {
3768
if (nsnull != nextInFlow) {
3769
aMadeNewFrame = PR_TRUE;
3771
aLine->SetChildCount(aLine->GetChildCount() + 1);
3775
VerifyLines(PR_FALSE);
3781
nsBlockFrame::SplitPlaceholder(nsIPresContext& aPresContext,
3782
nsIFrame& aPlaceholder)
3784
nsIFrame* nextInFlow;
3785
nsresult rv = CreateNextInFlow(&aPresContext, this, &aPlaceholder, nextInFlow);
3788
// put the sibling list back to what it was before the continuation was created
3789
nsIFrame *contFrame = aPlaceholder.GetNextSibling();
3790
nsIFrame *next = contFrame->GetNextSibling();
3791
aPlaceholder.SetNextSibling(next);
3792
contFrame->SetNextSibling(nsnull);
3793
// add the placehoder to the overflow floats
3794
nsFrameList* overflowPlace = GetOverflowPlaceholders(&aPresContext, PR_FALSE);
3795
if (overflowPlace) {
3796
overflowPlace->AppendFrames(this, contFrame);
3799
overflowPlace = new nsFrameList(contFrame);
3800
if (overflowPlace) {
3801
SetOverflowPlaceholders(&aPresContext, overflowPlace);
3803
else return NS_ERROR_NULL_POINTER;
3809
nsBlockFrame::SplitLine(nsBlockReflowState& aState,
3810
nsLineLayout& aLineLayout,
3811
line_iterator aLine,
3814
NS_ABORT_IF_FALSE(aLine->IsInline(), "illegal SplitLine on block line");
3816
PRInt32 pushCount = aLine->GetChildCount() - aLineLayout.GetCurrentSpanCount();
3817
NS_ABORT_IF_FALSE(pushCount >= 0, "bad push count");
3821
nsFrame::IndentBy(stdout, gNoiseIndent);
3822
printf("split line: from line=%p pushCount=%d aFrame=",
3823
NS_STATIC_CAST(void*, aLine.get()), pushCount);
3825
nsFrame::ListTag(stdout, aFrame);
3831
if (gReallyNoisyReflow) {
3832
aLine->List(aState.mPresContext, stdout, gNoiseIndent+1);
3837
if (0 != pushCount) {
3838
NS_ABORT_IF_FALSE(aLine->GetChildCount() > pushCount, "bad push");
3839
NS_ABORT_IF_FALSE(nsnull != aFrame, "whoops");
3841
// Put frames being split out into their own line
3842
nsLineBox* newLine = aState.NewLineBox(aFrame, pushCount, PR_FALSE);
3844
return NS_ERROR_OUT_OF_MEMORY;
3846
mLines.after_insert(aLine, newLine);
3847
aLine->SetChildCount(aLine->GetChildCount() - pushCount);
3849
if (gReallyNoisyReflow) {
3850
newLine->List(aState.mPresContext, stdout, gNoiseIndent+1);
3854
// Let line layout know that some frames are no longer part of its
3856
aLineLayout.SplitLineTo(aLine->GetChildCount());
3858
VerifyLines(PR_TRUE);
3865
nsBlockFrame::ShouldJustifyLine(nsBlockReflowState& aState,
3866
line_iterator aLine)
3868
while (++aLine != end_lines()) {
3869
// There is another line
3870
if (0 != aLine->GetChildCount()) {
3871
// If the next line is a block line then we must not justify
3872
// this line because it means that this line is the last in a
3873
// group of inline lines.
3874
return !aLine->IsBlock();
3876
// The next line is empty, try the next one
3879
// XXX Not sure about this part
3880
// Try our next-in-flows lines to answer the question
3881
nsBlockFrame* nextInFlow = (nsBlockFrame*) mNextInFlow;
3882
while (nsnull != nextInFlow) {
3883
for (line_iterator line = nextInFlow->begin_lines(),
3884
line_end = nextInFlow->end_lines();
3888
if (0 != line->GetChildCount())
3889
return !line->IsBlock();
3891
nextInFlow = (nsBlockFrame*) nextInFlow->mNextInFlow;
3894
// This is the last line - so don't allow justification
3899
nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
3900
nsLineLayout& aLineLayout,
3901
line_iterator aLine,
3902
PRBool* aKeepReflowGoing,
3903
PRBool aUpdateMaximumWidth)
3905
// Trim extra white-space from the line before placing the frames
3906
aLineLayout.TrimTrailingWhiteSpace();
3908
// Vertically align the frames on this line.
3910
// According to the CSS2 spec, section 12.6.1, the "marker" box
3911
// participates in the height calculation of the list-item box's
3914
// There are exactly two places a bullet can be placed: near the
3915
// first or second line. Its only placed on the second line in a
3916
// rare case: an empty first line followed by a second line that
3917
// contains a block (example: <LI>\n<P>... ).
3919
// For this code, only the first case is possible because this
3920
// method is used for placing a line of inline frames. If the rare
3921
// case is happening then the worst that will happen is that the
3922
// bullet frame will be reflowed twice.
3923
PRBool addedBullet = PR_FALSE;
3924
if (mBullet && HaveOutsideBullet() && (aLine == mLines.front()) &&
3925
(!aLineLayout.IsZeroHeight() || (aLine == mLines.back()))) {
3926
nsHTMLReflowMetrics metrics(nsnull);
3927
ReflowBullet(aState, metrics);
3928
aLineLayout.AddBulletFrame(mBullet, metrics);
3929
addedBullet = PR_TRUE;
3931
nscoord maxElementWidth;
3932
aLineLayout.VerticalAlignLine(aLine, &maxElementWidth);
3933
// Our ascent is the ascent of our first line (but if this line is all
3934
// whitespace we'll correct things in |ReflowBlockFrame|).
3935
if (aLine == mLines.front()) {
3936
mAscent = aLine->mBounds.y + aLine->GetAscent();
3939
// See if we're shrink wrapping the width
3940
if (aState.GetFlag(BRS_SHRINKWRAPWIDTH)) {
3941
// When determining the line's width we also need to include any
3942
// right floats that impact us. This represents the shrink wrap
3943
// width of the line
3944
if (aState.IsImpactedByFloat() && !aLine->IsLineWrapped()) {
3945
NS_ASSERTION(aState.mContentArea.width >= aState.mAvailSpaceRect.XMost(), "bad state");
3946
aLine->mBounds.width += aState.mContentArea.width - aState.mAvailSpaceRect.XMost();
3951
static nscoord lastHeight = 0;
3952
if (CRAZY_HEIGHT(aLine->mBounds.y)) {
3953
lastHeight = aLine->mBounds.y;
3954
if (abs(aLine->mBounds.y - lastHeight) > CRAZY_H/10) {
3955
nsFrame::ListTag(stdout);
3956
printf(": line=%p y=%d line.bounds.height=%d\n",
3957
NS_STATIC_CAST(void*, aLine.get()),
3958
aLine->mBounds.y, aLine->mBounds.height);
3967
// Only block frames horizontally align their children because
3968
// inline frames "shrink-wrap" around their children (therefore
3969
// there is no extra horizontal space).
3970
const nsStyleText* styleText = GetStyleText();
3971
PRBool allowJustify = NS_STYLE_TEXT_ALIGN_JUSTIFY == styleText->mTextAlign &&
3972
!aLineLayout.GetLineEndsInBR() &&
3973
ShouldJustifyLine(aState, aLine);
3974
PRBool successful = aLineLayout.HorizontalAlignFrames(aLine->mBounds,
3975
allowJustify, aState.GetFlag(BRS_SHRINKWRAPWIDTH));
3976
// XXX: not only bidi: right alignment can be broken after
3977
// RelativePositionFrames!!!
3978
// XXXldb Is something here considering relatively positioned frames at
3979
// other than their original positions?
3981
// Mark the line dirty and then later once we've determined the width
3982
// we can do the horizontal alignment
3984
aState.SetFlag(BRS_NEEDRESIZEREFLOW, PR_TRUE);
3987
// XXXldb Why don't we do this earlier?
3990
aState.mPresContext->GetBidiEnabled(&bidiEnabled);
3993
if (!aState.mPresContext->IsVisualMode()) {
3994
nsBidiPresUtils* bidiUtils;
3995
aState.mPresContext->GetBidiUtils(&bidiUtils);
3997
if (bidiUtils && bidiUtils->IsSuccessful() ) {
3998
nsIFrame* nextInFlow = (aLine.next() != end_lines())
3999
? aLine.next()->mFirstChild : nsnull;
4001
bidiUtils->ReorderFrames(aState.mPresContext,
4002
aState.mReflowState.rendContext,
4003
aLine->mFirstChild, nextInFlow,
4004
aLine->GetChildCount() );
4006
} // not visual mode
4011
nsRect combinedArea;
4012
aLineLayout.RelativePositionFrames(combinedArea); // XXXldb This returned width as -15, 2001-06-12, Bugzilla
4013
aLine->SetCombinedArea(combinedArea);
4015
aLineLayout.RemoveBulletFrame(mBullet);
4018
// Inline lines do not have margins themselves; however they are
4019
// impacted by prior block margins. If this line ends up having some
4020
// height then we zero out the previous bottom margin value that was
4021
// already applied to the line's starting Y coordinate. Otherwise we
4022
// leave it be so that the previous blocks bottom margin can be
4023
// collapsed with a block that follows.
4026
if (!aLine->IsEmpty()) {
4027
// This line has some height. Therefore the application of the
4028
// previous-bottom-margin should stick.
4029
aState.mPrevBottomMargin.Zero();
4030
newY = aLine->mBounds.YMost();
4033
// Don't let the previous-bottom-margin value affect the newY
4034
// coordinate (it was applied in ReflowInlineFrames speculatively)
4035
// since the line is empty.
4036
// We already called |ShouldApplyTopMargin|, and if we applied it
4037
// then BRS_APPLYTOPMARGIN is set.
4038
nscoord dy = aState.GetFlag(BRS_APPLYTOPMARGIN)
4039
? -aState.mPrevBottomMargin.get() : 0;
4040
newY = aState.mY + dy;
4041
aLine->SlideBy(dy); // XXXldb Do we really want to do this?
4042
// keep our ascent in sync
4043
// XXXldb If it's empty, shouldn't the next line control the ascent?
4044
if (mLines.front() == aLine) {
4049
// See if the line fit. If it doesn't we need to push it. Our first
4050
// line will always fit.
4051
if (mLines.front() != aLine &&
4052
newY > aState.mBottomEdge &&
4053
aState.mBottomEdge != NS_UNCONSTRAINEDSIZE) {
4054
// Push this line and all of it's children and anything else that
4055
// follows to our next-in-flow
4056
NS_ASSERTION((aState.mCurrentLine == aLine), "oops");
4057
PushLines(aState, aLine.prev());
4059
// Stop reflow and whack the reflow status if reflow hasn't
4060
// already been stopped.
4061
if (*aKeepReflowGoing) {
4062
NS_ASSERTION(NS_FRAME_COMPLETE == aState.mReflowStatus,
4063
"lost reflow status");
4064
aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
4065
*aKeepReflowGoing = PR_FALSE;
4072
// If we're reflowing the line just to incrementally update the
4073
// maximum width, then don't post-place the line. It's doing work we
4074
// don't need, and it will update things like aState.mKidXMost that
4075
// we don't want updated...
4076
if (aUpdateMaximumWidth) {
4077
// However, we do need to update the max-element-width if requested
4078
if (aState.GetFlag(BRS_COMPUTEMAXELEMENTWIDTH)) {
4079
aState.UpdateMaxElementWidth(maxElementWidth);
4080
// We also cache the max element width in the line. This is needed for
4081
// incremental reflow
4082
aLine->mMaxElementWidth = maxElementWidth;
4084
if (gNoisyMaxElementWidth) {
4085
IndentBy(stdout, gNoiseIndent);
4086
printf ("nsBlockFrame::PlaceLine: %p setting MEW for line %p to %d\n",
4087
NS_STATIC_CAST(void*, this), NS_STATIC_CAST(void*, aLine.get()),
4094
PostPlaceLine(aState, aLine, maxElementWidth);
4097
// Add the already placed current-line floats to the line
4098
aLine->AppendFloats(aState.mCurrentLineFloats);
4100
// Any below current line floats to place?
4101
if (aState.mBelowCurrentLineFloats.NotEmpty()) {
4102
// keep track of the last overflow float in case we need to undo and push the line
4103
nsFrameList* overflowPlace = GetOverflowPlaceholders(aState.mPresContext, PR_FALSE);
4104
nsIFrame* lastPlaceholder = (overflowPlace) ? overflowPlace->LastChild() : nsnull;
4105
// Reflow the below-current-line floats, then add them to the
4106
// lines float list if there aren't any truncated floats.
4107
if (aState.PlaceBelowCurrentLineFloats(aState.mBelowCurrentLineFloats)) {
4108
aLine->AppendFloats(aState.mBelowCurrentLineFloats);
4111
// At least one float is truncated, so fix up any placeholders that got split and
4112
// push the line. XXX It may be better to put the float on the next line, but this
4113
// is not common enough to justify the complexity.
4114
PushTruncatedPlaceholderLine(aState, aLine, lastPlaceholder, *aKeepReflowGoing);
4118
// When a line has floats, factor them into the combined-area
4120
if (aLine->HasFloats()) {
4121
// Combine the float combined area (stored in aState) and the
4122
// value computed by the line layout code.
4123
nsRect lineCombinedArea(aLine->GetCombinedArea());
4124
#ifdef NOISY_COMBINED_AREA
4126
printf(": lineCA=%d,%d,%d,%d floatCA=%d,%d,%d,%d\n",
4127
lineCombinedArea.x, lineCombinedArea.y,
4128
lineCombinedArea.width, lineCombinedArea.height,
4129
aState.mFloatCombinedArea.x, aState.mFloatCombinedArea.y,
4130
aState.mFloatCombinedArea.width,
4131
aState.mFloatCombinedArea.height);
4133
lineCombinedArea.UnionRect(aState.mFloatCombinedArea, lineCombinedArea);
4135
aLine->SetCombinedArea(lineCombinedArea);
4136
#ifdef NOISY_COMBINED_AREA
4137
printf(" ==> final lineCA=%d,%d,%d,%d\n",
4138
lineCombinedArea.x, lineCombinedArea.y,
4139
lineCombinedArea.width, lineCombinedArea.height);
4143
// Apply break-after clearing if necessary
4144
// This must stay in sync with |ReflowDirtyLines|.
4145
PRUint8 breakType = aLine->GetBreakType();
4146
switch (breakType) {
4147
case NS_STYLE_CLEAR_LEFT:
4148
case NS_STYLE_CLEAR_RIGHT:
4149
case NS_STYLE_CLEAR_LEFT_AND_RIGHT:
4150
aState.ClearFloats(aState.mY, breakType);
4158
nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState,
4160
nscoord aMaxElementWidth)
4162
// Update max-element-width
4163
if (aState.GetFlag(BRS_COMPUTEMAXELEMENTWIDTH)) {
4164
aState.UpdateMaxElementWidth(aMaxElementWidth);
4165
// We also cache the max element width in the line. This is needed for
4166
// incremental reflow
4167
aLine->mMaxElementWidth = aMaxElementWidth;
4169
if (gNoisyMaxElementWidth) {
4170
IndentBy(stdout, gNoiseIndent);
4171
printf ("nsBlockFrame::PostPlaceLine: %p setting line %p MEW %d\n",
4172
NS_STATIC_CAST(void*, this), NS_STATIC_CAST(void*, aLine),
4178
// If this is an unconstrained reflow, then cache the line width in the
4179
// line. We'll need this during incremental reflow if we're asked to
4180
// calculate the maximum width
4181
if (aState.GetFlag(BRS_UNCONSTRAINEDWIDTH)) {
4182
#ifdef NOISY_MAXIMUM_WIDTH
4183
printf("nsBlockFrame::PostPlaceLine during UC Reflow of block %p line %p caching max width %d\n",
4184
NS_STATIC_CAST(void*, this), NS_STATIC_CAST(void*, aLine),
4185
aLine->mBounds.XMost());
4187
aLine->mMaximumWidth = aLine->mBounds.XMost();
4191
nscoord xmost = aLine->mBounds.XMost();
4194
if (CRAZY_WIDTH(xmost)) {
4196
printf(": line=%p xmost=%d\n", NS_STATIC_CAST(void*, aLine), xmost);
4199
if (xmost > aState.mKidXMost) {
4200
aState.mKidXMost = xmost;
4201
#ifdef NOISY_KIDXMOST
4202
printf("%p PostPlaceLine aState.mKidXMost=%d\n", this, aState.mKidXMost);
4208
nsBlockFrame::PushLines(nsBlockReflowState& aState,
4209
nsLineList::iterator aLineBefore)
4211
nsLineList::iterator overBegin(aLineBefore.next());
4213
// PushTruncatedPlaceholderLine sometimes pushes the first line. Ugh.
4214
PRBool firstLine = overBegin == begin_lines();
4216
if (overBegin != end_lines()) {
4217
// XXXldb use presshell arena!
4218
nsLineList* overflowLines = new nsLineList();
4219
overflowLines->splice(overflowLines->end(), mLines, overBegin,
4221
NS_ASSERTION(!overflowLines->empty(), "should not be empty");
4222
// this takes ownership but it won't delete it immediately so we
4223
// can keep using it.
4224
SetOverflowLines(aState.mPresContext, overflowLines);
4226
// Mark all the overflow lines dirty so that they get reflowed when
4227
// they are pulled up by our next-in-flow.
4229
// XXXldb Can this get called O(N) times making the whole thing O(N^2)?
4230
for (line_iterator line = overflowLines->begin(),
4231
line_end = overflowLines->end();
4239
// Break frame sibling list
4241
aLineBefore->LastChild()->SetNextSibling(nsnull);
4244
VerifyOverflowSituation(aState.mPresContext);
4248
// The overflowLines property is stored as a pointer to a line list,
4249
// which must be deleted. However, the following functions all maintain
4250
// the invariant that the property is never set if the list is empty.
4253
nsBlockFrame::DrainOverflowLines(nsIPresContext* aPresContext)
4256
VerifyOverflowSituation(aPresContext);
4258
PRBool drained = PR_FALSE;
4259
nsLineList* overflowLines;
4261
// First grab the prev-in-flows overflow lines
4262
nsBlockFrame* prevBlock = (nsBlockFrame*) mPrevInFlow;
4263
if (nsnull != prevBlock) {
4264
overflowLines = prevBlock->GetOverflowLines(aPresContext, PR_TRUE);
4265
if (nsnull != overflowLines) {
4266
NS_ASSERTION(! overflowLines->empty(),
4267
"overflow lines should never be set and empty");
4270
// Make all the frames on the overflow line list mine
4271
nsIFrame* lastFrame = nsnull;
4272
nsIFrame* frame = overflowLines->front()->mFirstChild;
4273
while (nsnull != frame) {
4274
frame->SetParent(this);
4276
// When pushing and pulling frames we need to check for whether any
4277
// views need to be reparented
4278
nsHTMLContainerFrame::ReparentFrameView(aPresContext, frame, prevBlock, this);
4280
// Get the next frame
4282
frame = frame->GetNextSibling();
4285
// Join the line lists
4286
NS_ASSERTION(lastFrame, "overflow list was created with no frames");
4287
if (! mLines.empty()) {
4288
// Join the sibling lists together
4289
lastFrame->SetNextSibling(mLines.front()->mFirstChild);
4291
// Place overflow lines at the front of our line list
4292
mLines.splice(mLines.begin(), *overflowLines);
4293
NS_ASSERTION(overflowLines->empty(), "splice should empty list");
4294
delete overflowLines;
4296
// Out-of-flow floats need to be reparented too.
4297
nsFrameList* overflowOutOfFlows = prevBlock->GetOverflowOutOfFlows(PR_TRUE);
4298
if (overflowOutOfFlows) {
4299
for (nsIFrame* f = overflowOutOfFlows->FirstChild(); f;
4300
f = f->GetNextSibling()) {
4303
// When pushing and pulling frames we need to check for whether any
4304
// views need to be reparented
4305
nsHTMLContainerFrame::ReparentFrameView(aPresContext, f, prevBlock, this);
4307
delete overflowOutOfFlows;
4312
// Now grab our own overflow lines
4313
overflowLines = GetOverflowLines(aPresContext, PR_TRUE);
4314
if (overflowLines) {
4315
NS_ASSERTION(! overflowLines->empty(),
4316
"overflow lines should never be set and empty");
4317
// This can happen when we reflow and not everything fits and then
4318
// we are told to reflow again before a next-in-flow is created
4321
if (! mLines.empty()) {
4322
mLines.back()->LastChild()->SetNextSibling(
4323
overflowLines->front()->mFirstChild );
4325
// append the overflow to mLines
4326
mLines.splice(mLines.end(), *overflowLines);
4328
delete overflowLines;
4330
// Likewise, drain our own overflow out-of-flows. We don't need to
4331
// reparent them since they're already our children. We don't need
4332
// to put them on any child list since BuildFloatList will put
4333
// them on some child list. All we need to do is remove the
4335
nsFrameList* overflowOutOfFlows = GetOverflowOutOfFlows(PR_TRUE);
4336
delete overflowOutOfFlows;
4342
nsBlockFrame::GetOverflowLines(nsIPresContext* aPresContext,
4343
PRBool aRemoveProperty) const
4346
NS_STATIC_CAST(nsLineList*, GetProperty(aPresContext,
4347
nsLayoutAtoms::overflowLinesProperty,
4349
NS_ASSERTION(!lines || !lines->empty(), "value should never be stored as empty");
4353
// Destructor function for the overflowLines frame property
4355
DestroyOverflowLines(nsIPresContext* aPresContext,
4357
nsIAtom* aPropertyName,
4358
void* aPropertyValue)
4360
if (aPropertyValue) {
4361
nsLineList* lines = NS_STATIC_CAST(nsLineList*, aPropertyValue);
4362
nsLineBox::DeleteLineList(aPresContext, *lines);
4367
// This takes ownership of aOverflowLines.
4368
// XXX We should allocate overflowLines from presShell arena!
4370
nsBlockFrame::SetOverflowLines(nsIPresContext* aPresContext,
4371
nsLineList* aOverflowLines)
4373
NS_ASSERTION(aOverflowLines, "null lines");
4374
NS_ASSERTION(!aOverflowLines->empty(), "empty lines");
4376
nsresult rv = SetProperty(aPresContext, nsLayoutAtoms::overflowLinesProperty,
4377
aOverflowLines, DestroyOverflowLines);
4378
// Verify that we didn't overwrite an existing overflow list
4379
NS_ASSERTION(rv != NS_IFRAME_MGR_PROP_OVERWRITTEN, "existing overflow list");
4384
nsBlockFrame::GetOverflowOutOfFlows(PRBool aRemoveProperty) const
4386
return NS_STATIC_CAST(nsFrameList*,
4387
GetProperty(GetPresContext(), nsLayoutAtoms::overflowOutOfFlowsProperty,
4391
// Destructor function for the overflowPlaceholders frame property
4393
DestroyOverflowOOFs(nsIPresContext* aPresContext,
4395
nsIAtom* aPropertyName,
4396
void* aPropertyValue)
4398
delete NS_STATIC_CAST(nsFrameList*, aPropertyValue);
4401
// This takes ownership of aFloaters.
4403
nsBlockFrame::SetOverflowOutOfFlows(nsFrameList* aOOFs)
4405
nsresult rv = SetProperty(GetPresContext(), nsLayoutAtoms::overflowOutOfFlowsProperty,
4406
aOOFs, DestroyOverflowOOFs);
4407
// Verify that we didn't overwrite an existing overflow list
4408
NS_ASSERTION(rv != NS_IFRAME_MGR_PROP_OVERWRITTEN, "existing overflow float list");
4413
nsBlockFrame::GetOverflowPlaceholders(nsIPresContext* aPresContext,
4414
PRBool aRemoveProperty) const
4416
nsFrameList* placeholders =
4417
NS_STATIC_CAST(nsFrameList*,
4418
GetProperty(aPresContext, nsLayoutAtoms::overflowPlaceholdersProperty,
4420
return placeholders;
4423
// Destructor function for the overflowPlaceholders frame property
4425
DestroyOverflowPlaceholders(nsIPresContext* aPresContext,
4427
nsIAtom* aPropertyName,
4428
void* aPropertyValue)
4430
nsFrameList* overflowPlace = NS_STATIC_CAST(nsFrameList*, aPropertyValue);
4431
delete overflowPlace;
4434
// This takes ownership of aOverflowLines.
4435
// XXX We should allocate overflowLines from presShell arena!
4437
nsBlockFrame::SetOverflowPlaceholders(nsIPresContext* aPresContext,
4438
nsFrameList* aOverflowPlaceholders)
4440
nsresult rv = SetProperty(aPresContext, nsLayoutAtoms::overflowPlaceholdersProperty,
4441
aOverflowPlaceholders, DestroyOverflowPlaceholders);
4442
// Verify that we didn't overwrite an existing overflow list
4443
NS_ASSERTION(rv != NS_IFRAME_MGR_PROP_OVERWRITTEN, "existing overflow placeholder list");
4447
//////////////////////////////////////////////////////////////////////
4448
// Frame list manipulation routines
4451
nsBlockFrame::LastChild()
4453
if (! mLines.empty()) {
4454
return mLines.back()->LastChild();
4460
nsBlockFrame::AppendFrames(nsIPresContext* aPresContext,
4461
nsIPresShell& aPresShell,
4463
nsIFrame* aFrameList)
4465
if (nsnull == aFrameList) {
4468
if (mAbsoluteContainer.GetChildListName() == aListName) {
4469
return mAbsoluteContainer.AppendFrames(this, aPresContext, aPresShell, aListName,
4472
else if (nsLayoutAtoms::floatList == aListName) {
4473
// XXX we don't *really* care about this right now because we are
4474
// BuildFloatList ing still
4475
mFloats.AppendFrames(nsnull, aFrameList);
4478
else if (nsnull != aListName) {
4479
return NS_ERROR_INVALID_ARG;
4482
// Find the proper last-child for where the append should go
4483
nsIFrame* lastKid = nsnull;
4484
nsLineBox* lastLine = mLines.empty() ? nsnull : mLines.back();
4486
lastKid = lastLine->LastChild();
4489
// Add frames after the last child
4490
#ifdef NOISY_REFLOW_REASON
4492
printf(": append ");
4493
nsFrame::ListTag(stdout, aFrameList);
4496
nsFrame::ListTag(stdout, lastKid);
4500
nsresult rv = AddFrames(aPresContext, aFrameList, lastKid);
4501
if (NS_SUCCEEDED(rv)) {
4502
// Ask the parent frame to reflow me.
4503
ReflowDirtyChild(&aPresShell, nsnull);
4509
nsBlockFrame::InsertFrames(nsIPresContext* aPresContext,
4510
nsIPresShell& aPresShell,
4512
nsIFrame* aPrevFrame,
4513
nsIFrame* aFrameList)
4515
if (mAbsoluteContainer.GetChildListName() == aListName) {
4516
return mAbsoluteContainer.InsertFrames(this, aPresContext, aPresShell, aListName,
4517
aPrevFrame, aFrameList);
4519
else if (nsLayoutAtoms::floatList == aListName) {
4520
// XXX we don't *really* care about this right now because we are
4521
// BuildFloatList'ing still
4522
mFloats.AppendFrames(nsnull, aFrameList);
4526
else if (nsLayoutAtoms::nextBidi == aListName) {}
4528
else if (nsnull != aListName) {
4529
return NS_ERROR_INVALID_ARG;
4532
#ifdef NOISY_REFLOW_REASON
4534
printf(": insert ");
4535
nsFrame::ListTag(stdout, aFrameList);
4538
nsFrame::ListTag(stdout, aPrevFrame);
4542
nsresult rv = AddFrames(aPresContext, aFrameList, aPrevFrame);
4544
if (aListName != nsLayoutAtoms::nextBidi)
4546
if (NS_SUCCEEDED(rv)) {
4547
// Ask the parent frame to reflow me.
4548
ReflowDirtyChild(&aPresShell, nsnull);
4554
nsBlockFrame::AddFrames(nsIPresContext* aPresContext,
4555
nsIFrame* aFrameList,
4556
nsIFrame* aPrevSibling)
4558
// Clear our line cursor, since our lines may change.
4561
if (nsnull == aFrameList) {
4565
nsIPresShell *presShell = aPresContext->PresShell();
4567
// Attempt to find the line that contains the previous sibling
4568
nsLineList::iterator prevSibLine = end_lines();
4569
PRInt32 prevSiblingIndex = -1;
4571
// XXX_perf This is technically O(N^2) in some cases, but by using
4572
// RFind instead of Find, we make it O(N) in the most common case,
4573
// which is appending cotent.
4575
// Find the line that contains the previous sibling
4576
if (! nsLineBox::RFindLineContaining(aPrevSibling,
4577
begin_lines(), prevSibLine,
4578
&prevSiblingIndex)) {
4579
// Note: defensive code! RFindLineContaining must not return
4580
// false in this case, so if it does...
4581
NS_NOTREACHED("prev sibling not in line list");
4582
aPrevSibling = nsnull;
4583
prevSibLine = end_lines();
4587
// Find the frame following aPrevSibling so that we can join up the
4588
// two lists of frames.
4589
nsIFrame* prevSiblingNextFrame = nsnull;
4591
prevSiblingNextFrame = aPrevSibling->GetNextSibling();
4593
// Split line containing aPrevSibling in two if the insertion
4594
// point is somewhere in the middle of the line.
4595
PRInt32 rem = prevSibLine->GetChildCount() - prevSiblingIndex - 1;
4597
// Split the line in two where the frame(s) are being inserted.
4598
nsLineBox* line = NS_NewLineBox(presShell, prevSiblingNextFrame, rem, PR_FALSE);
4600
return NS_ERROR_OUT_OF_MEMORY;
4602
mLines.after_insert(prevSibLine, line);
4603
prevSibLine->SetChildCount(prevSibLine->GetChildCount() - rem);
4604
prevSibLine->MarkDirty();
4607
// Now (partially) join the sibling lists together
4608
aPrevSibling->SetNextSibling(aFrameList);
4610
else if (! mLines.empty()) {
4611
prevSiblingNextFrame = mLines.front()->mFirstChild;
4614
// Walk through the new frames being added and update the line data
4615
// structures to fit.
4616
nsIFrame* newFrame = aFrameList;
4618
PRBool isBlock = nsLineLayout::TreatFrameAsBlock(newFrame);
4620
// If the frame is a block frame, or if there is no previous line or if the
4621
// previous line is a block line or ended with a <br> then make a new line.
4622
if (isBlock || prevSibLine == end_lines() || prevSibLine->IsBlock() ||
4623
(aPrevSibling && aPrevSibling->GetType() == nsLayoutAtoms::brFrame)) {
4624
// Create a new line for the frame and add its line to the line
4626
nsLineBox* line = NS_NewLineBox(presShell, newFrame, 1, isBlock);
4628
return NS_ERROR_OUT_OF_MEMORY;
4630
if (prevSibLine != end_lines()) {
4631
// Append new line after prevSibLine
4632
mLines.after_insert(prevSibLine, line);
4636
// New line is going before the other lines
4637
mLines.push_front(line);
4638
prevSibLine = begin_lines();
4642
prevSibLine->SetChildCount(prevSibLine->GetChildCount() + 1);
4643
prevSibLine->MarkDirty();
4646
aPrevSibling = newFrame;
4647
newFrame = newFrame->GetNextSibling();
4649
if (prevSiblingNextFrame) {
4650
// Connect the last new frame to the remainder of the sibling list
4651
aPrevSibling->SetNextSibling(prevSiblingNextFrame);
4655
VerifyLines(PR_TRUE);
4661
nsBlockFrame::RemoveFrame(nsIPresContext* aPresContext,
4662
nsIPresShell& aPresShell,
4664
nsIFrame* aOldFrame)
4666
nsresult rv = NS_OK;
4668
#ifdef NOISY_REFLOW_REASON
4670
printf(": remove ");
4671
nsFrame::ListTag(stdout, aOldFrame);
4675
if (nsnull == aListName) {
4676
rv = DoRemoveFrame(aPresContext, aOldFrame);
4678
else if (mAbsoluteContainer.GetChildListName() == aListName) {
4679
return mAbsoluteContainer.RemoveFrame(this, aPresContext, aPresShell,
4680
aListName, aOldFrame);
4682
else if (nsLayoutAtoms::floatList == aListName) {
4683
// Find which line contains the float
4684
line_iterator line = begin_lines(), line_end = end_lines();
4685
for ( ; line != line_end; ++line) {
4686
if (line->IsInline() && line->RemoveFloat(aOldFrame)) {
4691
mFloats.DestroyFrame(aPresContext, aOldFrame);
4693
// Mark every line at and below the line where the float was dirty
4694
// XXXldb This could be done more efficiently.
4695
for ( ; line != line_end; ++line) {
4700
else if (nsLayoutAtoms::nextBidi == aListName) {
4701
// Skip the call to |ReflowDirtyChild| below by returning now.
4702
return DoRemoveFrame(aPresContext, aOldFrame);
4706
rv = NS_ERROR_INVALID_ARG;
4709
if (NS_SUCCEEDED(rv)) {
4710
// Ask the parent frame to reflow me.
4711
ReflowDirtyChild(&aPresShell, nsnull);
4717
nsBlockFrame::DoRemoveOutOfFlowFrame(nsIPresContext* aPresContext,
4720
// First remove aFrame's next in flow
4721
nsIFrame* nextInFlow;
4722
aFrame->GetNextInFlow(&nextInFlow);
4724
nsBlockFrame::DoRemoveOutOfFlowFrame(aPresContext, nextInFlow);
4726
// Now remove aFrame
4727
const nsStyleDisplay* display = aFrame->GetStyleDisplay();
4728
// find the containing block, this is either the parent or the grandparent
4729
// if the parent is an inline frame
4730
nsIFrame* parent = aFrame->GetParent();
4731
nsIAtom* parentType = parent->GetType();
4732
while ((nsLayoutAtoms::blockFrame != parentType) &&
4733
(nsLayoutAtoms::areaFrame != parentType)) {
4734
parent = parent->GetParent();
4735
parentType = parent->GetType();
4738
nsBlockFrame* block = (nsBlockFrame*)parent;
4739
// Remove aFrame from the appropriate list.
4740
if (display->IsAbsolutelyPositioned()) {
4741
block->mAbsoluteContainer.RemoveFrame(block, aPresContext,
4742
*(aPresContext->PresShell()),
4743
block->mAbsoluteContainer.GetChildListName(), aFrame);
4746
block->mFloats.RemoveFrame(aFrame);
4749
aFrame->Destroy(aPresContext);
4753
nsBlockFrame::DoRemoveFrame(nsIPresContext* aPresContext,
4754
nsIFrame* aDeletedFrame)
4756
// Clear our line cursor, since our lines may change.
4759
if (aDeletedFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
4760
DoRemoveOutOfFlowFrame(aPresContext, aDeletedFrame);
4764
nsIPresShell *presShell = aPresContext->PresShell();
4766
// Find the line and the previous sibling that contains
4767
// deletedFrame; we also find the pointer to the line.
4768
nsBlockFrame* flow = this;
4769
nsLineList& lines = flow->mLines;
4770
nsLineList::iterator line = lines.begin(),
4771
line_end = lines.end();
4772
nsIFrame* prevSibling = nsnull;
4773
for ( ; line != line_end; ++line) {
4774
nsIFrame* frame = line->mFirstChild;
4775
PRInt32 n = line->GetChildCount();
4777
if (frame == aDeletedFrame) {
4780
prevSibling = frame;
4781
frame = frame->GetNextSibling();
4785
if (line == line_end) {
4786
NS_ERROR("can't find deleted frame in lines");
4787
return NS_ERROR_FAILURE;
4789
NS_ASSERTION(!prevSibling || prevSibling->GetNextSibling() == aDeletedFrame, "bad prevSibling");
4791
// Remove frame and all of its continuations
4792
while (nsnull != aDeletedFrame) {
4793
while ((line != line_end) && (nsnull != aDeletedFrame)) {
4794
NS_ASSERTION(flow == aDeletedFrame->GetParent(), "messed up delete code");
4795
NS_ASSERTION(line->Contains(aDeletedFrame), "frame not in line");
4797
// See if the frame being deleted is the last one on the line
4798
PRBool isLastFrameOnLine = PR_FALSE;
4799
if (1 == line->GetChildCount()) {
4800
isLastFrameOnLine = PR_TRUE;
4802
else if (line->LastChild() == aDeletedFrame) {
4803
isLastFrameOnLine = PR_TRUE;
4806
// Remove aDeletedFrame from the line
4807
nsIFrame* nextFrame = aDeletedFrame->GetNextSibling();
4808
if (line->mFirstChild == aDeletedFrame) {
4809
line->mFirstChild = nextFrame;
4813
if (line != line_end && !line->IsBlock()) {
4814
// Since we just removed a frame that follows some inline
4815
// frames, we need to reflow the previous line.
4820
// Take aDeletedFrame out of the sibling list. Note that
4821
// prevSibling will only be nsnull when we are deleting the very
4824
prevSibling->SetNextSibling(nextFrame);
4827
// Update the child count of the line to be accurate
4828
PRInt32 lineChildCount = line->GetChildCount();
4830
line->SetChildCount(lineChildCount);
4832
// Destroy frame; capture its next-in-flow first in case we need
4833
// to destroy that too.
4834
nsIFrame* nextInFlow;
4835
aDeletedFrame->GetNextInFlow(&nextInFlow);
4836
#ifdef NOISY_REMOVE_FRAME
4837
printf("DoRemoveFrame: line=%p frame=", line);
4838
nsFrame::ListTag(stdout, aDeletedFrame);
4839
printf(" prevSibling=%p nextInFlow=%p\n", prevSibling, nextInFlow);
4841
aDeletedFrame->Destroy(aPresContext);
4842
aDeletedFrame = nextInFlow;
4844
// If line is empty, remove it now
4845
if (0 == lineChildCount) {
4846
nsLineBox *cur = line;
4847
line = lines.erase(line);
4848
// Invalidate the space taken up by the line.
4849
// XXX We need to do this if we're removing a frame as a result of
4850
// a call to RemoveFrame(), but we may not need to do this in all
4852
nsRect lineCombinedArea(cur->GetCombinedArea());
4853
#ifdef NOISY_BLOCK_INVALIDATE
4854
printf("%p invalidate 10 (%d, %d, %d, %d)\n",
4855
this, lineCombinedArea.x, lineCombinedArea.y, lineCombinedArea.width, lineCombinedArea.height);
4857
Invalidate(lineCombinedArea);
4858
cur->Destroy(presShell);
4860
// If we're removing a line, ReflowDirtyLines isn't going to
4861
// know that it needs to slide lines unless something is marked
4862
// dirty. So mark the previous margin of the next line dirty if
4864
if (line != line_end)
4865
line->MarkPreviousMarginDirty();
4868
// Make the line that just lost a frame dirty
4871
// If we just removed the last frame on the line then we need
4872
// to advance to the next line.
4873
if (isLastFrameOnLine) {
4878
// See if we should keep looking in the current flow's line list.
4879
if (nsnull != aDeletedFrame) {
4880
if (aDeletedFrame != nextFrame) {
4881
// The deceased frames continuation is not the next frame in
4882
// the current flow's frame list. Therefore we know that the
4883
// continuation is in a different parent. So break out of
4884
// the loop so that we advance to the next parent.
4885
NS_ASSERTION(aDeletedFrame->GetParent() != flow, "strange continuation");
4891
// Advance to next flow block if the frame has more continuations
4892
if (flow && aDeletedFrame) {
4893
flow = (nsBlockFrame*) flow->mNextInFlow;
4894
NS_ASSERTION(nsnull != flow, "whoops, continuation without a parent");
4895
// add defensive pointer check for bug 56894
4897
lines = flow->mLines;
4898
line = lines.begin();
4899
line_end = lines.end();
4900
prevSibling = nsnull;
4902
aDeletedFrame = nsnull;
4908
VerifyLines(PR_TRUE);
4914
nsBlockFrame::DeleteNextInFlowChild(nsIPresContext* aPresContext,
4915
nsIFrame* aNextInFlow)
4917
nsIFrame* prevInFlow;
4918
aNextInFlow->GetPrevInFlow(&prevInFlow);
4919
NS_PRECONDITION(prevInFlow, "bad next-in-flow");
4920
NS_PRECONDITION(IsChild(aPresContext, aNextInFlow), "bad geometric parent");
4924
prevInFlow->GetBidiProperty(aPresContext, nsLayoutAtoms::nextBidi,
4925
(void**) &nextBidi,sizeof(nextBidi));
4926
if (nextBidi != aNextInFlow) {
4928
DoRemoveFrame(aPresContext, aNextInFlow);
4934
////////////////////////////////////////////////////////////////////////
4938
nsBlockFrame::ReflowFloat(nsBlockReflowState& aState,
4939
nsPlaceholderFrame* aPlaceholder,
4940
nsFloatCache* aFloatCache,
4941
nsReflowStatus& aReflowStatus)
4943
// Delete the placeholder's next in flows, if any
4944
nsIFrame* nextInFlow;
4945
aPlaceholder->GetNextInFlow(&nextInFlow);
4947
// If aPlaceholder's parent is an inline, nextInFlow's will be a block.
4948
NS_STATIC_CAST(nsHTMLContainerFrame*, nextInFlow->GetParent())
4949
->DeleteNextInFlowChild(aState.mPresContext, nextInFlow);
4951
// Reflow the float.
4952
nsIFrame* floatFrame = aPlaceholder->GetOutOfFlowFrame();
4953
aReflowStatus = NS_FRAME_COMPLETE;
4956
printf("Reflow Float %p in parent %p, availSpace(%d,%d,%d,%d)\n",
4957
aPlaceholder->GetOutOfFlowFrame(), this,
4958
aState.mAvailSpaceRect.x, aState.mAvailSpaceRect.y,
4959
aState.mAvailSpaceRect.width, aState.mAvailSpaceRect.height
4963
// Compute the available width. By default, assume the width of the
4964
// containing block.
4966
if (aState.GetFlag(BRS_UNCONSTRAINEDWIDTH)) {
4967
availWidth = NS_UNCONSTRAINEDSIZE;
4970
const nsStyleDisplay* floatDisplay = floatFrame->GetStyleDisplay();
4972
nsIFrame* prevInFlow;
4973
floatFrame->GetPrevInFlow(&prevInFlow);
4974
// if the float is continued, constrain its width to the prev-in-flow's width
4976
availWidth = prevInFlow->GetRect().width;
4978
else if (NS_STYLE_DISPLAY_TABLE != floatDisplay->mDisplay ||
4979
eCompatibility_NavQuirks != aState.mPresContext->CompatibilityMode() ) {
4980
availWidth = aState.mContentArea.width;
4983
// This quirk matches the one in nsBlockReflowState::FlowAndPlaceFloat
4984
// give tables only the available space
4985
// if they can shrink we may not be constrained to place
4986
// them in the next line
4987
availWidth = aState.mAvailSpaceRect.width;
4988
// round down to twips per pixel so that we fit
4989
// needed when prev. float has procentage width
4990
// (maybe is a table flaw that makes table chose to round up
4991
// but i don't want to change that, too risky)
4994
aState.mPresContext->GetScaledPixelsToTwips(&p2t);
4995
twp = NSIntPixelsToTwips(1,p2t);
4996
availWidth -= availWidth % twp;
4999
nscoord availHeight = ((NS_UNCONSTRAINEDSIZE == aState.mAvailSpaceRect.height) ||
5000
(NS_UNCONSTRAINEDSIZE == aState.mContentArea.height))
5001
? NS_UNCONSTRAINEDSIZE
5002
: PR_MAX(0, aState.mContentArea.height - aState.mY);
5004
// If the float's width is automatic, we can't let the float's
5005
// width shrink below its maxElementWidth.
5006
const nsStylePosition* position = floatFrame->GetStylePosition();
5007
PRBool isAutoWidth = (eStyleUnit_Auto == position->mWidth.GetUnit());
5009
// We'll need to compute the max element size if either 1) we're
5010
// auto-width or 2) the state wanted us to compute it anyway.
5011
PRBool computeMaxElementWidth =
5012
isAutoWidth || aState.GetFlag(BRS_COMPUTEMAXELEMENTWIDTH);
5014
nsRect availSpace(aState.BorderPadding().left,
5015
aState.BorderPadding().top,
5016
availWidth, availHeight);
5018
// construct the html reflow state for the float. ReflowBlock will
5019
// initialize it and set its reason.
5020
nsHTMLReflowState floatRS(aState.mPresContext, aState.mReflowState,
5022
nsSize(availSpace.width, availSpace.height),
5023
aState.mReflowState.reason, PR_FALSE);
5024
// Setup a block reflow state to reflow the float.
5025
nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState,
5026
computeMaxElementWidth,
5027
aState.GetFlag(BRS_COMPUTEMAXWIDTH));
5030
PRBool isAdjacentWithTop = aState.IsAdjacentWithTop();
5032
nsCollapsingMargin margin;
5033
nsresult rv = brc.ReflowBlock(availSpace, PR_TRUE, margin, isAdjacentWithTop,
5034
aFloatCache->mOffsets, floatRS,
5036
// An incomplete reflow status means we should split the float
5037
// if the height is constrained (bug 145305).
5038
if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus) && (NS_UNCONSTRAINEDSIZE == availHeight))
5039
aReflowStatus = NS_FRAME_COMPLETE;
5041
if (NS_SUCCEEDED(rv) && isAutoWidth) {
5042
nscoord maxElementWidth = brc.GetMaxElementWidth();
5043
if (maxElementWidth > availSpace.width) {
5044
// The float's maxElementWidth is larger than the available
5045
// width. Reflow it again, this time pinning the width to the
5047
availSpace.width = maxElementWidth;
5048
nsCollapsingMargin marginMEW;
5049
// construct the html reflow state for the float.
5050
// ReflowBlock will initialize it and set its reason.
5051
nsHTMLReflowState redoFloatRS(aState.mPresContext, aState.mReflowState,
5053
nsSize(availSpace.width, availSpace.height),
5054
aState.mReflowState.reason, PR_FALSE);
5055
rv = brc.ReflowBlock(availSpace, PR_TRUE, marginMEW, isAdjacentWithTop,
5056
aFloatCache->mOffsets, redoFloatRS,
5061
// Remove the float from the reflow tree.
5062
if (aState.mReflowState.path)
5063
aState.mReflowState.path->RemoveChild(floatFrame);
5065
if (NS_FAILED(rv)) {
5069
// Capture the margin information for the caller
5070
const nsMargin& m = brc.GetMargin();
5071
aFloatCache->mMargins.top = brc.GetTopMargin();
5072
aFloatCache->mMargins.right = m.right;
5073
brc.GetCarriedOutBottomMargin().Include(m.bottom);
5074
aFloatCache->mMargins.bottom = brc.GetCarriedOutBottomMargin().get();
5075
aFloatCache->mMargins.left = m.left;
5077
const nsHTMLReflowMetrics& metrics = brc.GetMetrics();
5078
aFloatCache->mCombinedArea = metrics.mOverflowArea;
5080
// Set the rect, make sure the view is properly sized and positioned,
5081
// and tell the frame we're done reflowing it
5082
// XXXldb This seems like the wrong place to be doing this -- shouldn't
5083
// we be doing this in nsBlockReflowState::FlowAndPlaceFloat after
5084
// we've positioned the float, and shouldn't we be doing the equivalent
5085
// of |::PlaceFrameView| here?
5086
floatFrame->SetSize(nsSize(metrics.width, metrics.height));
5087
if (floatFrame->HasView()) {
5088
nsContainerFrame::SyncFrameViewAfterReflow(aState.mPresContext, floatFrame,
5089
floatFrame->GetView(),
5090
&metrics.mOverflowArea,
5091
NS_FRAME_NO_MOVE_VIEW);
5093
// Pass floatRS so the frame hierarchy can be used (redoFloatRS has the same hierarchy)
5094
floatFrame->DidReflow(aState.mPresContext, &floatRS,
5095
NS_FRAME_REFLOW_FINISHED);
5097
// If we computed it, then stash away the max-element-width for later
5098
if (aState.GetFlag(BRS_COMPUTEMAXELEMENTWIDTH)) {
5099
nscoord mew = brc.GetMaxElementWidth() +
5100
aFloatCache->mMargins.left + aFloatCache->mMargins.right;
5102
// This is all we need to do to include the float
5103
// max-element-width since we don't require that we end up with
5104
// content next to floats.
5105
aState.UpdateMaxElementWidth(mew); // fix for bug 13553
5107
// Allow the float width to be restored in state recovery.
5108
aFloatCache->mMaxElementWidth = mew;
5111
printf("end ReflowFloat %p, sized to %d,%d\n",
5112
floatFrame, metrics.width, metrics.height);
5115
// If the placeholder was continued and its first-in-flow was followed by a
5116
// <BR>, then cache the <BR>'s break type in aState.mFloatBreakType so that
5117
// the next frame after the placeholder can combine that break type with its own
5118
nsIFrame* prevPlaceholder = nsnull;
5119
aPlaceholder->GetPrevInFlow(&prevPlaceholder);
5120
if (prevPlaceholder) {
5121
// the break occurs only after the last continued placeholder
5122
PRBool lastPlaceholder = PR_TRUE;
5123
nsIFrame* next = aPlaceholder->GetNextSibling();
5125
if (nsLayoutAtoms::placeholderFrame == next->GetType()) {
5126
lastPlaceholder = PR_FALSE;
5129
if (lastPlaceholder) {
5130
// get the containing block of prevPlaceholder which is our prev-in-flow
5132
// get the break type of the last line in mPrevInFlow
5133
line_iterator endLine = --((nsBlockFrame*)mPrevInFlow)->end_lines();
5134
PRUint8 breakType = endLine->GetBreakType();
5135
if ((NS_STYLE_CLEAR_LEFT == breakType) ||
5136
(NS_STYLE_CLEAR_RIGHT == breakType) ||
5137
(NS_STYLE_CLEAR_LEFT_AND_RIGHT == breakType)) {
5138
aState.mFloatBreakType = breakType;
5141
else NS_ASSERTION(PR_FALSE, "no prev in flow");
5147
//////////////////////////////////////////////////////////////////////
5148
// Painting, event handling
5151
nsBlockFrame::GetSkipSides() const
5154
if (nsnull != mPrevInFlow) {
5155
skip |= 1 << NS_SIDE_TOP;
5157
if (nsnull != mNextInFlow) {
5158
skip |= 1 << NS_SIDE_BOTTOM;
5164
static void ComputeCombinedArea(nsLineList& aLines,
5165
nscoord aWidth, nscoord aHeight,
5168
nscoord xa = 0, ya = 0, xb = aWidth, yb = aHeight;
5169
for (nsLineList::iterator line = aLines.begin(), line_end = aLines.end();
5172
// Compute min and max x/y values for the reflowed frame's
5174
nsRect lineCombinedArea(line->GetCombinedArea());
5175
nscoord x = lineCombinedArea.x;
5176
nscoord y = lineCombinedArea.y;
5177
nscoord xmost = x + lineCombinedArea.width;
5178
nscoord ymost = y + lineCombinedArea.height;
5195
aResult.width = xb - xa;
5196
aResult.height = yb - ya;
5201
nsBlockFrame::IsVisibleForPainting(nsIPresContext * aPresContext,
5202
nsIRenderingContext& aRenderingContext,
5206
// first check to see if we are visible
5208
if (!GetStyleVisibility()->IsVisible()) {
5209
*aIsVisible = PR_FALSE;
5214
// Start by assuming we are visible and need to be painted
5215
*aIsVisible = PR_TRUE;
5217
// NOTE: GetSelectionforVisCheck checks the pagination to make sure we are printing
5218
// In otherwords, the selection will ALWAYS be null if we are not printing, meaning
5219
// the visibility will be TRUE in that case
5220
nsCOMPtr<nsISelection> selection;
5221
nsresult rv = GetSelectionForVisCheck(aPresContext, getter_AddRefs(selection));
5222
if (NS_SUCCEEDED(rv) && selection) {
5223
nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mContent));
5225
nsCOMPtr<nsIDOMHTMLHtmlElement> html(do_QueryInterface(mContent));
5226
nsCOMPtr<nsIDOMHTMLBodyElement> body(do_QueryInterface(mContent));
5228
if (!html && !body) {
5229
rv = selection->ContainsNode(node, PR_TRUE, aIsVisible);
5237
nsBlockFrame::PaintTextDecorationLines(nsIRenderingContext& aRenderingContext,
5243
aRenderingContext.SetColor(aColor);
5244
for (nsLineList::iterator line = begin_lines(), line_end = end_lines();
5245
line != line_end; ++line) {
5246
if (!line->IsBlock()) {
5247
aRenderingContext.FillRect(line->mBounds.x,
5248
line->mBounds.y + line->GetAscent() - aOffset,
5249
line->mBounds.width, aSize);
5255
nsBlockFrame::Paint(nsIPresContext* aPresContext,
5256
nsIRenderingContext& aRenderingContext,
5257
const nsRect& aDirtyRect,
5258
nsFramePaintLayer aWhichLayer,
5261
if (NS_FRAME_IS_UNFLOWABLE & mState) {
5266
if (gNoisyDamageRepair) {
5267
if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) {
5268
PRInt32 depth = GetDepth();
5270
::ComputeCombinedArea(mLines, mRect.width, mRect.height, ca);
5271
nsFrame::IndentBy(stdout, depth);
5273
printf(": bounds=%d,%d,%d,%d dirty=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
5274
mRect.x, mRect.y, mRect.width, mRect.height,
5275
aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height,
5276
ca.x, ca.y, ca.width, ca.height);
5281
if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) {
5282
PaintSelf(aPresContext, aRenderingContext, aDirtyRect);
5285
PRBool paintingSuppressed = PR_FALSE;
5286
aPresContext->PresShell()->IsPaintingSuppressed(&paintingSuppressed);
5287
if (paintingSuppressed)
5290
const nsStyleDisplay* disp = GetStyleDisplay();
5292
// If overflow is hidden then set the clip rect so that children don't
5293
// leak out of us. Note that because overflow'-clip' only applies to
5294
// the content area we do this after painting the border and background
5295
if (NS_STYLE_OVERFLOW_HIDDEN == disp->mOverflow) {
5296
aRenderingContext.PushState();
5297
SetOverflowClipRect(aRenderingContext);
5300
// Child elements have the opportunity to override the visibility
5301
// property and display even if the parent is hidden
5302
if (NS_FRAME_PAINT_LAYER_FLOATS == aWhichLayer) {
5303
PaintFloats(aPresContext, aRenderingContext, aDirtyRect);
5306
PaintDecorationsAndChildren(aPresContext, aRenderingContext,
5307
aDirtyRect, aWhichLayer, PR_TRUE);
5309
if (NS_STYLE_OVERFLOW_HIDDEN == disp->mOverflow) {
5311
aRenderingContext.PopState(clipState);
5315
if ((NS_FRAME_PAINT_LAYER_DEBUG == aWhichLayer) && GetShowFrameBorders()) {
5316
// Render the bands in the spacemanager
5317
nsSpaceManager* sm = mSpaceManager;
5320
nsBlockBandData band;
5321
band.Init(sm, nsSize(mRect.width, mRect.height));
5323
while (y < mRect.height) {
5325
band.GetAvailableSpace(y, availArea);
5327
// Render a box and a diagonal line through the band
5328
aRenderingContext.SetColor(NS_RGB(0,255,0));
5329
aRenderingContext.DrawRect(0, availArea.y,
5330
mRect.width, availArea.height);
5331
aRenderingContext.DrawLine(0, availArea.y,
5332
mRect.width, availArea.YMost());
5334
// Render boxes and opposite diagonal lines around the
5335
// unavailable parts of the band.
5337
for (i = 0; i < band.GetTrapezoidCount(); i++) {
5338
const nsBandTrapezoid* trapezoid = band.GetTrapezoid(i);
5339
if (nsBandTrapezoid::Available != trapezoid->mState) {
5340
nsRect r = trapezoid->GetRect();
5341
if (nsBandTrapezoid::OccupiedMultiple == trapezoid->mState) {
5342
aRenderingContext.SetColor(NS_RGB(0,255,128));
5345
aRenderingContext.SetColor(NS_RGB(128,255,0));
5347
aRenderingContext.DrawRect(r);
5348
aRenderingContext.DrawLine(r.x, r.YMost(), r.XMost(), r.y);
5351
y = availArea.YMost();
5361
nsBlockFrame::PaintFloats(nsIPresContext* aPresContext,
5362
nsIRenderingContext& aRenderingContext,
5363
const nsRect& aDirtyRect)
5365
for (nsIFrame* floatFrame = mFloats.FirstChild();
5367
floatFrame = floatFrame->GetNextSibling()) {
5368
PaintChild(aPresContext, aRenderingContext, aDirtyRect,
5369
floatFrame, NS_FRAME_PAINT_LAYER_BACKGROUND);
5370
PaintChild(aPresContext, aRenderingContext, aDirtyRect,
5371
floatFrame, NS_FRAME_PAINT_LAYER_FLOATS);
5372
PaintChild(aPresContext, aRenderingContext, aDirtyRect,
5373
floatFrame, NS_FRAME_PAINT_LAYER_FOREGROUND);
5378
static void DebugOutputDrawLine(nsFramePaintLayer aWhichLayer, PRInt32 aDepth,
5379
nsLineBox* aLine, PRBool aDrawn) {
5380
if (nsBlockFrame::gNoisyDamageRepair &&
5381
(NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer)) {
5382
nsFrame::IndentBy(stdout, aDepth+1);
5383
nsRect lineArea = aLine->GetCombinedArea();
5384
printf("%s line=%p bounds=%d,%d,%d,%d ca=%d,%d,%d,%d\n",
5385
aDrawn ? "draw" : "skip",
5386
NS_STATIC_CAST(void*, aLine),
5387
aLine->mBounds.x, aLine->mBounds.y,
5388
aLine->mBounds.width, aLine->mBounds.height,
5389
lineArea.x, lineArea.y,
5390
lineArea.width, lineArea.height);
5396
PaintLine(const nsRect& aLineArea, const nsRect& aDirtyRect,
5397
nsBlockFrame::line_iterator& aLine, PRInt32 aDepth,
5398
PRInt32& aDrawnLines, nsIPresContext* aPresContext,
5399
nsIRenderingContext& aRenderingContext,
5400
nsFramePaintLayer aWhichLayer, nsBlockFrame* aFrame) {
5401
// If the line's combined area (which includes child frames that
5402
// stick outside of the line's bounding box or our bounding box)
5403
// intersects the dirty rect then paint the line.
5404
if (aLineArea.Intersects(aDirtyRect)) {
5406
DebugOutputDrawLine(aWhichLayer, aDepth, aLine.get(), PR_TRUE);
5407
if (nsBlockFrame::gLamePaintMetrics) {
5411
nsIFrame* kid = aLine->mFirstChild;
5412
PRInt32 n = aLine->GetChildCount();
5414
aFrame->PaintChild(aPresContext, aRenderingContext, aDirtyRect, kid,
5416
kid = kid->GetNextSibling();
5421
DebugOutputDrawLine(aWhichLayer, aDepth, aLine.get(), PR_FALSE);
5427
nsBlockFrame::PaintChildren(nsIPresContext* aPresContext,
5428
nsIRenderingContext& aRenderingContext,
5429
const nsRect& aDirtyRect,
5430
nsFramePaintLayer aWhichLayer,
5433
PRInt32 drawnLines; // Will only be used if set (gLamePaintMetrics).
5436
if (gNoisyDamageRepair) {
5437
if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) {
5441
PRTime start = LL_ZERO; // Initialize these variables to silence the compiler.
5442
if (gLamePaintMetrics) {
5448
nsLineBox* cursor = GetFirstLineContaining(aDirtyRect.y);
5449
line_iterator line_end = end_lines();
5452
for (line_iterator line = mLines.begin(cursor);
5455
nsRect lineArea = line->GetCombinedArea();
5456
if (!lineArea.IsEmpty()) {
5457
// Because we have a cursor, the combinedArea.ys are non-decreasing.
5458
// Once we've passed aDirtyRect.YMost(), we can never see it again.
5459
if (lineArea.y >= aDirtyRect.YMost()) {
5462
PaintLine(lineArea, aDirtyRect, line, depth, drawnLines, aPresContext,
5463
aRenderingContext, aWhichLayer, this);
5467
PRBool nonDecreasingYs = PR_TRUE;
5468
PRInt32 lineCount = 0;
5469
nscoord lastY = PR_INT32_MIN;
5470
nscoord lastYMost = PR_INT32_MIN;
5471
for (line_iterator line = begin_lines();
5474
nsRect lineArea = line->GetCombinedArea();
5475
if (!lineArea.IsEmpty()) {
5476
if (lineArea.y < lastY
5477
|| lineArea.YMost() < lastYMost) {
5478
nonDecreasingYs = PR_FALSE;
5481
lastYMost = lineArea.YMost();
5483
PaintLine(lineArea, aDirtyRect, line, depth, drawnLines, aPresContext,
5484
aRenderingContext, aWhichLayer, this);
5489
if (nonDecreasingYs && lineCount >= MIN_LINES_NEEDING_CURSOR) {
5494
if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer) {
5495
if ((nsnull != mBullet) && HaveOutsideBullet()) {
5496
// Paint outside bullets manually
5497
PaintChild(aPresContext, aRenderingContext, aDirtyRect, mBullet,
5503
if (gLamePaintMetrics) {
5504
PRTime end = PR_Now();
5506
PRInt32 numLines = mLines.size();
5507
if (!numLines) numLines = 1;
5508
PRTime lines, deltaPerLine, delta;
5509
LL_I2L(lines, numLines);
5510
LL_SUB(delta, end, start);
5511
LL_DIV(deltaPerLine, delta, lines);
5515
PR_snprintf(buf, sizeof(buf),
5516
": %lld elapsed (%lld per line) lines=%d drawn=%d skip=%d",
5517
delta, deltaPerLine,
5518
numLines, drawnLines, numLines - drawnLines);
5519
printf("%s\n", buf);
5524
// XXXldb Does this handle all overlap cases correctly? (How?)
5526
nsBlockFrame::GetClosestLine(nsILineIterator *aLI,
5527
const nsPoint &aOrigin,
5528
const nsPoint &aPoint,
5529
PRInt32 &aClosestLine)
5532
return NS_ERROR_NULL_POINTER;
5536
PRInt32 lineFrameCount;
5537
nsIFrame *firstFrame;
5540
nsresult result = aLI->GetNumLines(&numLines);
5542
if (NS_FAILED(result) || numLines < 0)
5543
return NS_OK;//do not handle
5545
PRInt32 shifted = numLines;
5546
PRInt32 start = 0, midpoint = 0;
5551
// Cut the number of lines to look at in half and
5552
// calculate the midpoint of the region we are looking at.
5554
shifted >>= 1; //divide by 2
5555
midpoint = start + shifted;
5557
// Get the dimensions of the line that is at the half
5558
// point of the region we are looking at.
5560
result = aLI->GetLine(midpoint, &firstFrame, &lineFrameCount,rect,&flags);
5561
if (NS_FAILED(result))
5562
break;//do not handle
5564
// Check to see if our point lies with the line's Y bounds.
5566
rect+=aOrigin; //offset origin to get comparative coordinates
5568
y = aPoint.y - rect.y;
5569
if (y >=0 && (aPoint.y < (rect.y+rect.height)))
5571
aClosestLine = midpoint; //spot on!
5577
// If we get here, no match was found above, so aPoint.y must
5578
// be greater than the Y bounds of the current line rect. Move
5579
// our starting point just beyond the midpoint of the current region.
5583
if (numLines > 1 && start < (numLines - 1))
5590
// Make sure we don't go off the edge in either direction!
5592
NS_ASSERTION(start >=0 && start <= numLines, "Invalid start calculated.");
5596
else if (start >= numLines)
5597
start = numLines - 1;
5599
aClosestLine = start; //close as we could come
5605
nsBlockFrame::HandleEvent(nsIPresContext* aPresContext,
5607
nsEventStatus* aEventStatus)
5611
nsIPresShell *shell = nsnull;
5612
if (aEvent->message == NS_MOUSE_MOVE) {
5613
shell = aPresContext->GetPresShell();
5616
nsCOMPtr<nsIFrameSelection> frameSelection;
5617
PRBool mouseDown = PR_FALSE;
5618
//check to see if we need to ask the selection controller..
5619
if (mState & NS_FRAME_INDEPENDENT_SELECTION)
5621
nsCOMPtr<nsISelectionController> selCon;
5622
result = GetSelectionController(aPresContext, getter_AddRefs(selCon));
5623
if (NS_FAILED(result) || !selCon)
5624
return result?result:NS_ERROR_FAILURE;
5625
frameSelection = do_QueryInterface(selCon);
5628
shell->GetFrameSelection(getter_AddRefs(frameSelection));
5629
if (!frameSelection || NS_FAILED(frameSelection->GetMouseDownState(&mouseDown)) || !mouseDown)
5630
return NS_OK;//do not handle
5633
if (aEvent->message == NS_MOUSE_LEFT_BUTTON_DOWN || aEvent->message == NS_MOUSE_MOVE ||
5634
aEvent->message == NS_MOUSE_LEFT_DOUBLECLICK ) {
5636
nsMouseEvent *me = (nsMouseEvent *)aEvent;
5638
nsIFrame *resultFrame = nsnull;//this will be passed the handle event when we
5639
//can tell who to pass it to
5640
nsIFrame *mainframe = this;
5641
shell = aPresContext->GetPresShell();
5644
nsCOMPtr<nsIFocusTracker> tracker( do_QueryInterface(shell, &result) );
5646
nsCOMPtr<nsILineIterator> it( do_QueryInterface(mainframe, &result) );
5647
nsIView* parentWithView;
5649
nsPeekOffsetStruct pos;
5651
while(NS_OK == result)
5652
{ //we are starting aloop to allow us to "drill down to the one we want"
5653
mainframe->GetOffsetFromView(aPresContext, origin, &parentWithView);
5655
if (NS_FAILED(result))
5656
return NS_OK;//do not handle
5657
PRInt32 closestLine;
5659
if (NS_FAILED(result = GetClosestLine(it,origin,aEvent->point,closestLine)))
5663
//we will now ask where to go. if we cant find what we want"aka another block frame"
5664
//we drill down again
5665
pos.mTracker = tracker;
5666
pos.mDirection = eDirNext;
5667
pos.mDesiredX = aEvent->point.x;
5668
pos.mScrollViewStop = PR_FALSE;
5669
pos.mIsKeyboardSelect = PR_FALSE;
5670
result = nsFrame::GetNextPrevLineFromeBlockFrame(aPresContext,
5677
if (NS_SUCCEEDED(result) && pos.mResultFrame){
5678
if (result == NS_OK)
5679
it = do_QueryInterface(pos.mResultFrame, &result);//if this fails thats ok
5680
resultFrame = pos.mResultFrame;
5681
mainframe = resultFrame;
5684
break;//time to go nothing was found
5686
//end while loop. if nssucceeded resutl then keep going that means
5687
//we have successfully hit another block frame and we should keep going.
5692
if (NS_POSITION_BEFORE_TABLE == result)
5694
nsCOMPtr<nsISelectionController> selCon;
5695
result = GetSelectionController(aPresContext, getter_AddRefs(selCon));
5696
//get the selection controller
5697
if (NS_SUCCEEDED(result) && selCon)
5699
PRInt16 displayresult;
5700
selCon->GetDisplaySelection(&displayresult);
5701
if (displayresult == nsISelectionController::SELECTION_OFF)
5702
return NS_OK;//nothing to do we cannot affect selection from here
5704
nsCOMPtr<nsIFrameSelection> frameselection;
5705
shell->GetFrameSelection(getter_AddRefs(frameselection));
5706
PRBool mouseDown = aEvent->message == NS_MOUSE_MOVE;
5709
result = frameselection->HandleClick(pos.mResultContent, pos.mContentOffset,
5710
pos.mContentOffsetEnd, mouseDown || me->isShift, PR_FALSE, pos.mPreferLeft);
5714
result = resultFrame->HandleEvent(aPresContext, aEvent, aEventStatus);//else let the frame/container do what it needs
5715
if (aEvent->message == NS_MOUSE_LEFT_BUTTON_DOWN && !IsMouseCaptured(aPresContext))
5716
CaptureMouse(aPresContext, PR_TRUE);
5721
/*we have to add this because any frame that overrides nsFrame::HandleEvent for mouse down MUST capture the mouse events!!
5722
if (aEvent->message == NS_MOUSE_LEFT_BUTTON_DOWN && !IsMouseCaptured(aPresContext))
5723
CaptureMouse(aPresContext, PR_TRUE);*/
5724
return NS_OK; //just stop it
5727
return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
5730
void nsBlockFrame::ClearLineCursor() {
5731
if (!(GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR)) {
5735
GetProperty(GetPresContext(), nsLayoutAtoms::lineCursorProperty, PR_TRUE);
5736
RemoveStateBits(NS_BLOCK_HAS_LINE_CURSOR);
5739
void nsBlockFrame::SetupLineCursor() {
5740
if (GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR
5741
|| mLines.empty()) {
5745
SetProperty(GetPresContext(), nsLayoutAtoms::lineCursorProperty,
5746
mLines.front(), nsnull);
5747
AddStateBits(NS_BLOCK_HAS_LINE_CURSOR);
5750
nsLineBox* nsBlockFrame::GetFirstLineContaining(nscoord y) {
5751
if (!(GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR)) {
5755
nsLineBox* property = NS_STATIC_CAST(nsLineBox*,
5756
GetProperty(GetPresContext(), nsLayoutAtoms::lineCursorProperty, PR_FALSE));
5757
line_iterator cursor = mLines.begin(property);
5758
nsRect cursorArea = cursor->GetCombinedArea();
5760
while ((cursorArea.IsEmpty() || cursorArea.YMost() > y)
5761
&& cursor != mLines.front()) {
5762
cursor = cursor.prev();
5763
cursorArea = cursor->GetCombinedArea();
5765
while ((cursorArea.IsEmpty() || cursorArea.YMost() <= y)
5766
&& cursor != mLines.back()) {
5767
cursor = cursor.next();
5768
cursorArea = cursor->GetCombinedArea();
5771
if (cursor.get() != property) {
5772
SetProperty(GetPresContext(), nsLayoutAtoms::lineCursorProperty,
5773
cursor.get(), nsnull);
5776
return cursor.get();
5780
GetFrameFromLine(const nsRect& aLineArea, const nsPoint& aTmp,
5781
nsBlockFrame::line_iterator& aLine, nsIPresContext* aPresContext,
5782
nsFramePaintLayer aWhichLayer, nsIFrame** aFrame) {
5783
if (aLineArea.Contains(aTmp)) {
5784
nsIFrame* kid = aLine->mFirstChild;
5785
PRInt32 n = aLine->GetChildCount();
5788
nsresult rv = kid->GetFrameForPoint(aPresContext, aTmp, aWhichLayer, &hit);
5790
if (NS_SUCCEEDED(rv) && hit) {
5793
kid = kid->GetNextSibling();
5798
// Optimized function that uses line combined areas to skip lines
5799
// we know can't contain the point
5801
nsBlockFrame::GetFrameForPointUsing(nsIPresContext* aPresContext,
5802
const nsPoint& aPoint,
5804
nsFramePaintLayer aWhichLayer,
5805
PRBool aConsiderSelf,
5809
return nsContainerFrame::GetFrameForPointUsing(aPresContext,
5810
aPoint, aList, aWhichLayer, aConsiderSelf, aFrame);
5813
PRBool inThisFrame = mRect.Contains(aPoint);
5815
if (! ((mState & NS_FRAME_OUTSIDE_CHILDREN) || inThisFrame ) ) {
5816
return NS_ERROR_FAILURE;
5820
nsPoint tmp(aPoint.x - mRect.x, aPoint.y - mRect.y);
5822
nsPoint originOffset;
5823
nsIView *view = nsnull;
5824
nsresult rv = GetOriginToViewOffset(aPresContext, originOffset, &view);
5826
if (NS_SUCCEEDED(rv) && view)
5827
tmp += originOffset;
5829
nsLineBox* cursor = GetFirstLineContaining(tmp.y);
5830
line_iterator line_end = end_lines();
5833
// This is the fast path for large blocks
5834
for (line_iterator line = mLines.begin(cursor);
5837
nsRect lineArea = line->GetCombinedArea();
5838
// Because we have a cursor, the combinedArea.ys are non-decreasing.
5839
// Once we've passed tmp.y, we can never see it again.
5840
if (!lineArea.IsEmpty()) {
5841
if (lineArea.y > tmp.y) {
5844
GetFrameFromLine(lineArea, tmp, line, aPresContext, aWhichLayer, aFrame);
5848
PRBool nonDecreasingYs = PR_TRUE;
5849
PRInt32 lineCount = 0;
5850
nscoord lastY = PR_INT32_MIN;
5851
nscoord lastYMost = PR_INT32_MIN;
5852
for (line_iterator line = mLines.begin();
5855
nsRect lineArea = line->GetCombinedArea();
5856
if (!lineArea.IsEmpty()) {
5857
if (lineArea.y < lastY
5858
|| lineArea.YMost() < lastYMost) {
5859
nonDecreasingYs = PR_FALSE;
5862
lastYMost = lineArea.YMost();
5864
GetFrameFromLine(lineArea, tmp, line, aPresContext, aWhichLayer, aFrame);
5869
if (nonDecreasingYs && lineCount >= MIN_LINES_NEEDING_CURSOR) {
5878
if ( inThisFrame && aConsiderSelf ) {
5879
if (GetStyleVisibility()->IsVisible()) {
5885
return NS_ERROR_FAILURE;
5889
nsBlockFrame::GetFrameForPoint(nsIPresContext* aPresContext,
5890
const nsPoint& aPoint,
5891
nsFramePaintLayer aWhichLayer,
5896
switch (aWhichLayer) {
5897
case NS_FRAME_PAINT_LAYER_FOREGROUND:
5898
rv = GetFrameForPointUsing(aPresContext, aPoint, nsnull, NS_FRAME_PAINT_LAYER_FOREGROUND, PR_FALSE, aFrame);
5902
if (nsnull != mBullet) {
5903
rv = GetFrameForPointUsing(aPresContext, aPoint, nsLayoutAtoms::bulletList, NS_FRAME_PAINT_LAYER_FOREGROUND, PR_FALSE, aFrame);
5908
case NS_FRAME_PAINT_LAYER_FLOATS:
5909
// we painted our floats before our children, and thus
5910
// we should check floats within children first
5911
rv = GetFrameForPointUsing(aPresContext, aPoint, nsnull, NS_FRAME_PAINT_LAYER_FLOATS, PR_FALSE, aFrame);
5915
if (mFloats.NotEmpty()) {
5917
rv = GetFrameForPointUsing(aPresContext, aPoint,
5918
nsLayoutAtoms::floatList,
5919
NS_FRAME_PAINT_LAYER_FOREGROUND,
5925
rv = GetFrameForPointUsing(aPresContext, aPoint,
5926
nsLayoutAtoms::floatList,
5927
NS_FRAME_PAINT_LAYER_FLOATS,
5933
return GetFrameForPointUsing(aPresContext, aPoint,
5934
nsLayoutAtoms::floatList,
5935
NS_FRAME_PAINT_LAYER_BACKGROUND,
5939
return NS_ERROR_FAILURE;
5943
case NS_FRAME_PAINT_LAYER_BACKGROUND:
5944
// we're a block, so PR_TRUE for consider self
5945
return GetFrameForPointUsing(aPresContext, aPoint, nsnull,
5946
NS_FRAME_PAINT_LAYER_BACKGROUND,
5950
// we shouldn't get here
5951
NS_ASSERTION(PR_FALSE, "aWhichLayer was not understood");
5952
return NS_ERROR_FAILURE;
5956
nsBlockFrame::ReflowDirtyChild(nsIPresShell* aPresShell, nsIFrame* aChild)
5960
IndentBy(stdout, gNoiseIndent);
5962
printf(": ReflowDirtyChild (");
5964
nsFrame::ListTag(stdout, aChild);
5974
// See if the child is absolutely positioned
5975
if (aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
5976
const nsStyleDisplay* disp = aChild->GetStyleDisplay();
5978
if (disp->IsAbsolutelyPositioned()) {
5979
// Generate a reflow command to reflow our dirty absolutely
5980
// positioned child frames.
5981
// XXX Note that we don't currently try and coalesce the reflow commands,
5982
// although we should. We can't use the NS_FRAME_HAS_DIRTY_CHILDREN
5983
// flag, because that's used to indicate whether in-flow children are
5985
nsHTMLReflowCommand* reflowCmd;
5986
nsresult rv = NS_NewHTMLReflowCommand(&reflowCmd, this,
5987
eReflowType_ReflowDirty);
5988
if (NS_SUCCEEDED(rv)) {
5989
reflowCmd->SetChildListName(mAbsoluteContainer.GetChildListName());
5990
aPresShell->AppendReflowCommand(reflowCmd);
5995
IndentBy(stdout, gNoiseIndent);
5996
printf("scheduled reflow command for absolutely positioned frame\n");
6005
// Mark the line containing the child frame dirty.
6006
line_iterator fline = FindLineFor(aChild);
6007
if (fline != end_lines())
6008
MarkLineDirty(fline);
6011
// Either generate a reflow command to reflow the dirty child or
6012
// coalesce this reflow request with an existing reflow command
6013
if (!(mState & NS_FRAME_HAS_DIRTY_CHILDREN)) {
6014
// If this is the first dirty child,
6015
// post a dirty children reflow command targeted at yourself
6016
mState |= NS_FRAME_HAS_DIRTY_CHILDREN;
6018
nsFrame::CreateAndPostReflowCommand(aPresShell, this,
6019
eReflowType_ReflowDirty, nsnull, nsnull, nsnull);
6023
IndentBy(stdout, gNoiseIndent);
6024
printf("scheduled reflow command targeted at self\n");
6038
//////////////////////////////////////////////////////////////////////
6043
InLineList(nsLineList& aLines, nsIFrame* aFrame)
6045
for (nsLineList::iterator line = aLines.begin(), line_end = aLines.end();
6048
nsIFrame* frame = line->mFirstChild;
6049
PRInt32 n = line->GetChildCount();
6051
if (frame == aFrame) {
6054
frame = frame->GetNextSibling();
6061
InSiblingList(nsLineList& aLines, nsIFrame* aFrame)
6063
if (! aLines.empty()) {
6064
nsIFrame* frame = aLines.front()->mFirstChild;
6066
if (frame == aFrame) {
6069
frame = frame->GetNextSibling();
6076
nsBlockFrame::IsChild(nsIPresContext* aPresContext, nsIFrame* aFrame)
6078
// Continued out-of-flows don't satisfy InLineList(), continued out-of-flows
6079
// and placeholders don't satisfy InSiblingList().
6080
PRBool skipLineList = PR_FALSE;
6081
PRBool skipSiblingList = PR_FALSE;
6082
nsIFrame* prevInFlow;
6083
aFrame->GetPrevInFlow(&prevInFlow);
6085
nsFrameState state = aFrame->GetStateBits();
6086
skipLineList = (state & NS_FRAME_OUT_OF_FLOW);
6087
skipSiblingList = (nsLayoutAtoms::placeholderFrame == aFrame->GetType()) ||
6088
(state & NS_FRAME_OUT_OF_FLOW);
6091
if (aFrame->GetParent() != (nsIFrame*)this) {
6094
if ((skipLineList || InLineList(mLines, aFrame)) &&
6095
(skipSiblingList || InSiblingList(mLines, aFrame))) {
6098
nsLineList* overflowLines = GetOverflowLines(aPresContext, PR_FALSE);
6099
if (overflowLines && (skipLineList || InLineList(*overflowLines, aFrame)) &&
6100
(skipSiblingList || InSiblingList(*overflowLines, aFrame))) {
6107
nsBlockFrame::VerifyTree() const
6115
//////////////////////////////////////////////////////////////////////
6118
nsBlockFrame::Init(nsIPresContext* aPresContext,
6119
nsIContent* aContent,
6121
nsStyleContext* aContext,
6122
nsIFrame* aPrevInFlow)
6125
// Copy over the block/area frame type flags
6126
nsBlockFrame* blockFrame = (nsBlockFrame*)aPrevInFlow;
6128
SetFlags(blockFrame->mState & NS_BLOCK_FLAGS_MASK);
6131
nsresult rv = nsBlockFrameSuper::Init(aPresContext, aContent, aParent,
6132
aContext, aPrevInFlow);
6137
nsBlockFrame::SetInitialChildList(nsIPresContext* aPresContext,
6139
nsIFrame* aChildList)
6141
nsresult rv = NS_OK;
6143
if (mAbsoluteContainer.GetChildListName() == aListName) {
6144
mAbsoluteContainer.SetInitialChildList(this, aPresContext, aListName, aChildList);
6146
else if (nsLayoutAtoms::floatList == aListName) {
6147
mFloats.SetFrames(aChildList);
6151
// Lookup up the two pseudo style contexts
6152
if (nsnull == mPrevInFlow) {
6153
nsRefPtr<nsStyleContext> firstLetterStyle = GetFirstLetterStyle(aPresContext);
6154
if (nsnull != firstLetterStyle) {
6155
mState |= NS_BLOCK_HAS_FIRST_LETTER_STYLE;
6156
#ifdef NOISY_FIRST_LETTER
6158
printf(": first-letter style found\n");
6163
rv = AddFrames(aPresContext, aChildList, nsnull);
6164
if (NS_FAILED(rv)) {
6168
// Create list bullet if this is a list-item. Note that this is done
6169
// here so that RenumberLists will work (it needs the bullets to
6170
// store the bullet numbers).
6171
const nsStyleDisplay* styleDisplay = GetStyleDisplay();
6172
if ((nsnull == mPrevInFlow) &&
6173
(NS_STYLE_DISPLAY_LIST_ITEM == styleDisplay->mDisplay) &&
6174
(nsnull == mBullet)) {
6175
// Resolve style for the bullet frame
6176
const nsStyleList* styleList = GetStyleList();
6177
nsIAtom *pseudoElement;
6178
switch (styleList->mListStyleType) {
6179
case NS_STYLE_LIST_STYLE_DISC:
6180
case NS_STYLE_LIST_STYLE_CIRCLE:
6181
case NS_STYLE_LIST_STYLE_SQUARE:
6182
pseudoElement = nsCSSPseudoElements::mozListBullet;
6185
pseudoElement = nsCSSPseudoElements::mozListNumber;
6189
nsIPresShell *shell = aPresContext->PresShell();
6191
nsRefPtr<nsStyleContext> kidSC = shell->StyleSet()->
6192
ResolvePseudoStyleFor(mContent, pseudoElement, mStyleContext);
6194
// Create bullet frame
6195
mBullet = new (shell) nsBulletFrame;
6197
if (nsnull == mBullet) {
6198
return NS_ERROR_OUT_OF_MEMORY;
6200
mBullet->Init(aPresContext, mContent, this, kidSC, nsnull);
6202
// If the list bullet frame should be positioned inside then add
6203
// it to the flow now.
6204
if (NS_STYLE_LIST_STYLE_POSITION_INSIDE ==
6205
styleList->mListStylePosition) {
6206
AddFrames(aPresContext, mBullet, nsnull);
6207
mState &= ~NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET;
6210
mState |= NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET;
6219
nsBlockFrame::FrameStartsCounterScope(nsIFrame* aFrame)
6221
const nsStyleContent* styleContent = aFrame->GetStyleContent();
6222
if (0 != styleContent->CounterResetCount()) {
6230
nsBlockFrame::RenumberLists(nsIPresContext* aPresContext)
6232
if (!FrameStartsCounterScope(this)) {
6233
// If this frame doesn't start a counter scope then we don't need
6234
// to renumber child list items.
6238
// Setup initial list ordinal value
6239
// XXX Map html's start property to counter-reset style
6240
PRInt32 ordinal = 1;
6242
nsCOMPtr<nsIHTMLContent> hc(do_QueryInterface(mContent));
6246
if (NS_CONTENT_ATTR_HAS_VALUE ==
6247
hc->GetHTMLAttribute(nsHTMLAtoms::start, value)) {
6248
if (eHTMLUnit_Integer == value.GetUnit()) {
6249
ordinal = value.GetIntValue();
6254
// Get to first-in-flow
6255
nsBlockFrame* block = (nsBlockFrame*) GetFirstInFlow();
6256
RenumberListsInBlock(aPresContext, block, &ordinal, 0);
6260
nsBlockFrame::RenumberListsInBlock(nsIPresContext* aPresContext,
6261
nsBlockFrame* aBlockFrame,
6265
PRBool renumberedABullet = PR_FALSE;
6267
while (nsnull != aBlockFrame) {
6268
// Examine each line in the block
6269
for (line_iterator line = aBlockFrame->begin_lines(),
6270
line_end = aBlockFrame->end_lines();
6273
nsIFrame* kid = line->mFirstChild;
6274
PRInt32 n = line->GetChildCount();
6276
PRBool kidRenumberedABullet = RenumberListsFor(aPresContext, kid, aOrdinal, aDepth);
6277
if (kidRenumberedABullet) {
6279
renumberedABullet = PR_TRUE;
6281
kid = kid->GetNextSibling();
6285
// Advance to the next continuation
6286
aBlockFrame->GetNextInFlow((nsIFrame**) &aBlockFrame);
6289
return renumberedABullet;
6293
nsBlockFrame::RenumberListsFor(nsIPresContext* aPresContext,
6298
NS_PRECONDITION(aPresContext && aKid && aOrdinal, "null params are immoral!");
6300
// add in a sanity check for absurdly deep frame trees. See bug 42138
6301
if (MAX_DEPTH_FOR_LIST_RENUMBERING < aDepth)
6304
PRBool kidRenumberedABullet = PR_FALSE;
6305
nsIFrame* kid = aKid;
6307
// if the frame is a placeholder, then get the out of flow frame
6308
if (nsLayoutAtoms::placeholderFrame == aKid->GetType()) {
6309
kid = NS_STATIC_CAST(nsPlaceholderFrame*, aKid)->GetOutOfFlowFrame();
6310
NS_ASSERTION(kid, "no out-of-flow frame");
6313
// If the frame is a list-item and the frame implements our
6314
// block frame API then get its bullet and set the list item
6316
const nsStyleDisplay* display = kid->GetStyleDisplay();
6317
if (NS_STYLE_DISPLAY_LIST_ITEM == display->mDisplay) {
6318
// Make certain that the frame is a block frame in case
6319
// something foreign has crept in.
6320
nsBlockFrame* listItem;
6321
nsresult rv = kid->QueryInterface(kBlockFrameCID, (void**)&listItem);
6322
if (NS_SUCCEEDED(rv)) {
6323
if (nsnull != listItem->mBullet) {
6325
*aOrdinal = listItem->mBullet->SetListItemOrdinal(*aOrdinal,
6328
kidRenumberedABullet = PR_TRUE;
6330
// Invalidate the bullet content area since it may look different now
6331
nsRect damageRect(nsPoint(0, 0), listItem->mBullet->GetSize());
6332
listItem->mBullet->Invalidate(damageRect);
6336
// XXX temporary? if the list-item has child list-items they
6337
// should be numbered too; especially since the list-item is
6338
// itself (ASSUMED!) not to be a counter-resetter.
6339
PRBool meToo = RenumberListsInBlock(aPresContext, listItem, aOrdinal, aDepth + 1);
6341
kidRenumberedABullet = PR_TRUE;
6345
else if (NS_STYLE_DISPLAY_BLOCK == display->mDisplay) {
6346
if (FrameStartsCounterScope(kid)) {
6347
// Don't bother recursing into a block frame that is a new
6348
// counter scope. Any list-items in there will be handled by
6352
// If the display=block element is a block frame then go ahead
6353
// and recurse into it, as it might have child list-items.
6354
nsBlockFrame* kidBlock;
6355
nsresult rv = kid->QueryInterface(kBlockFrameCID, (void**) &kidBlock);
6356
if (NS_SUCCEEDED(rv)) {
6357
kidRenumberedABullet = RenumberListsInBlock(aPresContext, kidBlock, aOrdinal, aDepth + 1);
6361
return kidRenumberedABullet;
6365
nsBlockFrame::ReflowBullet(nsBlockReflowState& aState,
6366
nsHTMLReflowMetrics& aMetrics)
6368
// Reflow the bullet now
6370
availSize.width = NS_UNCONSTRAINEDSIZE;
6371
availSize.height = NS_UNCONSTRAINEDSIZE;
6373
// Get the reason right.
6374
// XXXwaterson Should this look just like the logic in
6375
// nsBlockReflowContext::ReflowBlock and nsLineLayout::ReflowFrame?
6376
const nsHTMLReflowState &rs = aState.mReflowState;
6377
nsReflowReason reason = rs.reason;
6378
if (reason == eReflowReason_Incremental) {
6379
if (! rs.path->HasChild(mBullet)) {
6380
// An incremental reflow not explicitly destined to (or through)
6381
// the child should be treated as a resize...
6382
reason = eReflowReason_Resize;
6385
// ...unless its an incremental `style changed' reflow targeted at
6386
// the block, in which case, we propagate that to its children.
6387
nsHTMLReflowCommand *command = rs.path->mReflowCommand;
6390
command->GetType(type);
6391
if (type == eReflowType_StyleChanged)
6392
reason = eReflowReason_StyleChange;
6396
nsHTMLReflowState reflowState(aState.mPresContext, rs,
6397
mBullet, availSize, reason);
6398
nsReflowStatus status;
6399
mBullet->WillReflow(aState.mPresContext);
6400
mBullet->Reflow(aState.mPresContext, aMetrics, reflowState, status);
6402
// Place the bullet now; use its right margin to distance it
6403
// from the rest of the frames in the line
6406
(rs.availableWidth != NS_UNCONSTRAINEDSIZE &&
6407
NS_STYLE_DIRECTION_RTL == GetStyleVisibility()->mDirection)
6408
// According to the CSS2 spec, section 12.6.1, outside marker box
6409
// is distanced from the associated principal box's border edge.
6410
// |rs.availableWidth| reflects exactly a border edge: it includes
6411
// border, padding, and content area, without margins.
6412
? rs.availableWidth + reflowState.mComputedMargin.left :
6414
- reflowState.mComputedMargin.right - aMetrics.width;
6416
// Approximate the bullets position; vertical alignment will provide
6417
// the final vertical location.
6418
const nsMargin& bp = aState.BorderPadding();
6420
mBullet->SetRect(nsRect(x, y, aMetrics.width, aMetrics.height));
6421
mBullet->DidReflow(aState.mPresContext, &aState.mReflowState, NS_FRAME_REFLOW_FINISHED);
6424
//XXX get rid of this -- its slow
6426
nsBlockFrame::BuildFloatList()
6428
// Accumulate float list into mFloats.
6429
// Use the float cache to speed up searching the lines for floats.
6430
nsIFrame* head = nsnull;
6431
nsIFrame* current = nsnull;
6432
for (nsBlockFrame::line_iterator line = mLines.begin(), line_end = mLines.end();
6435
if (line->HasFloats()) {
6436
nsFloatCache* fc = line->GetFirstFloat();
6438
nsIFrame* floatFrame = fc->mPlaceholder->GetOutOfFlowFrame();
6440
current = head = floatFrame;
6442
current->SetNextSibling(floatFrame);
6443
current = floatFrame;
6450
// Terminate end of float list just in case a float was removed
6452
current->SetNextSibling(nsnull);
6454
mFloats.SetFrames(head);
6456
// ensure that the floats in the overflow lines are put on a child list
6457
// and not dropped from the frame tree!
6458
// Note that overflow lines do not have any float cache set up for them,
6459
// because the float cache contains only laid-out floats
6460
nsLineList* overflowLines = GetOverflowLines(GetPresContext(), PR_FALSE);
6461
if (overflowLines) {
6465
nsIFrame* frame = overflowLines->front()->mFirstChild;
6467
if (nsLayoutAtoms::placeholderFrame == frame->GetType()) {
6468
nsIFrame *outOfFlowFrame = NS_STATIC_CAST(nsPlaceholderFrame*, frame)->GetOutOfFlowFrame();
6469
if (outOfFlowFrame &&
6470
!outOfFlowFrame->GetStyleDisplay()->IsAbsolutelyPositioned()) {
6471
// It's not an absolute or fixed positioned frame, so it
6473
// XXX This is a lame-o way of detecting a float, but it's the only way
6476
head = current = outOfFlowFrame;
6478
current->SetNextSibling(outOfFlowFrame);
6479
current = outOfFlowFrame;
6483
frame = frame->GetNextSibling();
6487
current->SetNextSibling(nsnull);
6488
nsFrameList* frameList = new nsFrameList(head);
6490
SetOverflowOutOfFlows(frameList);
6496
// XXX keep the text-run data in the first-in-flow of the block
6500
nsBlockFrame::VerifyLines(PRBool aFinalCheckOK)
6502
if (!gVerifyLines) {
6505
if (mLines.empty()) {
6509
// Add up the counts on each line. Also validate that IsFirstLine is
6512
PRBool seenBlock = PR_FALSE;
6513
line_iterator line, line_end;
6514
for (line = begin_lines(), line_end = end_lines();
6517
if (aFinalCheckOK) {
6518
NS_ABORT_IF_FALSE(line->GetChildCount(), "empty line");
6519
if (line->IsBlock()) {
6520
seenBlock = PR_TRUE;
6522
if (line->IsBlock()) {
6523
NS_ASSERTION(1 == line->GetChildCount(), "bad first line");
6526
count += line->GetChildCount();
6529
// Then count the frames
6530
PRInt32 frameCount = 0;
6531
nsIFrame* frame = mLines.front()->mFirstChild;
6534
frame = frame->GetNextSibling();
6536
NS_ASSERTION(count == frameCount, "bad line list");
6538
// Next: test that each line has right number of frames on it
6539
for (line = begin_lines(), line_end = end_lines();
6542
count = line->GetChildCount();
6543
frame = line->mFirstChild;
6544
while (--count >= 0) {
6545
frame = frame->GetNextSibling();
6548
if ((line != line_end) && (0 != line->GetChildCount())) {
6549
NS_ASSERTION(frame == line->mFirstChild, "bad line list");
6554
// Its possible that a frame can have some frames on an overflow
6555
// list. But its never possible for multiple frames to have overflow
6556
// lists. Check that this fact is actually true.
6558
nsBlockFrame::VerifyOverflowSituation(nsIPresContext* aPresContext)
6560
nsBlockFrame* flow = (nsBlockFrame*) GetFirstInFlow();
6561
while (nsnull != flow) {
6562
nsLineList* overflowLines = GetOverflowLines(aPresContext, PR_FALSE);
6563
if (nsnull != overflowLines) {
6564
NS_ASSERTION(! overflowLines->empty(), "should not be empty if present");
6565
NS_ASSERTION(overflowLines->front()->mFirstChild, "bad overflow list");
6567
flow = (nsBlockFrame*) flow->mNextInFlow;
6572
nsBlockFrame::GetDepth() const
6575
nsIFrame* parent = mParent;
6577
parent = parent->GetParent();