~ubuntu-branches/ubuntu/precise/kompozer/precise

« back to all changes in this revision

Viewing changes to mozilla/layout/html/base/src/nsBlockFrame.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Anthony Yarusso
  • Date: 2007-08-27 01:11:03 UTC
  • Revision ID: james.westby@ubuntu.com-20070827011103-2jgf4s6532gqu2ka
Tags: upstream-0.7.10
ImportĀ upstreamĀ versionĀ 0.7.10

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
5
 *
 
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/
 
10
 *
 
11
 * Software distributed under the License is distributed on an "AS IS" basis,
 
12
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 
13
 * for the specific language governing rights and limitations under the
 
14
 * License.
 
15
 *
 
16
 * The Original Code is Mozilla Communicator client code.
 
17
 *
 
18
 * The Initial Developer of the Original Code is 
 
19
 * Netscape Communications Corporation.
 
20
 * Portions created by the Initial Developer are Copyright (C) 1998
 
21
 * the Initial Developer. All Rights Reserved.
 
22
 *
 
23
 * Contributor(s):
 
24
 *   Steve Clark <buster@netscape.com>
 
25
 *   Robert O'Callahan <roc+moz@cs.cmu.edu>
 
26
 *   L. David Baron <dbaron@dbaron.org>
 
27
 *   IBM Corporation
 
28
 *
 
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.
 
40
 *
 
41
 * ***** END LICENSE BLOCK ***** */
 
42
#include "nsCOMPtr.h"
 
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"
 
58
#include "nsIView.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"
 
65
#include "prprf.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"
 
73
#include "prenv.h"
 
74
#include "plstr.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"
 
81
#endif
 
82
 
 
83
#ifdef IBMBIDI
 
84
#include "nsBidiPresUtils.h"
 
85
#endif // IBMBIDI
 
86
 
 
87
#include "nsIDOMHTMLBodyElement.h"
 
88
#include "nsIDOMHTMLHtmlElement.h"
 
89
 
 
90
static const int MIN_LINES_NEEDING_CURSOR = 20;
 
91
 
 
92
#ifdef DEBUG
 
93
#include "nsPrintfCString.h"
 
94
#include "nsBlockDebugFlags.h"
 
95
 
 
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;
 
106
 
 
107
PRInt32 nsBlockFrame::gNoiseIndent;
 
108
 
 
109
struct BlockDebugFlags {
 
110
  const char* name;
 
111
  PRBool* on;
 
112
};
 
113
 
 
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 },
 
124
};
 
125
#define NUM_DEBUG_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
 
126
 
 
127
static void
 
128
ShowDebugFlags()
 
129
{
 
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);
 
135
  }
 
136
  printf("Note: GECKO_BLOCK_DEBUG_FLAGS is a comma separated list of flag\n");
 
137
  printf("names (no whitespace)\n");
 
138
}
 
139
 
 
140
void
 
141
nsBlockFrame::InitDebugFlags()
 
142
{
 
143
  static PRBool firstTime = PR_TRUE;
 
144
  if (firstTime) {
 
145
    firstTime = PR_FALSE;
 
146
    char* flags = PR_GetEnv("GECKO_BLOCK_DEBUG_FLAGS");
 
147
    if (flags) {
 
148
      PRBool error = PR_FALSE;
 
149
      for (;;) {
 
150
        char* cm = PL_strchr(flags, ',');
 
151
        if (cm) *cm = '\0';
 
152
 
 
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);
 
160
            gNoisy = PR_TRUE;
 
161
            found = PR_TRUE;
 
162
            break;
 
163
          }
 
164
        }
 
165
        if (!found) {
 
166
          error = PR_TRUE;
 
167
        }
 
168
 
 
169
        if (!cm) break;
 
170
        *cm = ',';
 
171
        flags = cm + 1;
 
172
      }
 
173
      if (error) {
 
174
        ShowDebugFlags();
 
175
      }
 
176
    }
 
177
  }
 
178
}
 
179
 
 
180
#endif
 
181
 
 
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
 
185
 
 
186
//----------------------------------------------------------------------
 
187
 
 
188
// Debugging support code
 
189
 
 
190
#ifdef DEBUG
 
191
const char* nsBlockFrame::kReflowCommandType[] = {
 
192
  "ContentChanged",
 
193
  "StyleChanged",
 
194
  "ReflowDirty",
 
195
  "Timeout",
 
196
  "UserDefined",
 
197
};
 
198
#endif
 
199
 
 
200
#ifdef REALLY_NOISY_FIRST_LINE
 
201
static void
 
202
DumpStyleGeneaology(nsIFrame* aFrame, const char* gap)
 
203
{
 
204
  fputs(gap, stdout);
 
205
  nsFrame::ListTag(stdout, aFrame);
 
206
  printf(": ");
 
207
  nsStyleContext* sc = aFrame->GetStyleContext();
 
208
  while (nsnull != sc) {
 
209
    nsStyleContext* psc;
 
210
    printf("%p ", sc);
 
211
    psc = sc->GetParent();
 
212
    sc = psc;
 
213
  }
 
214
  printf("\n");
 
215
}
 
216
#endif
 
217
 
 
218
#ifdef REFLOW_STATUS_COVERAGE
 
219
static void
 
220
RecordReflowStatus(PRBool aChildIsBlock, nsReflowStatus aFrameReflowStatus)
 
221
{
 
222
  static PRUint32 record[2];
 
223
 
 
224
  // 0: child-is-block
 
225
  // 1: child-is-inline
 
226
  PRIntn index = 0;
 
227
  if (!aChildIsBlock) index |= 1;
 
228
 
 
229
  // Compute new status
 
230
  PRUint32 newS = record[index];
 
231
  if (NS_INLINE_IS_BREAK(aFrameReflowStatus)) {
 
232
    if (NS_INLINE_IS_BREAK_BEFORE(aFrameReflowStatus)) {
 
233
      newS |= 1;
 
234
    }
 
235
    else if (NS_FRAME_IS_NOT_COMPLETE(aFrameReflowStatus)) {
 
236
      newS |= 2;
 
237
    }
 
238
    else {
 
239
      newS |= 4;
 
240
    }
 
241
  }
 
242
  else if (NS_FRAME_IS_NOT_COMPLETE(aFrameReflowStatus)) {
 
243
    newS |= 8;
 
244
  }
 
245
  else {
 
246
    newS |= 16;
 
247
  }
 
248
 
 
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]);
 
253
  }
 
254
}
 
255
#endif
 
256
 
 
257
//----------------------------------------------------------------------
 
258
 
 
259
const nsIID kBlockFrameCID = NS_BLOCK_FRAME_CID;
 
260
 
 
261
nsresult
 
262
NS_NewBlockFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame, PRUint32 aFlags)
 
263
{
 
264
  NS_PRECONDITION(aNewFrame, "null OUT ptr");
 
265
  if (nsnull == aNewFrame) {
 
266
    return NS_ERROR_NULL_POINTER;
 
267
  }
 
268
  nsBlockFrame* it = new (aPresShell) nsBlockFrame;
 
269
  if (nsnull == it) {
 
270
    return NS_ERROR_OUT_OF_MEMORY;
 
271
  }
 
272
  it->SetFlags(aFlags);
 
273
  *aNewFrame = it;
 
274
  return NS_OK;
 
275
}
 
276
 
 
277
nsBlockFrame::nsBlockFrame() 
 
278
{
 
279
#ifdef DEBUG
 
280
  InitDebugFlags();
 
281
#endif
 
282
}
 
283
 
 
284
nsBlockFrame::~nsBlockFrame()
 
285
{
 
286
}
 
287
 
 
288
NS_IMETHODIMP
 
289
nsBlockFrame::Destroy(nsIPresContext* aPresContext)
 
290
{
 
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);
 
296
    mBullet = nsnull;
 
297
  }
 
298
 
 
299
  mFloats.DestroyFrames(aPresContext);
 
300
 
 
301
  nsLineBox::DeleteLineList(aPresContext, mLines);
 
302
 
 
303
  // destroy overflow lines now
 
304
  nsLineList* overflowLines = GetOverflowLines(aPresContext, PR_TRUE);
 
305
  if (overflowLines) {
 
306
    nsLineBox::DeleteLineList(aPresContext, *overflowLines);
 
307
  }
 
308
  nsFrameList* overflowOutOfFlows = GetOverflowOutOfFlows(PR_TRUE);
 
309
  if (overflowOutOfFlows) {
 
310
    overflowOutOfFlows->DestroyFrames(aPresContext);
 
311
    delete overflowOutOfFlows;
 
312
  }
 
313
 
 
314
  return nsBlockFrameSuper::Destroy(aPresContext);
 
315
}
 
316
 
 
317
NS_IMETHODIMP
 
318
nsBlockFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr)
 
319
{
 
320
  NS_PRECONDITION(aInstancePtr, "null out param");
 
321
  if (aIID.Equals(kBlockFrameCID)) {
 
322
    *aInstancePtr = NS_STATIC_CAST(void*, NS_STATIC_CAST(nsBlockFrame*, this));
 
323
    return NS_OK;
 
324
  }
 
325
  if (aIID.Equals(NS_GET_IID(nsILineIterator)) ||
 
326
      aIID.Equals(NS_GET_IID(nsILineIteratorNavigator)))
 
327
  {
 
328
    nsLineIterator* it = new nsLineIterator;
 
329
    if (!it) {
 
330
      *aInstancePtr = nsnull;
 
331
      return NS_ERROR_OUT_OF_MEMORY;
 
332
    }
 
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);
 
337
    if (NS_FAILED(rv)) {
 
338
      NS_RELEASE(it);
 
339
      return rv;
 
340
    }
 
341
    *aInstancePtr = NS_STATIC_CAST(void*,
 
342
            NS_STATIC_CAST(nsILineIteratorNavigator*, it));
 
343
    return NS_OK;
 
344
  }
 
345
  return nsBlockFrameSuper::QueryInterface(aIID, aInstancePtr);
 
346
}
 
347
 
 
348
NS_IMETHODIMP
 
349
nsBlockFrame::IsSplittable(nsSplittableType& aIsSplittable) const
 
350
{
 
351
  aIsSplittable = NS_FRAME_SPLITTABLE_NON_RECTANGULAR;
 
352
  return NS_OK;
 
353
}
 
354
 
 
355
#ifdef DEBUG
 
356
NS_METHOD
 
357
nsBlockFrame::List(nsIPresContext* aPresContext, FILE* out, PRInt32 aIndent) const
 
358
{
 
359
  IndentBy(out, aIndent);
 
360
  ListTag(out);
 
361
#ifdef DEBUG_waterson
 
362
  fprintf(out, " [parent=%p]", mParent);
 
363
#endif
 
364
  if (HasView()) {
 
365
    fprintf(out, " [view=%p]", NS_STATIC_CAST(void*, GetView()));
 
366
  }
 
367
  if (nsnull != mNextSibling) {
 
368
    fprintf(out, " next=%p", NS_STATIC_CAST(void*, mNextSibling));
 
369
  }
 
370
 
 
371
  // Output the flow linkage
 
372
  if (nsnull != mPrevInFlow) {
 
373
    fprintf(out, " prev-in-flow=%p", NS_STATIC_CAST(void*, mPrevInFlow));
 
374
  }
 
375
  if (nsnull != mNextInFlow) {
 
376
    fprintf(out, " next-in-flow=%p", NS_STATIC_CAST(void*, mNextInFlow));
 
377
  }
 
378
 
 
379
  // Output the rect and state
 
380
  fprintf(out, " {%d,%d,%d,%d}", mRect.x, mRect.y, mRect.width, mRect.height);
 
381
  if (0 != mState) {
 
382
    fprintf(out, " [state=%08x]", mState);
 
383
  }
 
384
  PRInt32 numInlineLines = 0;
 
385
  PRInt32 numBlockLines = 0;
 
386
  if (! mLines.empty()) {
 
387
    for (const_line_iterator line = begin_lines(), line_end = end_lines();
 
388
         line != line_end;
 
389
         ++line)
 
390
    {
 
391
      if (line->IsBlock())
 
392
        numBlockLines++;
 
393
      else
 
394
        numInlineLines++;
 
395
    }
 
396
  }
 
397
  fprintf(out, " sc=%p(i=%d,b=%d)<\n",
 
398
          NS_STATIC_CAST(void*, mStyleContext), numInlineLines, numBlockLines);
 
399
  aIndent++;
 
400
 
 
401
  // Output the lines
 
402
  if (! mLines.empty()) {
 
403
    for (const_line_iterator line = begin_lines(), line_end = end_lines();
 
404
         line != line_end;
 
405
         ++line)
 
406
    {
 
407
      line->List(aPresContext, out, aIndent);
 
408
    }
 
409
  }
 
410
 
 
411
  nsIAtom* listName = nsnull;
 
412
  PRInt32 listIndex = 0;
 
413
  for (;;) {
 
414
    listName = GetAdditionalChildListName(listIndex++);
 
415
    if (nsnull == listName) {
 
416
      break;
 
417
    }
 
418
    nsIFrame* kid = GetFirstChild(listName);
 
419
    if (kid) {
 
420
      IndentBy(out, aIndent);
 
421
      nsAutoString tmp;
 
422
      if (nsnull != listName) {
 
423
        listName->ToString(tmp);
 
424
        fputs(NS_LossyConvertUCS2toASCII(tmp).get(), out);
 
425
      }
 
426
      fputs("<\n", out);
 
427
      while (kid) {
 
428
        nsIFrameDebug*  frameDebug;
 
429
 
 
430
        if (NS_SUCCEEDED(CallQueryInterface(kid, &frameDebug))) {
 
431
          frameDebug->List(aPresContext, out, aIndent + 1);
 
432
        }
 
433
        kid = kid->GetNextSibling();
 
434
      }
 
435
      IndentBy(out, aIndent);
 
436
      fputs(">\n", out);
 
437
    }
 
438
  }
 
439
 
 
440
  aIndent--;
 
441
  IndentBy(out, aIndent);
 
442
  fputs(">\n", out);
 
443
 
 
444
  return NS_OK;
 
445
}
 
446
 
 
447
NS_IMETHODIMP_(nsFrameState)
 
448
nsBlockFrame::GetDebugStateBits() const
 
449
{
 
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;
 
453
}
 
454
 
 
455
NS_IMETHODIMP
 
456
nsBlockFrame::GetFrameName(nsAString& aResult) const
 
457
{
 
458
  return MakeFrameName(NS_LITERAL_STRING("Block"), aResult);
 
459
}
 
460
#endif
 
461
 
 
462
nsIAtom*
 
463
nsBlockFrame::GetType() const
 
464
{
 
465
  return nsLayoutAtoms::blockFrame;
 
466
}
 
467
 
 
468
/////////////////////////////////////////////////////////////////////////////
 
469
// Child frame enumeration
 
470
 
 
471
nsIFrame*
 
472
nsBlockFrame::GetFirstChild(nsIAtom* aListName) const
 
473
{
 
474
  if (mAbsoluteContainer.GetChildListName() == aListName) {
 
475
    nsIFrame* result = nsnull;
 
476
    mAbsoluteContainer.FirstChild(this, aListName, &result);
 
477
    return result;
 
478
  }
 
479
  else if (nsnull == aListName) {
 
480
    return (mLines.empty()) ? nsnull : mLines.front()->mFirstChild;
 
481
  }
 
482
  else if (aListName == nsLayoutAtoms::overflowList) {
 
483
    nsLineList* overflowLines = GetOverflowLines(GetPresContext(), PR_FALSE);
 
484
    return overflowLines ? overflowLines->front()->mFirstChild : nsnull;
 
485
  }
 
486
  else if (aListName == nsLayoutAtoms::overflowOutOfFlowList) {
 
487
    nsFrameList* oof = GetOverflowOutOfFlows(PR_FALSE);
 
488
    return oof ? oof->FirstChild() : nsnull;
 
489
  }
 
490
  else if (aListName == nsLayoutAtoms::floatList) {
 
491
    return mFloats.FirstChild();
 
492
  }
 
493
  else if (aListName == nsLayoutAtoms::bulletList) {
 
494
    if (HaveOutsideBullet()) {
 
495
      return mBullet;
 
496
    }
 
497
  }
 
498
  return nsnull;
 
499
}
 
500
 
 
501
nsIAtom*
 
502
nsBlockFrame::GetAdditionalChildListName(PRInt32 aIndex) const
 
503
{
 
504
  switch (aIndex) {
 
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();
 
515
  default:
 
516
    return nsnull;
 
517
  }
 
518
}
 
519
 
 
520
/* virtual */ PRBool
 
521
nsBlockFrame::IsContainingBlock() const
 
522
{
 
523
  return PR_TRUE;
 
524
}
 
525
 
 
526
//////////////////////////////////////////////////////////////////////
 
527
// Frame structure methods
 
528
 
 
529
//////////////////////////////////////////////////////////////////////
 
530
// Reflow methods
 
531
 
 
532
static void
 
533
CalculateContainingBlock(const nsHTMLReflowState& aReflowState,
 
534
                         nscoord                  aFrameWidth,
 
535
                         nscoord                  aFrameHeight,
 
536
                         nscoord&                 aContainingBlockWidth,
 
537
                         nscoord&                 aContainingBlockHeight)
 
538
{
 
539
  aContainingBlockWidth = -1;  // have reflow state calculate
 
540
  aContainingBlockHeight = -1; // have reflow state calculate
 
541
 
 
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;
 
551
 
 
552
    // Containing block is relative to the padding edge
 
553
    nsMargin  border;
 
554
    if (!aReflowState.mStyleBorder->GetBorder(border)) {
 
555
      NS_NOTYETIMPLEMENTED("percentage border");
 
556
    }
 
557
    aContainingBlockWidth -= border.left + border.right;
 
558
    aContainingBlockHeight -= border.top + border.bottom;
 
559
  }
 
560
}
 
561
 
 
562
NS_IMETHODIMP
 
563
nsBlockFrame::Reflow(nsIPresContext*          aPresContext,
 
564
                     nsHTMLReflowMetrics&     aMetrics,
 
565
                     const nsHTMLReflowState& aReflowState,
 
566
                     nsReflowStatus&          aStatus)
 
567
{
 
568
  DO_GLOBAL_REFLOW_COUNT("nsBlockFrame", aReflowState.reason);
 
569
  DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
 
570
#ifdef DEBUG
 
571
  if (gNoisyReflow) {
 
572
    nsCAutoString reflow;
 
573
    reflow.Append(nsHTMLReflowState::ReasonToString(aReflowState.reason));
 
574
 
 
575
    if (aReflowState.reason == eReflowReason_Incremental) {
 
576
      nsHTMLReflowCommand *command = aReflowState.path->mReflowCommand;
 
577
 
 
578
      if (command) {
 
579
        // We're the target.
 
580
        reflow += " (";
 
581
 
 
582
        nsReflowType type;
 
583
        command->GetType(type);
 
584
        reflow += kReflowCommandType[type];
 
585
 
 
586
        reflow += ")";
 
587
      }
 
588
    }
 
589
 
 
590
    IndentBy(stdout, gNoiseIndent);
 
591
    ListTag(stdout);
 
592
    printf(": begin %s reflow availSize=%d,%d computedSize=%d,%d\n",
 
593
           reflow.get(),
 
594
           aReflowState.availableWidth, aReflowState.availableHeight,
 
595
           aReflowState.mComputedWidth, aReflowState.mComputedHeight);
 
596
  }
 
597
  if (gNoisy) {
 
598
    gNoiseIndent++;
 
599
  }
 
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) {
 
603
    start = PR_Now();
 
604
    ctc = nsLineBox::GetCtorCount();
 
605
  }
 
606
#endif
 
607
 
 
608
  nsRect oldRect(mRect);
 
609
 
 
610
  // Should we create a space manager?
 
611
  nsAutoSpaceManager autoSpaceManager(NS_CONST_CAST(nsHTMLReflowState &, aReflowState));
 
612
 
 
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);
 
619
 
 
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;
 
626
    PRBool  handled;
 
627
 
 
628
    CalculateContainingBlock(aReflowState, mRect.width, mRect.height,
 
629
                             containingBlockWidth, containingBlockHeight);
 
630
    
 
631
    mAbsoluteContainer.IncrementalReflow(this, aPresContext, aReflowState,
 
632
                                         containingBlockWidth,
 
633
                                         containingBlockHeight,
 
634
                                         handled);
 
635
 
 
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;
 
644
 
 
645
      // Whether or not we're complete hasn't changed
 
646
      aStatus = (nsnull != mNextInFlow) ? NS_FRAME_NOT_COMPLETE : NS_FRAME_COMPLETE;
 
647
      
 
648
      // Factor the absolutely positioned child bounds into the overflow area
 
649
      ComputeCombinedArea(aReflowState, aMetrics);
 
650
      nsRect childBounds;
 
651
      mAbsoluteContainer.CalculateChildBounds(aPresContext, childBounds);
 
652
      aMetrics.mOverflowArea.UnionRect(aMetrics.mOverflowArea, childBounds);
 
653
 
 
654
      StoreOverflow(aPresContext, aMetrics);
 
655
 
 
656
      return NS_OK;
 
657
    }
 
658
  }
 
659
 
 
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.
 
663
  ClearLineCursor();
 
664
 
 
665
  if (IsFrameTreeTooDeep(aReflowState, aMetrics)) {
 
666
#ifdef DEBUG_kipp
 
667
    {
 
668
      extern char* nsPresShell_ReflowStackPointerTop;
 
669
      char marker;
 
670
      char* newsp = (char*) &marker;
 
671
      printf("XXX: frame tree is too deep; approx stack size = %d\n",
 
672
             nsPresShell_ReflowStackPointerTop - newsp);
 
673
    }
 
674
#endif
 
675
    aStatus = NS_FRAME_COMPLETE;
 
676
    return NS_OK;
 
677
  }
 
678
 
 
679
  nsBlockReflowState state(aReflowState, aPresContext, this, aMetrics,
 
680
                           NS_BLOCK_MARGIN_ROOT & mState);
 
681
 
 
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) {
 
689
#ifdef IBMBIDI
 
690
    if (! mLines.empty()) {
 
691
      PRBool bidiEnabled;
 
692
      aPresContext->GetBidiEnabled(&bidiEnabled);
 
693
      if (bidiEnabled) {
 
694
        nsBidiPresUtils* bidiUtils;
 
695
        aPresContext->GetBidiUtils(&bidiUtils);
 
696
        if (bidiUtils) {
 
697
          PRBool forceReflow;
 
698
          nsresult rc = bidiUtils->Resolve(aPresContext, this,
 
699
                                           mLines.front()->mFirstChild,
 
700
                                           forceReflow,
 
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();
 
706
                 line != line_end;
 
707
                 ++line)
 
708
            {
 
709
              line->MarkDirty();
 
710
            }
 
711
          }
 
712
        }
 
713
      }
 
714
    }
 
715
#endif // IBMBIDI
 
716
    RenumberLists(aPresContext);
 
717
  }
 
718
 
 
719
  nsresult rv = NS_OK;
 
720
 
 
721
  switch (aReflowState.reason) {
 
722
  case eReflowReason_Initial:
 
723
#ifdef NOISY_REFLOW_REASON
 
724
    ListTag(stdout);
 
725
    printf(": reflow=initial\n");
 
726
#endif
 
727
    DrainOverflowLines(aPresContext);
 
728
    rv = PrepareInitialReflow(state);
 
729
    mState &= ~NS_FRAME_FIRST_REFLOW;
 
730
    break;  
 
731
 
 
732
  case eReflowReason_Dirty:
 
733
    // Do nothing; the dirty lines will already have been marked.
 
734
    break;
 
735
 
 
736
  case eReflowReason_Incremental: {
 
737
#ifdef NOISY_REFLOW_REASON
 
738
    ListTag(stdout);
 
739
    printf(": reflow=incremental ");
 
740
#endif
 
741
    nsReflowPath *path = aReflowState.path;
 
742
    nsHTMLReflowCommand *command = path->mReflowCommand;
 
743
    if (command) {
 
744
      nsReflowType type;
 
745
      command->GetType(type);
 
746
#ifdef NOISY_REFLOW_REASON
 
747
      printf("type=%s ", kReflowCommandType[type]);
 
748
#endif
 
749
      switch (type) {
 
750
      case eReflowType_StyleChanged:
 
751
        rv = PrepareStyleChangedReflow(state);
 
752
        break;
 
753
      case eReflowType_ReflowDirty:
 
754
        // Do nothing; the dirty lines will already have been marked.
 
755
        break;
 
756
      case eReflowType_ContentChanged:
 
757
        // Perform a full reflow.
 
758
        rv = PrepareResizeReflow(state);
 
759
        break;
 
760
      default:
 
761
        // We shouldn't get here. But, if we do, perform a full reflow.
 
762
        NS_ERROR("unexpected reflow type");
 
763
        rv = PrepareResizeReflow(state);
 
764
        break;
 
765
      }
 
766
    }
 
767
 
 
768
    if (path->FirstChild() != path->EndChildren()) {
 
769
      // We're along the reflow path, but not necessarily the target
 
770
      // of the reflow.
 
771
#ifdef NOISY_REFLOW_REASON
 
772
      ListTag(stdout);
 
773
      printf(" next={ ");
 
774
 
 
775
      for (nsReflowPath::iterator iter = path->FirstChild();
 
776
           iter != path->EndChildren();
 
777
           ++iter) {
 
778
        nsFrame::ListTag(stdout, *iter);
 
779
        printf(" ");
 
780
      }
 
781
 
 
782
      printf("}");
 
783
#endif
 
784
 
 
785
      rv = PrepareChildIncrementalReflow(state);
 
786
    }
 
787
 
 
788
#ifdef NOISY_REFLOW_REASON
 
789
    printf("\n");
 
790
#endif
 
791
 
 
792
    break;
 
793
  }
 
794
 
 
795
  case eReflowReason_StyleChange:
 
796
    DrainOverflowLines(aPresContext);
 
797
    rv = PrepareStyleChangedReflow(state);
 
798
    break;
 
799
 
 
800
  case eReflowReason_Resize:
 
801
  default:
 
802
#ifdef NOISY_REFLOW_REASON
 
803
    ListTag(stdout);
 
804
    printf(": reflow=resize (%d)\n", aReflowState.reason);
 
805
#endif
 
806
    DrainOverflowLines(aPresContext);
 
807
    rv = PrepareResizeReflow(state);
 
808
    break;
 
809
  }
 
810
 
 
811
  NS_ASSERTION(NS_SUCCEEDED(rv), "setting up reflow failed");
 
812
  if (NS_FAILED(rv)) return rv;
 
813
 
 
814
  // Now reflow...
 
815
  rv = ReflowDirtyLines(state);
 
816
  NS_ASSERTION(NS_SUCCEEDED(rv), "reflow dirty lines failed");
 
817
  if (NS_FAILED(rv)) return rv;
 
818
 
 
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; 
 
831
           ancestorRS; 
 
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);
 
840
            if (ancestorPlace) {
 
841
              ancestorPlace->AppendFrames(ancestor, overflowPlace->FirstChild());
 
842
            }
 
843
            else {
 
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.
 
849
 
 
850
              ancestorPlace = new nsFrameList();
 
851
              if (ancestorPlace) {
 
852
                ancestorPlace->AppendFrames(ancestor, overflowPlace->FirstChild());
 
853
                ((nsBlockFrame*)ancestor)->SetOverflowPlaceholders(aPresContext, ancestorPlace);
 
854
              }
 
855
              else 
 
856
                return NS_ERROR_OUT_OF_MEMORY;
 
857
            }
 
858
            gaveToAncestor = PR_TRUE;
 
859
            break;
 
860
          }
 
861
        }
 
862
      }
 
863
    }
 
864
    if (!gaveToAncestor) {
 
865
      PRInt32 numOverflowPlace = overflowPlace->GetLength();
 
866
      nsLineList* overflowLines = GetOverflowLines(aPresContext, PR_FALSE);
 
867
      if (overflowLines) {
 
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);
 
873
        }
 
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);
 
883
        }
 
884
      }
 
885
      else {
 
886
        // Create a line, put the floats in it, and then push.
 
887
        nsLineBox* newLine = state.NewLineBox(overflowPlace->FirstChild(), numOverflowPlace, PR_FALSE);
 
888
        if (!newLine) 
 
889
          return NS_ERROR_OUT_OF_MEMORY;
 
890
        mLines.push_back(newLine);
 
891
        nsLineList::iterator nextToLastLine = ----end_lines();
 
892
        PushLines(state, nextToLastLine);
 
893
      }
 
894
      state.mReflowStatus = NS_FRAME_NOT_COMPLETE;
 
895
    }
 
896
    delete overflowPlace;
 
897
  }
 
898
 
 
899
  if (NS_FRAME_IS_NOT_COMPLETE(state.mReflowStatus)) {
 
900
    if (NS_STYLE_OVERFLOW_HIDDEN == aReflowState.mStyleDisplay->mOverflow) {
 
901
      state.mReflowStatus = NS_FRAME_COMPLETE;
 
902
    }
 
903
    else {
 
904
#ifdef DEBUG_kipp
 
905
      ListTag(stdout); printf(": block is not complete\n");
 
906
#endif
 
907
    }
 
908
  }
 
909
 
 
910
  // XXX_perf get rid of this!  This is one of the things that makes
 
911
  // incremental reflow O(N^2).
 
912
  BuildFloatList();
 
913
  
 
914
  // Compute our final size
 
915
  ComputeFinalSize(aReflowState, state, aMetrics);
 
916
  StoreOverflow(aPresContext, aMetrics);
 
917
 
 
918
  // see if verifyReflow is enabled, and if so store off the space manager pointer
 
919
#ifdef DEBUG
 
920
  PRInt32 verifyReflowFlags = nsIPresShell::GetVerifyReflowFlags();
 
921
  if (VERIFY_REFLOW_INCLUDE_SPACE_MANAGER & verifyReflowFlags)
 
922
  {
 
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();
 
925
    if (shell) {
 
926
      nsHTMLReflowState&  reflowState = (nsHTMLReflowState&)aReflowState;
 
927
      rv = shell->FrameManager()->SetFrameProperty(
 
928
                             this, nsLayoutAtoms::spaceManagerProperty,
 
929
                             reflowState.mSpaceManager,
 
930
                             nsnull /* should be nsSpaceManagerDestroyer*/);
 
931
 
 
932
      autoSpaceManager.DebugOrphanSpaceManager();
 
933
    }
 
934
  }
 
935
#endif
 
936
 
 
937
  // Determine if we need to repaint our border, background or outline
 
938
  CheckInvalidateSizeChange(aPresContext, aMetrics, aReflowState);
 
939
 
 
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
 
949
  // condition 1.
 
950
  if (NS_SUCCEEDED(rv) && mAbsoluteContainer.HasAbsoluteFrames()) {
 
951
    nsRect childBounds;
 
952
    if (eReflowReason_Incremental != aReflowState.reason ||
 
953
        aReflowState.path->mReflowCommand ||
 
954
        mRect != oldRect) {
 
955
      nscoord containingBlockWidth;
 
956
      nscoord containingBlockHeight;
 
957
 
 
958
      CalculateContainingBlock(aReflowState, aMetrics.width, aMetrics.height,
 
959
                               containingBlockWidth, containingBlockHeight);
 
960
 
 
961
      rv = mAbsoluteContainer.Reflow(this, aPresContext, aReflowState,
 
962
                                     containingBlockWidth,
 
963
                                     containingBlockHeight,
 
964
                                     &childBounds);
 
965
    } else {
 
966
      mAbsoluteContainer.CalculateChildBounds(aPresContext, childBounds);
 
967
    }
 
968
 
 
969
    // Factor the absolutely positioned child bounds into the overflow area
 
970
    aMetrics.mOverflowArea.UnionRect(aMetrics.mOverflowArea, childBounds);
 
971
 
 
972
    StoreOverflow(aPresContext, aMetrics);
 
973
  }
 
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
 
976
  // space manager.
 
977
  if (NS_BLOCK_SPACE_MGR & mState)
 
978
    state.mSpaceManager = nsnull;
 
979
 
 
980
  aStatus = state.mReflowStatus;
 
981
 
 
982
#ifdef DEBUG
 
983
  if (gNoisy) {
 
984
    gNoiseIndent--;
 
985
  }
 
986
  if (gNoisyReflow) {
 
987
    IndentBy(stdout, gNoiseIndent);
 
988
    ListTag(stdout);
 
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);
 
999
    }
 
1000
    if (aMetrics.mComputeMEW) {
 
1001
      printf(" maxElementWidth=%d", aMetrics.mMaxElementWidth);
 
1002
    }
 
1003
    printf("\n");
 
1004
  }
 
1005
 
 
1006
  if (gLameReflowMetrics) {
 
1007
    PRTime end = PR_Now();
 
1008
 
 
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);
 
1016
 
 
1017
    ListTag(stdout);
 
1018
    char buf[400];
 
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);
 
1023
  }
 
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);
 
1030
    }
 
1031
  }
 
1032
#endif
 
1033
 
 
1034
  NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics);
 
1035
  return rv;
 
1036
}
 
1037
 
 
1038
static PRBool
 
1039
HaveAutoWidth(const nsHTMLReflowState& aReflowState)
 
1040
{
 
1041
  return NS_UNCONSTRAINEDSIZE == aReflowState.mComputedWidth ||
 
1042
         eStyleUnit_Auto == aReflowState.mStylePosition->mWidth.GetUnit();
 
1043
}
 
1044
 
 
1045
 
 
1046
// XXXldb why do we check vertical and horizontal at the same time?  Don't
 
1047
// we usually care about one or the other?
 
1048
static PRBool
 
1049
IsPercentageAwareChild(const nsIFrame* aFrame)
 
1050
{
 
1051
  NS_ASSERTION(aFrame, "null frame is not allowed");
 
1052
 
 
1053
  const nsStyleMargin* margin = aFrame->GetStyleMargin();
 
1054
  if (nsLineLayout::IsPercentageUnitSides(&margin->mMargin)) {
 
1055
    return PR_TRUE;
 
1056
  }
 
1057
 
 
1058
  const nsStylePadding* padding = aFrame->GetStylePadding();
 
1059
  if (nsLineLayout::IsPercentageUnitSides(&padding->mPadding)) {
 
1060
    return PR_TRUE;
 
1061
  }
 
1062
 
 
1063
  const nsStyleBorder* border = aFrame->GetStyleBorder();
 
1064
  if (nsLineLayout::IsPercentageUnitSides(&border->mBorder)) {
 
1065
    return PR_TRUE;
 
1066
  }
 
1067
 
 
1068
  const nsStylePosition* pos = aFrame->GetStylePosition();
 
1069
 
 
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!!!
 
1077
    return PR_TRUE;
 
1078
  }
 
1079
 
 
1080
  return PR_FALSE;
 
1081
}
 
1082
 
 
1083
void
 
1084
nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState,
 
1085
                               nsBlockReflowState&      aState,
 
1086
                               nsHTMLReflowMetrics&     aMetrics)
 
1087
{
 
1088
  const nsMargin& borderPadding = aState.BorderPadding();
 
1089
#ifdef NOISY_FINAL_SIZE
 
1090
  ListTag(stdout);
 
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);
 
1095
#endif
 
1096
 
 
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?
 
1101
 
 
1102
  // Compute final width
 
1103
  nscoord maxElementWidth = 0;
 
1104
#ifdef NOISY_KIDXMOST
 
1105
  printf("%p aState.mKidXMost=%d\n", this, aState.mKidXMost); 
 
1106
#endif
 
1107
  if (!HaveAutoWidth(aReflowState)) {
 
1108
    // Use style defined width
 
1109
    aMetrics.width = borderPadding.left + aReflowState.mComputedWidth +
 
1110
      borderPadding.right;
 
1111
 
 
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;
 
1121
      } else {
 
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;
 
1125
      }
 
1126
    }
 
1127
  }
 
1128
  else {
 
1129
    nscoord computedWidth;
 
1130
 
 
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;
 
1145
    } else {
 
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.
 
1150
        nscoord xmost;
 
1151
        if (aReflowState.mSpaceManager->XMost(xmost) &&
 
1152
            computedWidth < xmost)
 
1153
          computedWidth = xmost;
 
1154
      }
 
1155
      computedWidth += borderPadding.right;
 
1156
    }
 
1157
 
 
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;
 
1167
      }
 
1168
    }
 
1169
 
 
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;
 
1176
      }
 
1177
    }
 
1178
    if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMinWidth) {
 
1179
      nscoord computedMinWidth = aReflowState.mComputedMinWidth +
 
1180
        borderPadding.left + borderPadding.right;
 
1181
      if (computedWidth < computedMinWidth) {
 
1182
        computedWidth = computedMinWidth;
 
1183
      }
 
1184
    }
 
1185
    aMetrics.width = computedWidth;
 
1186
 
 
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);
 
1200
 
 
1201
        reflowState.mComputedWidth = aMetrics.width - borderPadding.left -
 
1202
                                     borderPadding.right;
 
1203
        reflowState.reason = eReflowReason_Resize;
 
1204
        reflowState.mSpaceManager->ClearRegions();
 
1205
 
 
1206
#ifdef DEBUG
 
1207
        nscoord oldDesiredWidth = aMetrics.width;
 
1208
#endif
 
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");
 
1214
      }
 
1215
    }
 
1216
  }
 
1217
 
 
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;
 
1225
      if (mPrevInFlow) {
 
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;
 
1235
        }
 
1236
        aMetrics.height = PR_MAX(0, aMetrics.height);
 
1237
      }
 
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;
 
1242
      }
 
1243
    }
 
1244
    else {
 
1245
      // Use the current height; continuations will take up the rest.
 
1246
      aMetrics.height = aState.mY;
 
1247
    }
 
1248
 
 
1249
    // Don't carry out a bottom margin when our height is fixed.
 
1250
    aState.mPrevBottomMargin.Zero();
 
1251
  }
 
1252
  else {
 
1253
    nscoord autoHeight = aState.mY;
 
1254
 
 
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();
 
1261
    }
 
1262
 
 
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.
 
1266
      nscoord ymost;
 
1267
      if (aReflowState.mSpaceManager->YMost(ymost) &&
 
1268
          autoHeight < ymost)
 
1269
        autoHeight = ymost;
 
1270
    }
 
1271
    autoHeight += borderPadding.bottom;
 
1272
 
 
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;
 
1279
      }
 
1280
    }
 
1281
    if (NS_UNCONSTRAINEDSIZE != aReflowState.mComputedMinHeight) {
 
1282
      nscoord computedMinHeight = aReflowState.mComputedMinHeight +
 
1283
        borderPadding.top + borderPadding.bottom;
 
1284
      if (autoHeight < computedMinHeight) {
 
1285
        autoHeight = computedMinHeight;
 
1286
      }
 
1287
    }
 
1288
    aMetrics.height = autoHeight;
 
1289
 
 
1290
  }
 
1291
 
 
1292
  aMetrics.ascent = mAscent;
 
1293
  aMetrics.descent = aMetrics.height - aMetrics.ascent;
 
1294
 
 
1295
  if (aState.GetFlag(BRS_COMPUTEMAXELEMENTWIDTH)) {
 
1296
    // Store away the final value
 
1297
    aMetrics.mMaxElementWidth = maxElementWidth;
 
1298
#ifdef DEBUG
 
1299
    if (gNoisyMaxElementWidth) {
 
1300
      IndentBy(stdout, gNoiseIndent);
 
1301
      printf ("nsBlockFrame::CFS: %p returning MEW %d\n", 
 
1302
              NS_STATIC_CAST(void*, this), aMetrics.mMaxElementWidth);
 
1303
    }
 
1304
#endif
 
1305
  }
 
1306
 
 
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;
 
1314
  else
 
1315
    aMetrics.mCarriedOutBottomMargin.Zero();
 
1316
 
 
1317
#ifdef DEBUG_blocks
 
1318
  if (CRAZY_WIDTH(aMetrics.width) || CRAZY_HEIGHT(aMetrics.height)) {
 
1319
    ListTag(stdout);
 
1320
    printf(": WARNING: desired:%d,%d\n", aMetrics.width, aMetrics.height);
 
1321
  }
 
1322
  if (aState.GetFlag(BRS_COMPUTEMAXELEMENTWIDTH) &&
 
1323
      (maxElementWidth > aMetrics.width))) {
 
1324
    ListTag(stdout);
 
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);
 
1329
  }
 
1330
#endif
 
1331
#ifdef DEBUG
 
1332
  if (gNoisyMaxElementWidth) {
 
1333
    if (aState.GetFlag(BRS_COMPUTEMAXELEMENTWIDTH)) {
 
1334
      IndentBy(stdout, gNoiseIndent);
 
1335
      if (NS_UNCONSTRAINEDSIZE == aState.mReflowState.availableWidth) {
 
1336
        printf("PASS1 ");
 
1337
      }
 
1338
      ListTag(stdout);
 
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);
 
1343
    }
 
1344
  }
 
1345
#endif
 
1346
 
 
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;
 
1352
    } else {
 
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;
 
1356
    }
 
1357
#ifdef NOISY_MAXIMUM_WIDTH
 
1358
    printf("nsBlockFrame::ComputeFinalSize block %p setting aMetrics.mMaximumWidth to %d\n", this, aMetrics.mMaximumWidth);
 
1359
#endif
 
1360
  }
 
1361
 
 
1362
  ComputeCombinedArea(aReflowState, aMetrics);
 
1363
}
 
1364
 
 
1365
void
 
1366
nsBlockFrame::ComputeCombinedArea(const nsHTMLReflowState& aReflowState,
 
1367
                                  nsHTMLReflowMetrics& aMetrics)
 
1368
{
 
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();
 
1375
         line != line_end;
 
1376
         ++line)
 
1377
    {
 
1378
      // Compute min and max x/y values for the reflowed frame's
 
1379
      // combined areas
 
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;
 
1385
      if (x < xa) {
 
1386
        xa = x;
 
1387
      }
 
1388
      if (xmost > xb) {
 
1389
        xb = xmost;
 
1390
      }
 
1391
      if (y < ya) {
 
1392
        ya = y;
 
1393
      }
 
1394
      if (ymost > yb) {
 
1395
        yb = ymost;
 
1396
      }
 
1397
    }
 
1398
 
 
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?
 
1404
    if (mBullet) {
 
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;
 
1412
    }
 
1413
  }
 
1414
#ifdef NOISY_COMBINED_AREA
 
1415
  ListTag(stdout);
 
1416
  printf(": ca=%d,%d,%d,%d\n", xa, ya, xb-xa, yb-ya);
 
1417
#endif
 
1418
 
 
1419
  aMetrics.mOverflowArea.x = xa;
 
1420
  aMetrics.mOverflowArea.y = ya;
 
1421
  aMetrics.mOverflowArea.width = xb - xa;
 
1422
  aMetrics.mOverflowArea.height = yb - ya;
 
1423
 
 
1424
}
 
1425
 
 
1426
nsresult
 
1427
nsBlockFrame::PrepareInitialReflow(nsBlockReflowState& aState)
 
1428
{
 
1429
  PrepareResizeReflow(aState);
 
1430
  return NS_OK;
 
1431
}
 
1432
 
 
1433
nsresult
 
1434
nsBlockFrame::PrepareChildIncrementalReflow(nsBlockReflowState& aState)
 
1435
{
 
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;
 
1440
 
 
1441
  nsReflowPath::iterator iter = path->FirstChild();
 
1442
  nsReflowPath::iterator end = path->EndChildren();
 
1443
 
 
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.
 
1451
      //
 
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");
 
1459
#endif
 
1460
      // This can't happen, but just in case it does...
 
1461
      PrepareResizeReflow(aState);
 
1462
      continue;
 
1463
    }
 
1464
 
 
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);
 
1477
        if (prevInFlow)
 
1478
          RetargetInlineIncrementalReflow(iter, line, prevInFlow);
 
1479
      }
 
1480
    }
 
1481
 
 
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
 
1485
    //    something up
 
1486
    //  * It's an incremental reflow to something within an inline, which
 
1487
    //    we know must be very limited.
 
1488
    line->MarkDirty();
 
1489
  }
 
1490
  return NS_OK;
 
1491
}
 
1492
 
 
1493
void
 
1494
nsBlockFrame::RetargetInlineIncrementalReflow(nsReflowPath::iterator &aTarget,
 
1495
                                              line_iterator &aLine,
 
1496
                                              nsIFrame *aPrevInFlow)
 
1497
{
 
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");
 
1503
 
 
1504
  // Now fix the iterator, keeping track of how many lines we walk
 
1505
  // back through.
 
1506
  PRInt32 lineCount = 0;
 
1507
  do {
 
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");
 
1512
 
 
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'?
 
1516
    --aLine;
 
1517
    if (aLine->GetBreakType() == NS_STYLE_CLEAR_LINE)
 
1518
      break;
 
1519
 
 
1520
    *aTarget = aPrevInFlow;
 
1521
    aPrevInFlow->GetPrevInFlow(&aPrevInFlow);
 
1522
 
 
1523
#ifdef DEBUG
 
1524
    // Paranoia. Ensure that the prev-in-flow is really in the
 
1525
    // previous line.
 
1526
    line_iterator check = FindLineFor(*aTarget);
 
1527
    NS_ASSERTION(check == aLine, "prev-in-flow not in previous linebox");
 
1528
#endif
 
1529
 
 
1530
    ++lineCount;
 
1531
  } while (aPrevInFlow);
 
1532
 
 
1533
  if (lineCount > 0) {
 
1534
    // Fix any frames deeper in the reflow path.
 
1535
#if 0
 
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.
 
1544
 
 
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();
 
1548
 
 
1549
    for (PRInt32 i = path->Count() - 1; i >= 0; --i) {
 
1550
      nsIFrame *frame = NS_STATIC_CAST(nsIFrame *, path->ElementAt(i));
 
1551
 
 
1552
      // Stop if we encounter a non-inline frame in the reflow path.
 
1553
      const nsStyleDisplay* display = frame->GetStyleDisplay();
 
1554
 
 
1555
      if (NS_STYLE_DISPLAY_INLINE != display->mDisplay)
 
1556
        break;
 
1557
 
 
1558
      // Walk back to the primary frame.
 
1559
      PRInt32 count = lineCount;
 
1560
      nsIFrame *prevInFlow;
 
1561
      do {
 
1562
        frame->GetPrevInFlow(&prevInFlow);
 
1563
      } while (--count >= 0 && prevInFlow && (frame = prevInFlow));
 
1564
 
 
1565
      path->ReplaceElementAt(frame, i);
 
1566
    }
 
1567
#else
 
1568
    NS_WARNING("blowing an incremental reflow targeted at a nested inline");
 
1569
#endif
 
1570
  }
 
1571
}
 
1572
 
 
1573
nsresult
 
1574
nsBlockFrame::MarkLineDirty(line_iterator aLine)
 
1575
{
 
1576
  // Mark aLine dirty
 
1577
  aLine->MarkDirty();
 
1578
#ifdef DEBUG
 
1579
  if (gNoisyReflow) {
 
1580
    IndentBy(stdout, gNoiseIndent);
 
1581
    ListTag(stdout);
 
1582
    printf(": mark line %p dirty\n", NS_STATIC_CAST(void*, aLine.get()));
 
1583
  }
 
1584
#endif
 
1585
 
 
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();
 
1593
#ifdef DEBUG
 
1594
    if (gNoisyReflow) {
 
1595
      IndentBy(stdout, gNoiseIndent);
 
1596
      ListTag(stdout);
 
1597
      printf(": mark prev-line %p dirty\n",
 
1598
             NS_STATIC_CAST(void*, aLine.prev().get()));
 
1599
    }
 
1600
#endif
 
1601
  }
 
1602
 
 
1603
  return NS_OK;
 
1604
}
 
1605
 
 
1606
nsresult
 
1607
nsBlockFrame::UpdateBulletPosition(nsBlockReflowState& aState)
 
1608
{
 
1609
  if (nsnull == mBullet) {
 
1610
    // Don't bother if there is no bullet
 
1611
    return NS_OK;
 
1612
  }
 
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
 
1620
        // first line
 
1621
        nsIFrame* child = nsnull;
 
1622
        nsLineBox* firstLine = mLines.front();
 
1623
        
 
1624
        // bullet should not have any siblings if it was an outside bullet
 
1625
        NS_ASSERTION(!mBullet->GetNextSibling(), "outside bullet should not have siblings");
 
1626
 
 
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();
 
1635
      } else {
 
1636
        // no prior lines, just create a new line for the bullet
 
1637
        nsLineBox* line = aState.NewLineBox(mBullet, 1, PR_FALSE);
 
1638
        if (!line) {
 
1639
          return NS_ERROR_OUT_OF_MEMORY;
 
1640
        }
 
1641
        mLines.push_back(line);
 
1642
      }
 
1643
    }
 
1644
    mState &= ~NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET;
 
1645
  }
 
1646
  else {
 
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
 
1650
      // list.
 
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);
 
1657
        if (0 == count) {
 
1658
          nsLineBox* oldFront = mLines.front();
 
1659
          mLines.pop_front();
 
1660
          aState.FreeLineBox(oldFront);
 
1661
          if (! mLines.empty()) {
 
1662
            mLines.front()->MarkDirty();
 
1663
          }
 
1664
        }
 
1665
        else {
 
1666
          mLines.front()->mFirstChild = next;
 
1667
          mLines.front()->MarkDirty();
 
1668
        }
 
1669
      }
 
1670
    }
 
1671
    mState |= NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET;
 
1672
  }
 
1673
#ifdef DEBUG
 
1674
  VerifyLines(PR_TRUE);
 
1675
#endif
 
1676
  return NS_OK;
 
1677
}
 
1678
 
 
1679
nsresult
 
1680
nsBlockFrame::PrepareStyleChangedReflow(nsBlockReflowState& aState)
 
1681
{
 
1682
  nsresult rv = UpdateBulletPosition(aState);
 
1683
 
 
1684
  // Mark everything dirty
 
1685
  for (line_iterator line = begin_lines(), line_end = end_lines();
 
1686
       line != line_end;
 
1687
       ++line)
 
1688
  {
 
1689
    line->MarkDirty();
 
1690
  }
 
1691
  return rv;
 
1692
}
 
1693
 
 
1694
nsresult
 
1695
nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState)
 
1696
{
 
1697
  // See if we can try and avoid marking all the lines as dirty
 
1698
  PRBool  tryAndSkipLines = PR_FALSE;
 
1699
 
 
1700
  // we need to calculate if any part of then block itself 
 
1701
  // is impacted by a float (bug 19579)
 
1702
  aState.GetAvailableSpace();
 
1703
 
 
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)) {
 
1708
 
 
1709
    // If the text is left-aligned, then we try and avoid reflowing the lines
 
1710
    const nsStyleText* styleText = GetStyleText();
 
1711
 
 
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;
 
1716
    }
 
1717
  }
 
1718
 
 
1719
#ifdef DEBUG
 
1720
  if (gDisableResizeOpt) {
 
1721
    tryAndSkipLines = PR_FALSE;
 
1722
  }
 
1723
  if (gNoisyReflow) {
 
1724
    if (!tryAndSkipLines) {
 
1725
      const nsStyleText* styleText = GetStyleText();
 
1726
      IndentBy(stdout, gNoiseIndent);
 
1727
      ListTag(stdout);
 
1728
      printf(": marking all lines dirty: reason=%d availWidth=%d textAlign=%d\n",
 
1729
             aState.mReflowState.reason,
 
1730
             aState.mReflowState.availableWidth,
 
1731
             styleText->mTextAlign);
 
1732
    }
 
1733
  }
 
1734
#endif
 
1735
 
 
1736
  if (tryAndSkipLines) {
 
1737
    nscoord newAvailWidth = aState.mReflowState.mComputedBorderPadding.left;
 
1738
     
 
1739
    if (NS_SHRINKWRAPWIDTH == aState.mReflowState.mComputedWidth) {
 
1740
      if (NS_UNCONSTRAINEDSIZE != aState.mReflowState.mComputedMaxWidth) {
 
1741
        newAvailWidth += aState.mReflowState.mComputedMaxWidth;
 
1742
      }
 
1743
      else {
 
1744
        newAvailWidth += aState.mReflowState.availableWidth;
 
1745
      }
 
1746
    } else {
 
1747
      if (NS_UNCONSTRAINEDSIZE != aState.mReflowState.mComputedWidth) {
 
1748
        newAvailWidth += aState.mReflowState.mComputedWidth;
 
1749
      }
 
1750
      else {
 
1751
        newAvailWidth += aState.mReflowState.availableWidth;
 
1752
      }
 
1753
    }
 
1754
    NS_ASSERTION(NS_UNCONSTRAINEDSIZE != newAvailWidth, "bad math, newAvailWidth is infinite");
 
1755
 
 
1756
#ifdef DEBUG
 
1757
    if (gNoisyReflow) {
 
1758
      IndentBy(stdout, gNoiseIndent);
 
1759
      ListTag(stdout);
 
1760
      printf(": trying to avoid marking all lines dirty\n");
 
1761
    }
 
1762
#endif
 
1763
 
 
1764
    for (line_iterator line = begin_lines(), line_end = end_lines();
 
1765
         line != line_end;
 
1766
         ++line)
 
1767
    {
 
1768
      // We let child blocks make their own decisions the same
 
1769
      // way we are here.
 
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)) {
 
1777
        line->MarkDirty();
 
1778
      }
 
1779
 
 
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 ");
 
1784
      }
 
1785
#endif
 
1786
#ifdef DEBUG
 
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());
 
1798
      }
 
1799
#endif
 
1800
    }
 
1801
  }
 
1802
  else {
 
1803
    // Mark everything dirty
 
1804
    for (line_iterator line = begin_lines(), line_end = end_lines();
 
1805
         line != line_end;
 
1806
         ++line)
 
1807
    {
 
1808
      line->MarkDirty();
 
1809
    }
 
1810
  }
 
1811
  return NS_OK;
 
1812
}
 
1813
 
 
1814
//----------------------------------------
 
1815
 
 
1816
nsBlockFrame::line_iterator
 
1817
nsBlockFrame::FindLineFor(nsIFrame* aFrame)
 
1818
{
 
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?");
 
1825
#endif
 
1826
 
 
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))
 
1833
      return line;
 
1834
 
 
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();
 
1839
           fc != nsnull;
 
1840
           fc = fc->Next()) {
 
1841
        if (aFrame == fc->mPlaceholder->GetOutOfFlowFrame())
 
1842
          return line;
 
1843
      }
 
1844
    }      
 
1845
  }
 
1846
 
 
1847
  return line_end;
 
1848
}
 
1849
 
 
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.
 
1853
nsresult 
 
1854
nsBlockFrame::GetCurrentLine(nsBlockReflowState *aState, nsLineBox ** aOutCurrentLine)
 
1855
{
 
1856
  if (!aState || !aOutCurrentLine) return NS_ERROR_FAILURE;
 
1857
  *aOutCurrentLine = aState->mCurrentLine;
 
1858
  return NS_OK;  
 
1859
}
 
1860
 
 
1861
/**
 
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.
 
1867
 *
 
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).
 
1871
 */
 
1872
void
 
1873
nsBlockFrame::PropagateFloatDamage(nsBlockReflowState& aState,
 
1874
                                   nsLineBox* aLine,
 
1875
                                   nscoord aDeltaY)
 
1876
{
 
1877
  NS_PRECONDITION(!aLine->IsDirty(), "should never be called on dirty lines");
 
1878
 
 
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)) {
 
1885
      aLine->MarkDirty();
 
1886
      return;
 
1887
    }
 
1888
  }
 
1889
 
 
1890
  if (aDeltaY) {
 
1891
    // Cases we need to find:
 
1892
    //
 
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
 
1901
    //    size.)
 
1902
    //
 
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);
 
1912
#endif
 
1913
    // Mark the line dirty if:
 
1914
    //  1. It used to be impacted by a float and now isn't, or vice
 
1915
    //     versa.
 
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())) {
 
1922
      aLine->MarkDirty();
 
1923
    }
 
1924
  }
 
1925
}
 
1926
 
 
1927
// NOTE:  The first parameter *must* be passed by value.
 
1928
static PRBool
 
1929
WrappedLinesAreDirty(nsLineList::iterator aLine,
 
1930
                     const nsLineList::iterator aLineEnd)
 
1931
{
 
1932
  if (aLine->IsInline()) {
 
1933
    while (aLine->IsLineWrapped()) {
 
1934
      ++aLine;
 
1935
      if (aLine == aLineEnd) {
 
1936
        break;
 
1937
      }
 
1938
 
 
1939
      NS_ASSERTION(!aLine->IsBlock(), "didn't expect a block line");
 
1940
      if (aLine->IsDirty()) {
 
1941
        // we found a continuing line that is dirty
 
1942
        return PR_TRUE;
 
1943
      }
 
1944
    }
 
1945
  }
 
1946
 
 
1947
  return PR_FALSE;
 
1948
}
 
1949
 
 
1950
static void PlaceFrameView(nsIPresContext* aPresContext, nsIFrame* aFrame);
 
1951
 
 
1952
/**
 
1953
 * Reflow the dirty lines
 
1954
 */
 
1955
nsresult
 
1956
nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
 
1957
{
 
1958
  nsresult rv = NS_OK;
 
1959
  PRBool keepGoing = PR_TRUE;
 
1960
  PRBool repositionViews = PR_FALSE; // should we really need this?
 
1961
 
 
1962
#ifdef DEBUG
 
1963
  if (gNoisyReflow) {
 
1964
    if (aState.mReflowState.reason == eReflowReason_Incremental) {
 
1965
      IndentBy(stdout, gNoiseIndent);
 
1966
      ListTag(stdout);
 
1967
      printf(": incrementally reflowing dirty lines");
 
1968
 
 
1969
      nsHTMLReflowCommand *command = aState.mReflowState.path->mReflowCommand;
 
1970
      if (command) {
 
1971
        nsReflowType type;
 
1972
        command->GetType(type);
 
1973
        printf(": type=%s(%d)", kReflowCommandType[type], type);
 
1974
      }
 
1975
    }
 
1976
    else {
 
1977
      IndentBy(stdout, gNoiseIndent);
 
1978
      ListTag(stdout);
 
1979
      printf(": reflowing dirty lines");
 
1980
    }
 
1981
    printf(" computedWidth=%d\n", aState.mReflowState.mComputedWidth);
 
1982
    gNoiseIndent++;
 
1983
  }
 
1984
#endif
 
1985
 
 
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;
 
1991
  
 
1992
    // the amount by which we will slide the current line if it is not
 
1993
    // dirty
 
1994
  nscoord deltaY = 0;
 
1995
 
 
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;
 
2000
  
 
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()) {
 
2004
#ifdef DEBUG
 
2005
    if (gNoisyReflow) {
 
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());
 
2015
      gNoiseIndent++;
 
2016
    }
 
2017
#endif
 
2018
 
 
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
 
2022
    // the line.
 
2023
    if ((NS_UNCONSTRAINEDSIZE != aState.mReflowState.availableHeight) ||
 
2024
        (!line->IsDirty() &&
 
2025
         aState.GetFlag(BRS_COMPUTEMAXWIDTH) &&
 
2026
         ::WrappedLinesAreDirty(line, line_end))) {
 
2027
      line->MarkDirty();
 
2028
    }
 
2029
 
 
2030
    // Make sure |aState.mPrevBottomMargin| is at the correct position
 
2031
    // before calling PropagateFloatDamage.
 
2032
    if (needToRecoverState &&
 
2033
        (line->IsDirty() || line->IsPreviousMarginDirty())) {
 
2034
 
 
2035
      // If the previous line had a 'clear' in it, we need to do the
 
2036
      // same thing that we do in |PlaceLine|.
 
2037
      --line;
 
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);
 
2044
      }
 
2045
      ++line;
 
2046
 
 
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);
 
2051
    }
 
2052
 
 
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,
 
2058
      // but whatever...
 
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.
 
2063
        line->MarkDirty();
 
2064
      } else {
 
2065
        deltaY = aState.mY + aState.mPrevBottomMargin.get() - line->mBounds.y;
 
2066
      }
 
2067
    }
 
2068
    line->ClearPreviousMarginDirty();
 
2069
 
 
2070
    // See if there's any reflow damage that requires that we mark the
 
2071
    // line dirty.
 
2072
    if (!line->IsDirty()) {
 
2073
      PropagateFloatDamage(aState, line, deltaY);
 
2074
    }
 
2075
 
 
2076
    if (needToRecoverState) {
 
2077
      needToRecoverState = PR_FALSE;
 
2078
 
 
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();
 
2084
    }
 
2085
 
 
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
 
2091
      // aState.mY.
 
2092
      nscoord oldY = line->mBounds.y;
 
2093
      nscoord oldYMost = line->mBounds.YMost();
 
2094
 
 
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)) {
 
2099
        return rv;
 
2100
      }
 
2101
      if (!keepGoing) {
 
2102
        if (0 == line->GetChildCount()) {
 
2103
          DeleteLine(aState, line, line_end);
 
2104
        }
 
2105
        break;
 
2106
      }
 
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
 
2110
        // line dirty.
 
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|
 
2116
        }
 
2117
      } else {
 
2118
        deltaY = line->mBounds.YMost() - oldYMost;
 
2119
      }
 
2120
    } else {
 
2121
      if (deltaY != 0)
 
2122
        SlideLine(aState, line, deltaY);
 
2123
      else
 
2124
        repositionViews = PR_TRUE;
 
2125
 
 
2126
      // XXX EVIL O(N^2) EVIL
 
2127
      aState.RecoverStateFrom(line, deltaY);
 
2128
 
 
2129
      // Keep mY up to date in case we're propagating reflow damage.
 
2130
      aState.mY = line->mBounds.YMost();
 
2131
      needToRecoverState = PR_TRUE;
 
2132
    }
 
2133
 
 
2134
#ifdef DEBUG
 
2135
    if (gNoisyReflow) {
 
2136
      gNoiseIndent--;
 
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());
 
2145
    }
 
2146
#endif
 
2147
  }
 
2148
 
 
2149
  if (needToRecoverState) {
 
2150
    // Is this expensive?
 
2151
    aState.ReconstructMarginAbove(line);
 
2152
 
 
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();
 
2157
  }
 
2158
 
 
2159
  // Should we really have to do this?
 
2160
  if (repositionViews)
 
2161
    ::PlaceFrameView(aState.mPresContext, this);
 
2162
 
 
2163
  // Pull data from a next-in-flow if there's still room for more
 
2164
  // content here.
 
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;
 
2171
      continue;
 
2172
    }
 
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);
 
2181
      continue;
 
2182
    }
 
2183
 
 
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();
 
2189
    while (--n >= 0) {
 
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);
 
2194
      lastFrame = frame;
 
2195
      frame = frame->GetNextSibling();
 
2196
    }
 
2197
    lastFrame->SetNextSibling(nsnull);
 
2198
 
 
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);
 
2203
 
 
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.
 
2207
    //
 
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.
 
2211
 
 
2212
    if (line->HasFloats()) {
 
2213
      nsFloatCache* fc = line->GetFirstFloat();
 
2214
      while (fc) {
 
2215
        if (fc->mPlaceholder) {
 
2216
          nsIFrame* floatFrame = fc->mPlaceholder->GetOutOfFlowFrame();
 
2217
          if (floatFrame)
 
2218
            aState.mNextInFlow->mFloats.RemoveFrame(floatFrame);
 
2219
        }
 
2220
        fc = fc->Next();
 
2221
      }
 
2222
    }
 
2223
 
 
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)) {
 
2231
        return rv;
 
2232
      }
 
2233
      if (!keepGoing) {
 
2234
        if (0 == line->GetChildCount()) {
 
2235
          DeleteLine(aState, line, line_end);
 
2236
        }
 
2237
        break;
 
2238
      }
 
2239
 
 
2240
      // If this is an inline frame then its time to stop
 
2241
      ++line;
 
2242
      aState.AdvanceToNextLine();
 
2243
    }
 
2244
  }
 
2245
 
 
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);
 
2250
 
 
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;
 
2254
  }
 
2255
 
 
2256
#ifdef DEBUG
 
2257
  if (gNoisyReflow) {
 
2258
    gNoiseIndent--;
 
2259
    IndentBy(stdout, gNoiseIndent);
 
2260
    ListTag(stdout);
 
2261
    printf(": done reflowing dirty lines (status=%x)\n",
 
2262
           aState.mReflowStatus);
 
2263
  }
 
2264
#endif
 
2265
 
 
2266
  return rv;
 
2267
}
 
2268
 
 
2269
void
 
2270
nsBlockFrame::DeleteLine(nsBlockReflowState& aState,
 
2271
                         nsLineList::iterator aLine,
 
2272
                         nsLineList::iterator aLineEnd)
 
2273
{
 
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();
 
2286
  }
 
2287
}
 
2288
 
 
2289
/**
 
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.)
 
2294
 */
 
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;
 
2309
}
 
2310
 
 
2311
/**
 
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.
 
2315
 */
 
2316
nsresult
 
2317
nsBlockFrame::ReflowLine(nsBlockReflowState& aState,
 
2318
                         line_iterator aLine,
 
2319
                         PRBool* aKeepReflowGoing,
 
2320
                         PRBool aDamageDirtyArea)
 
2321
{
 
2322
  nsresult rv = NS_OK;
 
2323
 
 
2324
  NS_ABORT_IF_FALSE(aLine->GetChildCount(), "reflowing empty line");
 
2325
 
 
2326
  // Setup the line-layout for the new line
 
2327
  aState.mCurrentLine = aLine;
 
2328
  aLine->ClearDirty();
 
2329
 
 
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();
 
2336
 
 
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...
 
2351
        nsRect  dirtyRect;
 
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);
 
2356
#endif
 
2357
        Invalidate(dirtyRect);
 
2358
      } else {
 
2359
        nsRect combinedAreaHStrip, combinedAreaVStrip;
 
2360
        nsRect boundsHStrip, boundsVStrip;
 
2361
        GetRectDifferenceStrips(oldBounds, newBounds,
 
2362
                                &boundsHStrip, &boundsVStrip);
 
2363
        GetRectDifferenceStrips(oldCombinedArea, lineCombinedArea,
 
2364
                                &combinedAreaHStrip, &combinedAreaVStrip);
 
2365
 
 
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);
 
2375
#endif
 
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);
 
2382
      }
 
2383
    }
 
2384
  }
 
2385
  else {
 
2386
    nsRect oldCombinedArea(aLine->GetCombinedArea());
 
2387
    aLine->SetLineWrapped(PR_FALSE);
 
2388
 
 
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()
 
2394
    // for details).
 
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);
 
2406
 
 
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?
 
2410
 
 
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 "
 
2415
               "reflow\n",
 
2416
               __FILE__, __LINE__);
 
2417
#endif
 
2418
 
 
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);
 
2427
      aState.mY = oldY;
 
2428
      aState.mPrevBottomMargin = oldPrevBottomMargin;
 
2429
      aState.SetFlag(BRS_UNCONSTRAINEDWIDTH, oldUnconstrainedWidth);
 
2430
      aState.mSpaceManager->PopState();
 
2431
 
 
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);
 
2437
#endif
 
2438
      aState.UpdateMaximumWidth(aLine->mMaximumWidth);
 
2439
 
 
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);
 
2447
 
 
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);
 
2453
 
 
2454
    } else {
 
2455
      rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing, aDamageDirtyArea);
 
2456
      if (NS_SUCCEEDED(rv))
 
2457
      {
 
2458
        if (aState.GetFlag(BRS_COMPUTEMAXWIDTH))
 
2459
        {
 
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);
 
2463
#endif
 
2464
          aState.UpdateMaximumWidth(aLine->mMaximumWidth);
 
2465
        }
 
2466
        if (aState.GetFlag(BRS_COMPUTEMAXELEMENTWIDTH))
 
2467
        {
 
2468
#ifdef DEBUG
 
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);
 
2474
          }
 
2475
#endif
 
2476
          aState.UpdateMaxElementWidth(aLine->mMaxElementWidth);
 
2477
        }
 
2478
      }
 
2479
    }
 
2480
 
 
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
 
2486
 
 
2487
      nsRect dirtyRect;
 
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());
 
2495
#endif
 
2496
      Invalidate(dirtyRect);
 
2497
    }
 
2498
  }
 
2499
 
 
2500
  return rv;
 
2501
}
 
2502
 
 
2503
/**
 
2504
 * Pull frame from the next available location (one of our lines or
 
2505
 * one of our next-in-flows lines).
 
2506
 */
 
2507
nsresult
 
2508
nsBlockFrame::PullFrame(nsBlockReflowState& aState,
 
2509
                        line_iterator aLine,
 
2510
                        PRBool aDamageDeletedLines,
 
2511
                        nsIFrame*& aFrameResult)
 
2512
{
 
2513
  aFrameResult = nsnull;
 
2514
 
 
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);
 
2519
  }
 
2520
 
 
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);
 
2528
    }
 
2529
 
 
2530
    nextInFlow = (nsBlockFrame*) nextInFlow->mNextInFlow;
 
2531
    aState.mNextInFlow = nextInFlow;
 
2532
  }
 
2533
 
 
2534
  return NS_OK;
 
2535
}
 
2536
 
 
2537
/**
 
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).
 
2542
 *
 
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.
 
2549
 */
 
2550
nsresult
 
2551
nsBlockFrame::PullFrameFrom(nsBlockReflowState& aState,
 
2552
                            nsLineBox* aLine,
 
2553
                            nsLineList& aFromContainer,
 
2554
                            nsLineList::iterator aFromLine,
 
2555
                            PRBool aUpdateGeometricParent,
 
2556
                            PRBool aDamageDeletedLines,
 
2557
                            nsIFrame*& aFrameResult)
 
2558
{
 
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");
 
2563
 
 
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
 
2567
    // we stop pulling.
 
2568
    aFrameResult = nsnull;
 
2569
  }
 
2570
  else {
 
2571
    // Take frame from fromLine
 
2572
    nsIFrame* frame = fromLine->mFirstChild;
 
2573
    aLine->SetChildCount(aLine->GetChildCount() + 1);
 
2574
 
 
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();
 
2581
    }
 
2582
    else {
 
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);
 
2589
      }
 
2590
      if (aFromLine.next() != end_lines())
 
2591
        aFromLine.next()->MarkPreviousMarginDirty();
 
2592
 
 
2593
      Invalidate(fromLine->GetCombinedArea());
 
2594
      aFromContainer.erase(aFromLine);
 
2595
      aState.FreeLineBox(fromLine);
 
2596
    }
 
2597
 
 
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);
 
2603
 
 
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);
 
2608
      
 
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);
 
2613
      }
 
2614
      frame->SetNextSibling(nsnull);
 
2615
    }
 
2616
 
 
2617
    // Stop pulling because we found a frame to pull
 
2618
    aFrameResult = frame;
 
2619
#ifdef DEBUG
 
2620
    VerifyLines(PR_TRUE);
 
2621
#endif
 
2622
  }
 
2623
  return NS_OK;
 
2624
}
 
2625
 
 
2626
static void
 
2627
PlaceFrameView(nsIPresContext* aPresContext,
 
2628
               nsIFrame*       aFrame)
 
2629
{
 
2630
  if (aFrame->HasView())
 
2631
    nsContainerFrame::PositionFrameView(aPresContext, aFrame);
 
2632
  else
 
2633
    nsContainerFrame::PositionChildViews(aPresContext, aFrame);
 
2634
}
 
2635
 
 
2636
void
 
2637
nsBlockFrame::SlideLine(nsBlockReflowState& aState,
 
2638
                        nsLineBox* aLine, nscoord aDY)
 
2639
{
 
2640
  NS_PRECONDITION(aDY != 0, "why slide a line nowhere?");
 
2641
 
 
2642
  Invalidate(aLine->GetCombinedArea());
 
2643
  // Adjust line state
 
2644
  aLine->SlideBy(aDY);
 
2645
  Invalidate(aLine->GetCombinedArea());
 
2646
 
 
2647
  // Adjust the frames in the line
 
2648
  nsIFrame* kid = aLine->mFirstChild;
 
2649
  if (!kid) {
 
2650
    return;
 
2651
  }
 
2652
 
 
2653
  if (aLine->IsBlock()) {
 
2654
    if (aDY) {
 
2655
      nsPoint p = kid->GetPosition();
 
2656
      p.y += aDY;
 
2657
      kid->SetPosition(p);
 
2658
    }
 
2659
 
 
2660
    // Make sure the frame's view and any child views are updated
 
2661
    ::PlaceFrameView(aState.mPresContext, kid);
 
2662
  }
 
2663
  else {
 
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();
 
2669
    while (--n >= 0) {
 
2670
      if (aDY) {
 
2671
        nsPoint p = kid->GetPosition();
 
2672
        p.y += aDY;
 
2673
        kid->SetPosition(p);
 
2674
      }
 
2675
      // Make sure the frame's view and any child views are updated
 
2676
      ::PlaceFrameView(aState.mPresContext, kid);
 
2677
      kid = kid->GetNextSibling();
 
2678
    }
 
2679
  }
 
2680
}
 
2681
 
 
2682
NS_IMETHODIMP 
 
2683
nsBlockFrame::AttributeChanged(nsIPresContext* aPresContext,
 
2684
                               nsIContent*     aChild,
 
2685
                               PRInt32         aNameSpaceID,
 
2686
                               nsIAtom*        aAttribute,
 
2687
                               PRInt32         aModType)
 
2688
{
 
2689
  nsresult rv = nsBlockFrameSuper::AttributeChanged(aPresContext, aChild,
 
2690
                                                    aNameSpaceID, aAttribute,
 
2691
                                                    aModType);
 
2692
 
 
2693
  if (NS_FAILED(rv)) {
 
2694
    return rv;
 
2695
  }
 
2696
  if (nsHTMLAtoms::start == aAttribute) {
 
2697
    // XXX Not sure if this is necessary anymore
 
2698
    RenumberLists(aPresContext);
 
2699
 
 
2700
    nsHTMLReflowCommand* reflowCmd;
 
2701
    rv = NS_NewHTMLReflowCommand(&reflowCmd, this,
 
2702
                                 eReflowType_ContentChanged,
 
2703
                                 nsnull,
 
2704
                                 aAttribute);
 
2705
    if (NS_SUCCEEDED(rv))
 
2706
      aPresContext->PresShell()->AppendReflowCommand(reflowCmd);
 
2707
  }
 
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;
 
2713
      
 
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)) {
 
2721
          break;
 
2722
        }
 
2723
        nextAncestor = nextAncestor->GetParent();
 
2724
      }
 
2725
 
 
2726
      // Tell the enclosing block frame to renumber list items within
 
2727
      // itself
 
2728
      if (nsnull != blockParent) {
 
2729
        // XXX Not sure if this is necessary anymore
 
2730
        blockParent->RenumberLists(aPresContext);
 
2731
 
 
2732
        nsHTMLReflowCommand* reflowCmd;
 
2733
        rv = NS_NewHTMLReflowCommand(&reflowCmd, blockParent,
 
2734
                                     eReflowType_ContentChanged,
 
2735
                                     nsnull,
 
2736
                                     aAttribute);
 
2737
        if (NS_SUCCEEDED(rv))
 
2738
          aPresContext->PresShell()->AppendReflowCommand(reflowCmd);
 
2739
      }
 
2740
    }
 
2741
  }
 
2742
 
 
2743
  return rv;
 
2744
}
 
2745
 
 
2746
inline PRBool
 
2747
IsBorderZero(nsStyleUnit aUnit, nsStyleCoord &aCoord)
 
2748
{
 
2749
    return ((aUnit == eStyleUnit_Coord && aCoord.GetCoordValue() == 0));
 
2750
}
 
2751
 
 
2752
inline PRBool
 
2753
IsPaddingZero(nsStyleUnit aUnit, nsStyleCoord &aCoord)
 
2754
{
 
2755
    return (aUnit == eStyleUnit_Null ||
 
2756
            (aUnit == eStyleUnit_Coord && aCoord.GetCoordValue() == 0) ||
 
2757
            (aUnit == eStyleUnit_Percent && aCoord.GetPercentValue() == 0.0));
 
2758
}
 
2759
 
 
2760
inline PRBool
 
2761
IsMarginZero(nsStyleUnit aUnit, nsStyleCoord &aCoord)
 
2762
{
 
2763
    return (aUnit == eStyleUnit_Null ||
 
2764
            aUnit == eStyleUnit_Auto ||
 
2765
            (aUnit == eStyleUnit_Coord && aCoord.GetCoordValue() == 0) ||
 
2766
            (aUnit == eStyleUnit_Percent && aCoord.GetPercentValue() == 0.0));
 
2767
}
 
2768
 
 
2769
/* virtual */ PRBool
 
2770
nsBlockFrame::IsEmpty()
 
2771
{
 
2772
  const nsStylePosition* position = GetStylePosition();
 
2773
 
 
2774
  switch (position->mMinHeight.GetUnit()) {
 
2775
    case eStyleUnit_Coord:
 
2776
      if (position->mMinHeight.GetCoordValue() != 0)
 
2777
        return PR_FALSE;
 
2778
      break;
 
2779
    case eStyleUnit_Percent:
 
2780
      if (position->mMinHeight.GetPercentValue() != 0.0f)
 
2781
        return PR_FALSE;
 
2782
      break;
 
2783
    default:
 
2784
      return PR_FALSE;
 
2785
  }
 
2786
 
 
2787
  switch (position->mHeight.GetUnit()) {
 
2788
    case eStyleUnit_Auto:
 
2789
      break;
 
2790
    case eStyleUnit_Coord:
 
2791
      if (position->mHeight.GetCoordValue() != 0)
 
2792
        return PR_FALSE;
 
2793
      break;
 
2794
    case eStyleUnit_Percent:
 
2795
      if (position->mHeight.GetPercentValue() != 0.0f)
 
2796
        return PR_FALSE;
 
2797
      break;
 
2798
    default:
 
2799
      return PR_FALSE;
 
2800
  }
 
2801
 
 
2802
  const nsStyleBorder* border = GetStyleBorder();
 
2803
  const nsStylePadding* padding = GetStylePadding();
 
2804
  nsStyleCoord coord;
 
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))) {
 
2815
    return PR_FALSE;
 
2816
  }
 
2817
 
 
2818
  for (line_iterator line = begin_lines(), line_end = end_lines();
 
2819
       line != line_end;
 
2820
       ++line)
 
2821
  {
 
2822
    if (!line->IsEmpty())
 
2823
      return PR_FALSE;
 
2824
  }
 
2825
  return PR_TRUE;
 
2826
}
 
2827
 
 
2828
PRBool
 
2829
nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState,
 
2830
                                   nsLineBox* aLine)
 
2831
{
 
2832
  if (aState.GetFlag(BRS_APPLYTOPMARGIN)) {
 
2833
    // Apply short-circuit check to avoid searching the line list
 
2834
    return PR_TRUE;
 
2835
  }
 
2836
 
 
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
 
2840
    // applies.
 
2841
    aState.SetFlag(BRS_APPLYTOPMARGIN, PR_TRUE);
 
2842
    return PR_TRUE;
 
2843
  }
 
2844
 
 
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);
 
2851
      return PR_TRUE;
 
2852
    }
 
2853
    // No need to apply the top margin if the line has floats.  We
 
2854
    // should collapse anyway (bug 44419)
 
2855
  }
 
2856
 
 
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).
 
2860
  return PR_FALSE;
 
2861
}
 
2862
 
 
2863
nsIFrame*
 
2864
nsBlockFrame::GetTopBlockChild(nsIPresContext* aPresContext)
 
2865
{
 
2866
  if (mLines.empty())
 
2867
    return nsnull;
 
2868
 
 
2869
  nsLineBox *firstLine = mLines.front();
 
2870
  if (firstLine->IsBlock())
 
2871
    return firstLine->mFirstChild;
 
2872
 
 
2873
  if (!firstLine->IsEmpty())
 
2874
    return nsnull;
 
2875
 
 
2876
  line_iterator secondLine = begin_lines();
 
2877
  ++secondLine;
 
2878
  if (secondLine == end_lines() || !secondLine->IsBlock())
 
2879
    return nsnull;
 
2880
 
 
2881
  return secondLine->mFirstChild;
 
2882
}
 
2883
 
 
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. 
 
2887
void
 
2888
nsBlockFrame::UndoSplitPlaceholders(nsBlockReflowState& aState,
 
2889
                                    nsIFrame*           aLastPlaceholder)
 
2890
{
 
2891
  nsIFrame* undoPlaceholder = nsnull;
 
2892
  if (aLastPlaceholder) {
 
2893
    undoPlaceholder = aLastPlaceholder->GetNextSibling();
 
2894
    aLastPlaceholder->SetNextSibling(nsnull);
 
2895
  }
 
2896
  else {
 
2897
    // just remove the property
 
2898
    nsFrameList* overflowPlace = GetOverflowPlaceholders(aState.mPresContext, PR_TRUE);
 
2899
    delete overflowPlace;
 
2900
  }
 
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);
 
2907
  }
 
2908
}
 
2909
 
 
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.
 
2914
static PRUint8
 
2915
CombineBreakType(PRUint8 aOrigBreakType, 
 
2916
                 PRUint8 aNewBreakType)
 
2917
{
 
2918
  PRUint8 breakType = aOrigBreakType;
 
2919
  switch(breakType) {
 
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;
 
2924
    }
 
2925
    break;
 
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;
 
2930
    }
 
2931
    break;
 
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;
 
2937
    }
 
2938
  }
 
2939
  return breakType;
 
2940
}
 
2941
 
 
2942
nsresult
 
2943
nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
 
2944
                               line_iterator aLine,
 
2945
                               PRBool* aKeepReflowGoing)
 
2946
{
 
2947
  NS_PRECONDITION(*aKeepReflowGoing, "bad caller");
 
2948
 
 
2949
  nsresult rv = NS_OK;
 
2950
 
 
2951
  nsIFrame* frame = aLine->mFirstChild;
 
2952
  if (!frame) {
 
2953
    NS_ASSERTION(PR_FALSE, "program error - unexpected empty line"); 
 
2954
    return NS_ERROR_NULL_POINTER; 
 
2955
  }
 
2956
 
 
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));
 
2962
 
 
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
 
2966
  // deeper.
 
2967
  PRBool applyTopMargin = PR_FALSE;
 
2968
  nsIFrame* framePrevInFlow;
 
2969
  frame->GetPrevInFlow(&framePrevInFlow);
 
2970
  if (nsnull == framePrevInFlow) {
 
2971
    applyTopMargin = ShouldApplyTopMargin(aState, aLine);
 
2972
  }
 
2973
 
 
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;
 
2981
  }
 
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;
 
2988
    }
 
2989
#ifdef NOISY_VERTICAL_MARGINS
 
2990
    ListTag(stdout);
 
2991
    printf(": y=%d child ", aState.mY);
 
2992
    ListTag(stdout, frame);
 
2993
    printf(" has clear of %d => %s, mPrevBottomMargin=%d\n",
 
2994
           breakType,
 
2995
           applyTopMargin ? "applyTopMargin" : "nope",
 
2996
           aState.mPrevBottomMargin);
 
2997
#endif
 
2998
  }
 
2999
 
 
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
 
3005
 
 
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.
 
3009
 
 
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);
 
3016
 
 
3017
    // Now compute the collapsed margin-top value into aState.mPrevBottomMargin
 
3018
    nsCollapsingMargin oldPrevBottomMargin = aState.mPrevBottomMargin;
 
3019
    nsBlockReflowContext::ComputeCollapsedTopMargin(aState.mPresContext,
 
3020
                                                    reflowState,
 
3021
                                                    aState.mPrevBottomMargin);
 
3022
    topMargin = aState.mPrevBottomMargin.get();
 
3023
    aState.mPrevBottomMargin = oldPrevBottomMargin; // perhaps not needed
 
3024
 
 
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;
 
3029
  }
 
3030
 
 
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");
 
3035
#endif
 
3036
  PRBool isImpacted = aState.IsImpactedByFloat() ? PR_TRUE : PR_FALSE;
 
3037
  aLine->SetLineIsImpactedByFloat(isImpacted);
 
3038
  nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE;
 
3039
  frame->IsSplittable(splitType);
 
3040
  nsRect availSpace;
 
3041
  aState.ComputeBlockAvailSpace(frame, splitType, display, availSpace);
 
3042
 
 
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
 
3045
  // computed (sigh).
 
3046
  if (topMargin) {
 
3047
    aState.mY -= topMargin;
 
3048
    availSpace.y -= topMargin;
 
3049
    if (NS_UNCONSTRAINEDSIZE != availSpace.height) {
 
3050
      availSpace.height += topMargin;
 
3051
    }
 
3052
  }
 
3053
 
 
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;
 
3057
 
 
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);
 
3069
 
 
3070
  // Remove the frame from the reflow tree.
 
3071
  if (aState.mReflowState.path)
 
3072
    aState.mReflowState.path->RemoveChild(frame);
 
3073
 
 
3074
  if (NS_FAILED(rv)) {
 
3075
    return rv;
 
3076
  }
 
3077
  aState.mPrevChild = frame;
 
3078
 
 
3079
#if defined(REFLOW_STATUS_COVERAGE)
 
3080
  RecordReflowStatus(PR_TRUE, frameReflowStatus);
 
3081
#endif
 
3082
 
 
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;
 
3089
  }
 
3090
  else {
 
3091
    // Note: line-break-after a block is a nop
 
3092
 
 
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);
 
3101
 
 
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...
 
3106
      aLine->MarkDirty();
 
3107
      aState.SetFlag(BRS_NEEDRESIZEREFLOW, PR_TRUE);
 
3108
    }
 
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;
 
3115
      }
 
3116
    }
 
3117
    aLine->SetCombinedArea(combinedArea);
 
3118
    if (*aKeepReflowGoing) {
 
3119
      // Some of the child block fit
 
3120
 
 
3121
      // Advance to new Y position
 
3122
      nscoord newY = aLine->mBounds.YMost();
 
3123
      aState.mY = newY;
 
3124
 
 
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);
 
3130
        if (NS_FAILED(rv)) 
 
3131
          return rv;
 
3132
 
 
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;
 
3139
          }
 
3140
          mLines.after_insert(aLine, line);
 
3141
        }
 
3142
 
 
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;
 
3148
 
 
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
 
3154
        ListTag(stdout);
 
3155
        printf(": reflow incomplete, frame=");
 
3156
        nsFrame::ListTag(stdout, frame);
 
3157
        printf(" prevBottomMargin=%d, setting to zero\n",
 
3158
               aState.mPrevBottomMargin);
 
3159
#endif
 
3160
        aState.mPrevBottomMargin.Zero();
 
3161
      }
 
3162
      else {
 
3163
#ifdef NOISY_VERTICAL_MARGINS
 
3164
        ListTag(stdout);
 
3165
        printf(": reflow complete for ");
 
3166
        nsFrame::ListTag(stdout, frame);
 
3167
        printf(" prevBottomMargin=%d collapsedBottomMargin=%d\n",
 
3168
               aState.mPrevBottomMargin, collapsedBottomMargin.get());
 
3169
#endif
 
3170
        aState.mPrevBottomMargin = collapsedBottomMargin;
 
3171
      }
 
3172
#ifdef NOISY_VERTICAL_MARGINS
 
3173
      ListTag(stdout);
 
3174
      printf(": frame=");
 
3175
      nsFrame::ListTag(stdout, frame);
 
3176
      printf(" carriedOutBottomMargin=%d collapsedBottomMargin=%d => %d\n",
 
3177
             brc.GetCarriedOutBottomMargin(), collapsedBottomMargin.get(),
 
3178
             aState.mPrevBottomMargin);
 
3179
#endif
 
3180
 
 
3181
      // Post-process the "line"
 
3182
      nscoord maxElementWidth = 0;
 
3183
      if (aState.GetFlag(BRS_COMPUTEMAXELEMENTWIDTH)) {
 
3184
        maxElementWidth = brc.GetMaxElementWidth();
 
3185
      }
 
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);
 
3191
      }
 
3192
      PostPlaceLine(aState, aLine, maxElementWidth);
 
3193
 
 
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;
 
3199
      }
 
3200
 
 
3201
      // Place the "marker" (bullet) frame.
 
3202
      //
 
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
 
3205
      // first line box.
 
3206
      //
 
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);
 
3219
 
 
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?)
 
3224
 
 
3225
        // Tall bullets won't look particularly nice here...
 
3226
        nsRect bbox = mBullet->GetRect();
 
3227
        nscoord bulletTopMargin = applyTopMargin
 
3228
                                    ? collapsedBottomMargin.get()
 
3229
                                    : 0;
 
3230
        bbox.y = aState.BorderPadding().top + mAscent -
 
3231
          metrics.ascent + bulletTopMargin;
 
3232
        mBullet->SetRect(bbox);
 
3233
      }
 
3234
    }
 
3235
    else {
 
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();
 
3242
      }
 
3243
      else {
 
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;
 
3249
      }
 
3250
    }
 
3251
  }
 
3252
#ifdef DEBUG
 
3253
  VerifyLines(PR_TRUE);
 
3254
#endif
 
3255
  return rv;
 
3256
}
 
3257
 
 
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
 
3263
 
 
3264
nsresult
 
3265
nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState,
 
3266
                                 line_iterator aLine,
 
3267
                                 PRBool* aKeepReflowGoing,
 
3268
                                 PRBool aDamageDirtyArea,
 
3269
                                 PRBool aUpdateMaximumWidth)
 
3270
{
 
3271
  nsresult rv = NS_OK;
 
3272
  *aKeepReflowGoing = PR_TRUE;
 
3273
 
 
3274
#ifdef DEBUG
 
3275
  PRInt32 spins = 0;
 
3276
#endif
 
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
 
3281
    // large.
 
3282
    if (aState.mReflowState.mReflowDepth > 30) {//XXX layout-tune.h?
 
3283
      rv = DoReflowInlineFramesMalloc(aState, aLine, aKeepReflowGoing,
 
3284
                                      &lineReflowStatus,
 
3285
                                      aUpdateMaximumWidth, aDamageDirtyArea);
 
3286
    }
 
3287
    else {
 
3288
      rv = DoReflowInlineFramesAuto(aState, aLine, aKeepReflowGoing,
 
3289
                                    &lineReflowStatus,
 
3290
                                    aUpdateMaximumWidth, aDamageDirtyArea);
 
3291
    }
 
3292
    if (NS_FAILED(rv)) {
 
3293
      break;
 
3294
    }
 
3295
#ifdef DEBUG
 
3296
    spins++;
 
3297
    if (1000 == spins) {
 
3298
      ListTag(stdout);
 
3299
      printf(": yikes! spinning on a line over 1000 times!\n");
 
3300
      NS_ABORT();
 
3301
    }
 
3302
#endif
 
3303
  }
 
3304
  return rv;
 
3305
}
 
3306
 
 
3307
nsresult
 
3308
nsBlockFrame::DoReflowInlineFramesMalloc(nsBlockReflowState& aState,
 
3309
                                         line_iterator aLine,
 
3310
                                         PRBool* aKeepReflowGoing,
 
3311
                                         PRUint8* aLineReflowStatus,
 
3312
                                         PRBool aUpdateMaximumWidth,
 
3313
                                         PRBool aDamageDirtyArea)
 
3314
{
 
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));
 
3320
  if (!ll) {
 
3321
    return NS_ERROR_OUT_OF_MEMORY;
 
3322
  }
 
3323
  ll->Init(&aState, aState.mMinLineHeight, aState.mLineNumber);
 
3324
  nsresult rv = DoReflowInlineFrames(aState, *ll, aLine, aKeepReflowGoing,
 
3325
                                     aLineReflowStatus, aUpdateMaximumWidth, aDamageDirtyArea);
 
3326
  ll->EndLineReflow();
 
3327
  delete ll;
 
3328
  return rv;
 
3329
}
 
3330
 
 
3331
nsresult
 
3332
nsBlockFrame::DoReflowInlineFramesAuto(nsBlockReflowState& aState,
 
3333
                                       line_iterator aLine,
 
3334
                                       PRBool* aKeepReflowGoing,
 
3335
                                       PRUint8* aLineReflowStatus,
 
3336
                                       PRBool aUpdateMaximumWidth,
 
3337
                                       PRBool aDamageDirtyArea)
 
3338
{
 
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();
 
3348
  return rv;
 
3349
}
 
3350
 
 
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.  
 
3355
void
 
3356
nsBlockFrame::PushTruncatedPlaceholderLine(nsBlockReflowState& aState,
 
3357
                                           line_iterator       aLine,
 
3358
                                           nsIFrame*           aLastPlaceholder,
 
3359
                                           PRBool&             aKeepReflowGoing)
 
3360
{
 
3361
  UndoSplitPlaceholders(aState, aLastPlaceholder);
 
3362
 
 
3363
  line_iterator prevLine = aLine;
 
3364
  --prevLine;
 
3365
  PushLines(aState, prevLine);
 
3366
  aKeepReflowGoing = PR_FALSE;
 
3367
  aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
 
3368
}
 
3369
 
 
3370
nsresult
 
3371
nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState,
 
3372
                                   nsLineLayout& aLineLayout,
 
3373
                                   line_iterator aLine,
 
3374
                                   PRBool* aKeepReflowGoing,
 
3375
                                   PRUint8* aLineReflowStatus,
 
3376
                                   PRBool aUpdateMaximumWidth,
 
3377
                                   PRBool aDamageDirtyArea)
 
3378
{
 
3379
  // Forget all of the floats on the line
 
3380
  aLine->FreeFloats(aState.mFloatCacheFreeList);
 
3381
  aState.mFloatCombinedArea.SetRect(0, 0, 0, 0);
 
3382
 
 
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();
 
3387
  }
 
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);
 
3394
#endif
 
3395
 
 
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;
 
3402
  }
 
3403
  else {
 
3404
    /* XXX get the height right! */
 
3405
    availHeight = aState.mAvailSpaceRect.height;
 
3406
  }
 
3407
  if (aUpdateMaximumWidth) {
 
3408
    availWidth = NS_UNCONSTRAINEDSIZE;
 
3409
  }
 
3410
#ifdef IBMBIDI
 
3411
  else {
 
3412
    nscoord rightEdge = aState.mReflowState.mRightEdge;
 
3413
    if ( (rightEdge != NS_UNCONSTRAINEDSIZE)
 
3414
         && (availWidth < rightEdge) ) {
 
3415
      availWidth = rightEdge;
 
3416
    }
 
3417
  }
 
3418
#endif // IBMBIDI
 
3419
  aLineLayout.BeginLineReflow(x, aState.mY,
 
3420
                              availWidth, availHeight,
 
3421
                              impactedByFloats,
 
3422
                              PR_FALSE /*XXX isTopOfPage*/);
 
3423
 
 
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);
 
3429
  }
 
3430
 
 
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;
 
3434
 
 
3435
  // Reflow the frames that are already on the line first
 
3436
  nsresult rv = NS_OK;
 
3437
  PRUint8 lineReflowStatus = LINE_REFLOW_OK;
 
3438
  PRInt32 i;
 
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,
 
3445
                           &lineReflowStatus);
 
3446
    if (NS_FAILED(rv)) {
 
3447
      return rv;
 
3448
    }
 
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.
 
3453
      ++aLine;
 
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);
 
3461
      }
 
3462
      --aLine;
 
3463
 
 
3464
      if (LINE_REFLOW_TRUNCATED == lineReflowStatus) {
 
3465
        // Push the line with the truncated float 
 
3466
        PushTruncatedPlaceholderLine(aState, aLine, lastPlaceholder, *aKeepReflowGoing);
 
3467
      }
 
3468
      break;
 
3469
    }
 
3470
    frame = frame->GetNextSibling();
 
3471
  }
 
3472
 
 
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)) {
 
3477
      return rv;
 
3478
    }
 
3479
    if (nsnull == frame) {
 
3480
      break;
 
3481
    }
 
3482
    while (LINE_REFLOW_OK == lineReflowStatus) {
 
3483
      PRInt32 oldCount = aLine->GetChildCount();
 
3484
      rv = ReflowInlineFrame(aState, aLineLayout, aLine, frame,
 
3485
                             &lineReflowStatus);
 
3486
      if (NS_FAILED(rv)) {
 
3487
        return rv;
 
3488
      }
 
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();
 
3495
      }
 
3496
      else {
 
3497
        break;
 
3498
      }
 
3499
    }
 
3500
  }
 
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
 
3504
    // the floats.
 
3505
    //
 
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");
 
3512
 
 
3513
    aState.mY += aState.mAvailSpaceRect.height;
 
3514
 
 
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();
 
3520
 
 
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
 
3524
    // past the float.
 
3525
  }
 
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
 
3532
      }
 
3533
    }
 
3534
  }
 
3535
  *aLineReflowStatus = lineReflowStatus;
 
3536
 
 
3537
  return rv;
 
3538
}
 
3539
 
 
3540
/**
 
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.
 
3547
 */
 
3548
nsresult
 
3549
nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
 
3550
                                nsLineLayout& aLineLayout,
 
3551
                                line_iterator aLine,
 
3552
                                nsIFrame* aFrame,
 
3553
                                PRUint8* aLineReflowStatus)
 
3554
{
 
3555
 NS_ENSURE_ARG_POINTER(aFrame);
 
3556
  
 
3557
  *aLineReflowStatus = LINE_REFLOW_OK;
 
3558
 
 
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
 
3563
  ListTag(stdout);
 
3564
  printf(": reflowing ");
 
3565
  nsFrame::ListTag(stdout, aFrame);
 
3566
  printf(" reflowingFirstLetter=%s\n", reflowingFirstLetter ? "on" : "off");
 
3567
#endif
 
3568
 
 
3569
  // Remember if we have a percentage aware child on this line
 
3570
  if (IsPercentageAwareChild(aFrame)) {
 
3571
    aLine->SetHasPercentageChild(PR_TRUE);
 
3572
  }
 
3573
 
 
3574
  // Reflow the inline frame
 
3575
  nsReflowStatus frameReflowStatus;
 
3576
  PRBool         pushedFrame;
 
3577
  nsresult rv = aLineLayout.ReflowFrame(aFrame, frameReflowStatus,
 
3578
                                        nsnull, pushedFrame);
 
3579
 
 
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
 
3585
  // again, someday.
 
3586
  if (aState.mReflowState.path)
 
3587
    aState.mReflowState.path->RemoveChild(aFrame);
 
3588
 
 
3589
  if (NS_FAILED(rv)) {
 
3590
    return rv;
 
3591
  }
 
3592
#ifdef REALLY_NOISY_REFLOW_CHILD
 
3593
  nsFrame::ListTag(stdout, aFrame);
 
3594
  printf(": status=%x\n", frameReflowStatus);
 
3595
#endif
 
3596
 
 
3597
#if defined(REFLOW_STATUS_COVERAGE)
 
3598
  RecordReflowStatus(PR_FALSE, frameReflowStatus);
 
3599
#endif
 
3600
 
 
3601
  // Send post-reflow notification
 
3602
  aState.mPrevChild = aFrame;
 
3603
 
 
3604
   /* XXX
 
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.
 
3608
      see bug 22496
 
3609
   */
 
3610
 
 
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;
 
3622
 
 
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");
 
3628
 
 
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;
 
3637
      }
 
3638
      else {
 
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)) {
 
3643
          return rv;
 
3644
        }
 
3645
 
 
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
 
3649
        if (pushedFrame) {
 
3650
          aLine->SetLineWrapped(PR_TRUE);
 
3651
        }
 
3652
      }
 
3653
    }
 
3654
    else {
 
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;
 
3661
      }
 
3662
      // Break-after cases
 
3663
      if (breakType == NS_STYLE_CLEAR_LINE) {
 
3664
        if (!aLineLayout.GetLineEndsInBR()) {
 
3665
          breakType = NS_STYLE_CLEAR_NONE;
 
3666
        }
 
3667
      }
 
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);
 
3674
        if (NS_FAILED(rv)) 
 
3675
          return rv;
 
3676
        // Remember that the line has wrapped
 
3677
        aLine->SetLineWrapped(PR_TRUE);
 
3678
      }
 
3679
 
 
3680
      // Split line, but after the frame just reflowed
 
3681
      rv = SplitLine(aState, aLineLayout, aLine, aFrame->GetNextSibling());
 
3682
      if (NS_FAILED(rv)) {
 
3683
        return rv;
 
3684
      }
 
3685
 
 
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()) {
 
3691
          next->MarkDirty();
 
3692
        }
 
3693
      }
 
3694
    }
 
3695
  }
 
3696
  else if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus)) {
 
3697
    // Frame is not-complete, no special breaking status
 
3698
 
 
3699
    nsIAtom* frameType = aFrame->GetType();
 
3700
 
 
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);
 
3707
    if (NS_FAILED(rv)) 
 
3708
      return rv;
 
3709
 
 
3710
    // Remember that the line has wrapped
 
3711
    aLine->SetLineWrapped(PR_TRUE);
 
3712
    
 
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;
 
3721
      }
 
3722
    }
 
3723
 
 
3724
    if (splitLine) {
 
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)) {
 
3729
        return rv;
 
3730
      }
 
3731
 
 
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()) {
 
3736
        next->MarkDirty();
 
3737
      }
 
3738
    }
 
3739
  }
 
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;
 
3745
    }
 
3746
  }  
 
3747
 
 
3748
  return NS_OK;
 
3749
}
 
3750
 
 
3751
/**
 
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.
 
3754
 */
 
3755
nsresult
 
3756
nsBlockFrame::CreateContinuationFor(nsBlockReflowState& aState,
 
3757
                                    nsLineBox*          aLine,
 
3758
                                    nsIFrame*           aFrame,
 
3759
                                    PRBool&             aMadeNewFrame)
 
3760
{
 
3761
  aMadeNewFrame = PR_FALSE;
 
3762
  nsresult rv;
 
3763
  nsIFrame* nextInFlow;
 
3764
  rv = CreateNextInFlow(aState.mPresContext, this, aFrame, nextInFlow);
 
3765
  if (NS_FAILED(rv)) {
 
3766
    return rv;
 
3767
  }
 
3768
  if (nsnull != nextInFlow) {
 
3769
    aMadeNewFrame = PR_TRUE;
 
3770
    if (aLine) { 
 
3771
      aLine->SetChildCount(aLine->GetChildCount() + 1);
 
3772
    }
 
3773
  }
 
3774
#ifdef DEBUG
 
3775
  VerifyLines(PR_FALSE);
 
3776
#endif
 
3777
  return rv;
 
3778
}
 
3779
 
 
3780
nsresult
 
3781
nsBlockFrame::SplitPlaceholder(nsIPresContext& aPresContext,
 
3782
                               nsIFrame&       aPlaceholder)
 
3783
{
 
3784
  nsIFrame* nextInFlow;
 
3785
  nsresult rv = CreateNextInFlow(&aPresContext, this, &aPlaceholder, nextInFlow);
 
3786
  if (NS_FAILED(rv)) 
 
3787
    return rv;
 
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);
 
3797
  }
 
3798
  else {
 
3799
    overflowPlace = new nsFrameList(contFrame);
 
3800
    if (overflowPlace) {
 
3801
      SetOverflowPlaceholders(&aPresContext, overflowPlace);
 
3802
    }
 
3803
    else return NS_ERROR_NULL_POINTER;
 
3804
  }
 
3805
  return NS_OK;
 
3806
}
 
3807
 
 
3808
nsresult
 
3809
nsBlockFrame::SplitLine(nsBlockReflowState& aState,
 
3810
                        nsLineLayout& aLineLayout,
 
3811
                        line_iterator aLine,
 
3812
                        nsIFrame* aFrame)
 
3813
{
 
3814
  NS_ABORT_IF_FALSE(aLine->IsInline(), "illegal SplitLine on block line");
 
3815
 
 
3816
  PRInt32 pushCount = aLine->GetChildCount() - aLineLayout.GetCurrentSpanCount();
 
3817
  NS_ABORT_IF_FALSE(pushCount >= 0, "bad push count"); 
 
3818
 
 
3819
#ifdef DEBUG
 
3820
  if (gNoisyReflow) {
 
3821
    nsFrame::IndentBy(stdout, gNoiseIndent);
 
3822
    printf("split line: from line=%p pushCount=%d aFrame=",
 
3823
           NS_STATIC_CAST(void*, aLine.get()), pushCount);
 
3824
    if (aFrame) {
 
3825
      nsFrame::ListTag(stdout, aFrame);
 
3826
    }
 
3827
    else {
 
3828
      printf("(null)");
 
3829
    }
 
3830
    printf("\n");
 
3831
    if (gReallyNoisyReflow) {
 
3832
      aLine->List(aState.mPresContext, stdout, gNoiseIndent+1);
 
3833
    }
 
3834
  }
 
3835
#endif
 
3836
 
 
3837
  if (0 != pushCount) {
 
3838
    NS_ABORT_IF_FALSE(aLine->GetChildCount() > pushCount, "bad push");
 
3839
    NS_ABORT_IF_FALSE(nsnull != aFrame, "whoops");
 
3840
 
 
3841
    // Put frames being split out into their own line
 
3842
    nsLineBox* newLine = aState.NewLineBox(aFrame, pushCount, PR_FALSE);
 
3843
    if (!newLine) {
 
3844
      return NS_ERROR_OUT_OF_MEMORY;
 
3845
    }
 
3846
    mLines.after_insert(aLine, newLine);
 
3847
    aLine->SetChildCount(aLine->GetChildCount() - pushCount);
 
3848
#ifdef DEBUG
 
3849
    if (gReallyNoisyReflow) {
 
3850
      newLine->List(aState.mPresContext, stdout, gNoiseIndent+1);
 
3851
    }
 
3852
#endif
 
3853
 
 
3854
    // Let line layout know that some frames are no longer part of its
 
3855
    // state.
 
3856
    aLineLayout.SplitLineTo(aLine->GetChildCount());
 
3857
#ifdef DEBUG
 
3858
    VerifyLines(PR_TRUE);
 
3859
#endif
 
3860
  }
 
3861
  return NS_OK;
 
3862
}
 
3863
 
 
3864
PRBool
 
3865
nsBlockFrame::ShouldJustifyLine(nsBlockReflowState& aState,
 
3866
                                line_iterator aLine)
 
3867
{
 
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();
 
3875
    }
 
3876
    // The next line is empty, try the next one
 
3877
  }
 
3878
 
 
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();
 
3885
         line != line_end;
 
3886
         ++line)
 
3887
    {
 
3888
      if (0 != line->GetChildCount())
 
3889
        return !line->IsBlock();
 
3890
    }
 
3891
    nextInFlow = (nsBlockFrame*) nextInFlow->mNextInFlow;
 
3892
  }
 
3893
 
 
3894
  // This is the last line - so don't allow justification
 
3895
  return PR_FALSE;
 
3896
}
 
3897
 
 
3898
PRBool
 
3899
nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
 
3900
                        nsLineLayout&       aLineLayout,
 
3901
                        line_iterator       aLine,
 
3902
                        PRBool*             aKeepReflowGoing,
 
3903
                        PRBool              aUpdateMaximumWidth)
 
3904
{
 
3905
  // Trim extra white-space from the line before placing the frames
 
3906
  aLineLayout.TrimTrailingWhiteSpace();
 
3907
 
 
3908
  // Vertically align the frames on this line.
 
3909
  //
 
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
 
3912
  // first line box.
 
3913
  //
 
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>... ).
 
3918
  //
 
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;
 
3930
  }
 
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();
 
3937
  }
 
3938
 
 
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();
 
3947
    }
 
3948
  }
 
3949
#ifdef DEBUG
 
3950
  {
 
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);
 
3959
      }
 
3960
    }
 
3961
    else {
 
3962
      lastHeight = 0;
 
3963
    }
 
3964
  }
 
3965
#endif
 
3966
 
 
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?
 
3980
  if (!successful) {
 
3981
    // Mark the line dirty and then later once we've determined the width
 
3982
    // we can do the horizontal alignment
 
3983
    aLine->MarkDirty();
 
3984
    aState.SetFlag(BRS_NEEDRESIZEREFLOW, PR_TRUE);
 
3985
  }
 
3986
#ifdef IBMBIDI
 
3987
  // XXXldb Why don't we do this earlier?
 
3988
  else {
 
3989
    PRBool bidiEnabled;
 
3990
    aState.mPresContext->GetBidiEnabled(&bidiEnabled);
 
3991
 
 
3992
    if (bidiEnabled) {
 
3993
      if (!aState.mPresContext->IsVisualMode()) {
 
3994
        nsBidiPresUtils* bidiUtils;
 
3995
        aState.mPresContext->GetBidiUtils(&bidiUtils);
 
3996
 
 
3997
        if (bidiUtils && bidiUtils->IsSuccessful() ) {
 
3998
          nsIFrame* nextInFlow = (aLine.next() != end_lines())
 
3999
                                 ? aLine.next()->mFirstChild : nsnull;
 
4000
 
 
4001
          bidiUtils->ReorderFrames(aState.mPresContext,
 
4002
                                   aState.mReflowState.rendContext,
 
4003
                                   aLine->mFirstChild, nextInFlow,
 
4004
                                   aLine->GetChildCount() );
 
4005
        } // bidiUtils
 
4006
      } // not visual mode
 
4007
    } // bidi enabled
 
4008
  } // successful
 
4009
#endif // IBMBIDI
 
4010
 
 
4011
  nsRect combinedArea;
 
4012
  aLineLayout.RelativePositionFrames(combinedArea);  // XXXldb This returned width as -15, 2001-06-12, Bugzilla
 
4013
  aLine->SetCombinedArea(combinedArea);
 
4014
  if (addedBullet) {
 
4015
    aLineLayout.RemoveBulletFrame(mBullet);
 
4016
  }
 
4017
 
 
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.
 
4024
  nscoord newY;
 
4025
 
 
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();
 
4031
  }
 
4032
  else {
 
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) {
 
4045
      mAscent += dy;
 
4046
    }
 
4047
  }
 
4048
 
 
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());
 
4058
 
 
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;
 
4066
    }
 
4067
    return PR_TRUE;
 
4068
  }
 
4069
 
 
4070
  aState.mY = newY;
 
4071
  
 
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;
 
4083
#ifdef DEBUG
 
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()),
 
4088
                maxElementWidth);
 
4089
      }
 
4090
#endif
 
4091
    }
 
4092
 
 
4093
  } else {
 
4094
    PostPlaceLine(aState, aLine, maxElementWidth);
 
4095
  }
 
4096
 
 
4097
  // Add the already placed current-line floats to the line
 
4098
  aLine->AppendFloats(aState.mCurrentLineFloats);
 
4099
 
 
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);
 
4109
    }
 
4110
    else { 
 
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);
 
4115
    }
 
4116
  }
 
4117
 
 
4118
  // When a line has floats, factor them into the combined-area
 
4119
  // computations.
 
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
 
4125
    ListTag(stdout);
 
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);
 
4132
#endif
 
4133
    lineCombinedArea.UnionRect(aState.mFloatCombinedArea, lineCombinedArea);
 
4134
 
 
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);
 
4140
#endif
 
4141
  }
 
4142
 
 
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);
 
4151
    break;
 
4152
  }
 
4153
 
 
4154
  return PR_FALSE;
 
4155
}
 
4156
 
 
4157
void
 
4158
nsBlockFrame::PostPlaceLine(nsBlockReflowState& aState,
 
4159
                            nsLineBox* aLine,
 
4160
                            nscoord aMaxElementWidth)
 
4161
{
 
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;
 
4168
#ifdef DEBUG
 
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),
 
4173
              aMaxElementWidth);
 
4174
    }
 
4175
#endif
 
4176
  }
 
4177
 
 
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());
 
4186
#endif
 
4187
    aLine->mMaximumWidth = aLine->mBounds.XMost();
 
4188
  }
 
4189
 
 
4190
  // Update xmost
 
4191
  nscoord xmost = aLine->mBounds.XMost();
 
4192
 
 
4193
#ifdef DEBUG
 
4194
  if (CRAZY_WIDTH(xmost)) {
 
4195
    ListTag(stdout);
 
4196
    printf(": line=%p xmost=%d\n", NS_STATIC_CAST(void*, aLine), xmost);
 
4197
  }
 
4198
#endif
 
4199
  if (xmost > aState.mKidXMost) {
 
4200
    aState.mKidXMost = xmost;
 
4201
#ifdef NOISY_KIDXMOST
 
4202
    printf("%p PostPlaceLine aState.mKidXMost=%d\n", this, aState.mKidXMost); 
 
4203
#endif
 
4204
  }
 
4205
}
 
4206
 
 
4207
void
 
4208
nsBlockFrame::PushLines(nsBlockReflowState&  aState,
 
4209
                        nsLineList::iterator aLineBefore)
 
4210
{
 
4211
  nsLineList::iterator overBegin(aLineBefore.next());
 
4212
 
 
4213
  // PushTruncatedPlaceholderLine sometimes pushes the first line.  Ugh.
 
4214
  PRBool firstLine = overBegin == begin_lines();
 
4215
 
 
4216
  if (overBegin != end_lines()) {
 
4217
    // XXXldb use presshell arena!
 
4218
    nsLineList* overflowLines = new nsLineList();
 
4219
    overflowLines->splice(overflowLines->end(), mLines, overBegin,
 
4220
                          end_lines());
 
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);
 
4225
  
 
4226
    // Mark all the overflow lines dirty so that they get reflowed when
 
4227
    // they are pulled up by our next-in-flow.
 
4228
 
 
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();
 
4232
         line != line_end;
 
4233
         ++line)
 
4234
    {
 
4235
      line->MarkDirty();
 
4236
    }
 
4237
  }
 
4238
 
 
4239
  // Break frame sibling list
 
4240
  if (!firstLine)
 
4241
    aLineBefore->LastChild()->SetNextSibling(nsnull);
 
4242
 
 
4243
#ifdef DEBUG
 
4244
  VerifyOverflowSituation(aState.mPresContext);
 
4245
#endif
 
4246
}
 
4247
 
 
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.
 
4251
 
 
4252
PRBool
 
4253
nsBlockFrame::DrainOverflowLines(nsIPresContext* aPresContext)
 
4254
{
 
4255
#ifdef DEBUG
 
4256
  VerifyOverflowSituation(aPresContext);
 
4257
#endif
 
4258
  PRBool drained = PR_FALSE;
 
4259
  nsLineList* overflowLines;
 
4260
 
 
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");
 
4268
      drained = PR_TRUE;
 
4269
 
 
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);
 
4275
 
 
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);
 
4279
 
 
4280
        // Get the next frame
 
4281
        lastFrame = frame;
 
4282
        frame = frame->GetNextSibling();
 
4283
      }
 
4284
 
 
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);
 
4290
      }
 
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;
 
4295
 
 
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()) {
 
4301
          f->SetParent(this);
 
4302
 
 
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);
 
4306
        }
 
4307
        delete overflowOutOfFlows;
 
4308
      }
 
4309
    }
 
4310
  }
 
4311
 
 
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
 
4319
    // and reflows.
 
4320
 
 
4321
    if (! mLines.empty()) {
 
4322
      mLines.back()->LastChild()->SetNextSibling(
 
4323
          overflowLines->front()->mFirstChild );
 
4324
    }
 
4325
    // append the overflow to mLines
 
4326
    mLines.splice(mLines.end(), *overflowLines);
 
4327
    drained = PR_TRUE;
 
4328
    delete overflowLines;
 
4329
 
 
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
 
4334
    // property.
 
4335
    nsFrameList* overflowOutOfFlows = GetOverflowOutOfFlows(PR_TRUE);
 
4336
    delete overflowOutOfFlows;
 
4337
  }
 
4338
  return drained;
 
4339
}
 
4340
 
 
4341
nsLineList*
 
4342
nsBlockFrame::GetOverflowLines(nsIPresContext* aPresContext,
 
4343
                               PRBool          aRemoveProperty) const
 
4344
{
 
4345
  nsLineList* lines = 
 
4346
    NS_STATIC_CAST(nsLineList*, GetProperty(aPresContext, 
 
4347
                                            nsLayoutAtoms::overflowLinesProperty, 
 
4348
                                            aRemoveProperty));
 
4349
  NS_ASSERTION(!lines || !lines->empty(), "value should never be stored as empty");
 
4350
  return lines;
 
4351
}
 
4352
 
 
4353
// Destructor function for the overflowLines frame property
 
4354
static void
 
4355
DestroyOverflowLines(nsIPresContext* aPresContext,
 
4356
                     nsIFrame*       aFrame,
 
4357
                     nsIAtom*        aPropertyName,
 
4358
                     void*           aPropertyValue)
 
4359
{
 
4360
  if (aPropertyValue) {
 
4361
    nsLineList* lines = NS_STATIC_CAST(nsLineList*, aPropertyValue);
 
4362
    nsLineBox::DeleteLineList(aPresContext, *lines);
 
4363
    delete lines;
 
4364
  }
 
4365
}
 
4366
 
 
4367
// This takes ownership of aOverflowLines.
 
4368
// XXX We should allocate overflowLines from presShell arena!
 
4369
nsresult
 
4370
nsBlockFrame::SetOverflowLines(nsIPresContext* aPresContext,
 
4371
                               nsLineList*     aOverflowLines)
 
4372
{
 
4373
  NS_ASSERTION(aOverflowLines, "null lines");
 
4374
  NS_ASSERTION(!aOverflowLines->empty(), "empty lines");
 
4375
 
 
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");
 
4380
  return rv;
 
4381
}
 
4382
 
 
4383
nsFrameList*
 
4384
nsBlockFrame::GetOverflowOutOfFlows(PRBool aRemoveProperty) const
 
4385
{
 
4386
  return NS_STATIC_CAST(nsFrameList*,
 
4387
    GetProperty(GetPresContext(), nsLayoutAtoms::overflowOutOfFlowsProperty, 
 
4388
                aRemoveProperty));
 
4389
}
 
4390
 
 
4391
// Destructor function for the overflowPlaceholders frame property
 
4392
static void
 
4393
DestroyOverflowOOFs(nsIPresContext* aPresContext,
 
4394
                    nsIFrame*       aFrame,
 
4395
                    nsIAtom*        aPropertyName,
 
4396
                    void*           aPropertyValue)
 
4397
{
 
4398
  delete NS_STATIC_CAST(nsFrameList*, aPropertyValue);
 
4399
}
 
4400
 
 
4401
// This takes ownership of aFloaters.
 
4402
nsresult
 
4403
nsBlockFrame::SetOverflowOutOfFlows(nsFrameList* aOOFs)
 
4404
{
 
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");
 
4409
  return rv;
 
4410
}
 
4411
 
 
4412
nsFrameList*
 
4413
nsBlockFrame::GetOverflowPlaceholders(nsIPresContext* aPresContext,
 
4414
                                      PRBool          aRemoveProperty) const
 
4415
{
 
4416
  nsFrameList* placeholders = 
 
4417
    NS_STATIC_CAST(nsFrameList*, 
 
4418
                   GetProperty(aPresContext, nsLayoutAtoms::overflowPlaceholdersProperty, 
 
4419
                               aRemoveProperty));
 
4420
  return placeholders;
 
4421
}
 
4422
 
 
4423
// Destructor function for the overflowPlaceholders frame property
 
4424
static void
 
4425
DestroyOverflowPlaceholders(nsIPresContext* aPresContext,
 
4426
                            nsIFrame*       aFrame,
 
4427
                            nsIAtom*        aPropertyName,
 
4428
                            void*           aPropertyValue)
 
4429
{
 
4430
  nsFrameList* overflowPlace = NS_STATIC_CAST(nsFrameList*, aPropertyValue);
 
4431
  delete overflowPlace;
 
4432
}
 
4433
 
 
4434
// This takes ownership of aOverflowLines.
 
4435
// XXX We should allocate overflowLines from presShell arena!
 
4436
nsresult
 
4437
nsBlockFrame::SetOverflowPlaceholders(nsIPresContext* aPresContext,
 
4438
                                      nsFrameList*    aOverflowPlaceholders)
 
4439
{
 
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");
 
4444
  return rv;
 
4445
}
 
4446
 
 
4447
//////////////////////////////////////////////////////////////////////
 
4448
// Frame list manipulation routines
 
4449
 
 
4450
nsIFrame*
 
4451
nsBlockFrame::LastChild()
 
4452
{
 
4453
  if (! mLines.empty()) {
 
4454
    return mLines.back()->LastChild();
 
4455
  }
 
4456
  return nsnull;
 
4457
}
 
4458
 
 
4459
NS_IMETHODIMP
 
4460
nsBlockFrame::AppendFrames(nsIPresContext* aPresContext,
 
4461
                           nsIPresShell&   aPresShell,
 
4462
                           nsIAtom*        aListName,
 
4463
                           nsIFrame*       aFrameList)
 
4464
{
 
4465
  if (nsnull == aFrameList) {
 
4466
    return NS_OK;
 
4467
  }
 
4468
  if (mAbsoluteContainer.GetChildListName() == aListName) {
 
4469
    return mAbsoluteContainer.AppendFrames(this, aPresContext, aPresShell, aListName,
 
4470
                                           aFrameList);
 
4471
  }
 
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);
 
4476
    return NS_OK;
 
4477
  }
 
4478
  else if (nsnull != aListName) {
 
4479
    return NS_ERROR_INVALID_ARG;
 
4480
  }
 
4481
 
 
4482
  // Find the proper last-child for where the append should go
 
4483
  nsIFrame* lastKid = nsnull;
 
4484
  nsLineBox* lastLine = mLines.empty() ? nsnull : mLines.back();
 
4485
  if (lastLine) {
 
4486
    lastKid = lastLine->LastChild();
 
4487
  }
 
4488
 
 
4489
  // Add frames after the last child
 
4490
#ifdef NOISY_REFLOW_REASON
 
4491
  ListTag(stdout);
 
4492
  printf(": append ");
 
4493
  nsFrame::ListTag(stdout, aFrameList);
 
4494
  if (lastKid) {
 
4495
    printf(" after ");
 
4496
    nsFrame::ListTag(stdout, lastKid);
 
4497
  }
 
4498
  printf("\n");
 
4499
#endif
 
4500
  nsresult rv = AddFrames(aPresContext, aFrameList, lastKid);
 
4501
  if (NS_SUCCEEDED(rv)) {
 
4502
    // Ask the parent frame to reflow me.
 
4503
    ReflowDirtyChild(&aPresShell, nsnull);
 
4504
  }
 
4505
  return rv;
 
4506
}
 
4507
 
 
4508
NS_IMETHODIMP
 
4509
nsBlockFrame::InsertFrames(nsIPresContext* aPresContext,
 
4510
                           nsIPresShell&   aPresShell,
 
4511
                           nsIAtom*        aListName,
 
4512
                           nsIFrame*       aPrevFrame,
 
4513
                           nsIFrame*       aFrameList)
 
4514
{
 
4515
  if (mAbsoluteContainer.GetChildListName() == aListName) {
 
4516
    return mAbsoluteContainer.InsertFrames(this, aPresContext, aPresShell, aListName,
 
4517
                                           aPrevFrame, aFrameList);
 
4518
  }
 
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);
 
4523
    return NS_OK;
 
4524
  }
 
4525
#ifdef IBMBIDI
 
4526
  else if (nsLayoutAtoms::nextBidi == aListName) {}
 
4527
#endif // IBMBIDI
 
4528
  else if (nsnull != aListName) {
 
4529
    return NS_ERROR_INVALID_ARG;
 
4530
  }
 
4531
 
 
4532
#ifdef NOISY_REFLOW_REASON
 
4533
  ListTag(stdout);
 
4534
  printf(": insert ");
 
4535
  nsFrame::ListTag(stdout, aFrameList);
 
4536
  if (aPrevFrame) {
 
4537
    printf(" after ");
 
4538
    nsFrame::ListTag(stdout, aPrevFrame);
 
4539
  }
 
4540
  printf("\n");
 
4541
#endif
 
4542
  nsresult rv = AddFrames(aPresContext, aFrameList, aPrevFrame);
 
4543
#ifdef IBMBIDI
 
4544
  if (aListName != nsLayoutAtoms::nextBidi)
 
4545
#endif // IBMBIDI
 
4546
  if (NS_SUCCEEDED(rv)) {
 
4547
    // Ask the parent frame to reflow me.
 
4548
    ReflowDirtyChild(&aPresShell, nsnull);
 
4549
  }
 
4550
  return rv;
 
4551
}
 
4552
 
 
4553
nsresult
 
4554
nsBlockFrame::AddFrames(nsIPresContext* aPresContext,
 
4555
                        nsIFrame* aFrameList,
 
4556
                        nsIFrame* aPrevSibling)
 
4557
{
 
4558
  // Clear our line cursor, since our lines may change.
 
4559
  ClearLineCursor();
 
4560
 
 
4561
  if (nsnull == aFrameList) {
 
4562
    return NS_OK;
 
4563
  }
 
4564
 
 
4565
  nsIPresShell *presShell = aPresContext->PresShell();
 
4566
 
 
4567
  // Attempt to find the line that contains the previous sibling
 
4568
  nsLineList::iterator prevSibLine = end_lines();
 
4569
  PRInt32 prevSiblingIndex = -1;
 
4570
  if (aPrevSibling) {
 
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.
 
4574
 
 
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();
 
4584
    }
 
4585
  }
 
4586
 
 
4587
  // Find the frame following aPrevSibling so that we can join up the
 
4588
  // two lists of frames.
 
4589
  nsIFrame* prevSiblingNextFrame = nsnull;
 
4590
  if (aPrevSibling) {
 
4591
    prevSiblingNextFrame = aPrevSibling->GetNextSibling();
 
4592
 
 
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;
 
4596
    if (rem) {
 
4597
      // Split the line in two where the frame(s) are being inserted.
 
4598
      nsLineBox* line = NS_NewLineBox(presShell, prevSiblingNextFrame, rem, PR_FALSE);
 
4599
      if (!line) {
 
4600
        return NS_ERROR_OUT_OF_MEMORY;
 
4601
      }
 
4602
      mLines.after_insert(prevSibLine, line);
 
4603
      prevSibLine->SetChildCount(prevSibLine->GetChildCount() - rem);
 
4604
      prevSibLine->MarkDirty();
 
4605
    }
 
4606
 
 
4607
    // Now (partially) join the sibling lists together
 
4608
    aPrevSibling->SetNextSibling(aFrameList);
 
4609
  }
 
4610
  else if (! mLines.empty()) {
 
4611
    prevSiblingNextFrame = mLines.front()->mFirstChild;
 
4612
  }
 
4613
 
 
4614
  // Walk through the new frames being added and update the line data
 
4615
  // structures to fit.
 
4616
  nsIFrame* newFrame = aFrameList;
 
4617
  while (newFrame) {
 
4618
    PRBool isBlock = nsLineLayout::TreatFrameAsBlock(newFrame);
 
4619
 
 
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
 
4625
      // list.
 
4626
      nsLineBox* line = NS_NewLineBox(presShell, newFrame, 1, isBlock);
 
4627
      if (!line) {
 
4628
        return NS_ERROR_OUT_OF_MEMORY;
 
4629
      }
 
4630
      if (prevSibLine != end_lines()) {
 
4631
        // Append new line after prevSibLine
 
4632
        mLines.after_insert(prevSibLine, line);
 
4633
        ++prevSibLine;
 
4634
      }
 
4635
      else {
 
4636
        // New line is going before the other lines
 
4637
        mLines.push_front(line);
 
4638
        prevSibLine = begin_lines();
 
4639
      }
 
4640
    }
 
4641
    else {
 
4642
      prevSibLine->SetChildCount(prevSibLine->GetChildCount() + 1);
 
4643
      prevSibLine->MarkDirty();
 
4644
    }
 
4645
 
 
4646
    aPrevSibling = newFrame;
 
4647
    newFrame = newFrame->GetNextSibling();
 
4648
  }
 
4649
  if (prevSiblingNextFrame) {
 
4650
    // Connect the last new frame to the remainder of the sibling list
 
4651
    aPrevSibling->SetNextSibling(prevSiblingNextFrame);
 
4652
  }
 
4653
 
 
4654
#ifdef DEBUG
 
4655
  VerifyLines(PR_TRUE);
 
4656
#endif
 
4657
  return NS_OK;
 
4658
}
 
4659
 
 
4660
NS_IMETHODIMP
 
4661
nsBlockFrame::RemoveFrame(nsIPresContext* aPresContext,
 
4662
                          nsIPresShell&   aPresShell,
 
4663
                          nsIAtom*        aListName,
 
4664
                          nsIFrame*       aOldFrame)
 
4665
{
 
4666
  nsresult rv = NS_OK;
 
4667
 
 
4668
#ifdef NOISY_REFLOW_REASON
 
4669
    ListTag(stdout);
 
4670
    printf(": remove ");
 
4671
    nsFrame::ListTag(stdout, aOldFrame);
 
4672
    printf("\n");
 
4673
#endif
 
4674
 
 
4675
  if (nsnull == aListName) {
 
4676
    rv = DoRemoveFrame(aPresContext, aOldFrame);
 
4677
  }
 
4678
  else if (mAbsoluteContainer.GetChildListName() == aListName) {
 
4679
    return mAbsoluteContainer.RemoveFrame(this, aPresContext, aPresShell,
 
4680
                                          aListName, aOldFrame);
 
4681
  }
 
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)) {
 
4687
        break;
 
4688
      }
 
4689
    }
 
4690
 
 
4691
    mFloats.DestroyFrame(aPresContext, aOldFrame);
 
4692
 
 
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) {
 
4696
      line->MarkDirty();
 
4697
    }
 
4698
  }
 
4699
#ifdef IBMBIDI
 
4700
  else if (nsLayoutAtoms::nextBidi == aListName) {
 
4701
    // Skip the call to |ReflowDirtyChild| below by returning now.
 
4702
    return DoRemoveFrame(aPresContext, aOldFrame);
 
4703
  }
 
4704
#endif // IBMBIDI
 
4705
  else {
 
4706
    rv = NS_ERROR_INVALID_ARG;
 
4707
  }
 
4708
 
 
4709
  if (NS_SUCCEEDED(rv)) {
 
4710
    // Ask the parent frame to reflow me.
 
4711
    ReflowDirtyChild(&aPresShell, nsnull);  
 
4712
  }
 
4713
  return rv;
 
4714
}
 
4715
 
 
4716
void
 
4717
nsBlockFrame::DoRemoveOutOfFlowFrame(nsIPresContext* aPresContext,
 
4718
                                     nsIFrame*       aFrame)
 
4719
{
 
4720
  // First remove aFrame's next in flow
 
4721
  nsIFrame* nextInFlow;
 
4722
  aFrame->GetNextInFlow(&nextInFlow);
 
4723
  if (nextInFlow) {
 
4724
    nsBlockFrame::DoRemoveOutOfFlowFrame(aPresContext, nextInFlow);
 
4725
  }
 
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();
 
4736
  }
 
4737
  
 
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);
 
4744
  }
 
4745
  else {
 
4746
    block->mFloats.RemoveFrame(aFrame);
 
4747
  }
 
4748
  // Destroy aFrame
 
4749
  aFrame->Destroy(aPresContext);
 
4750
}
 
4751
 
 
4752
nsresult
 
4753
nsBlockFrame::DoRemoveFrame(nsIPresContext* aPresContext,
 
4754
                            nsIFrame* aDeletedFrame)
 
4755
{
 
4756
  // Clear our line cursor, since our lines may change.
 
4757
  ClearLineCursor();
 
4758
        
 
4759
  if (aDeletedFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
 
4760
    DoRemoveOutOfFlowFrame(aPresContext, aDeletedFrame);
 
4761
    return NS_OK;
 
4762
  }
 
4763
 
 
4764
  nsIPresShell *presShell = aPresContext->PresShell();
 
4765
  
 
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();
 
4776
    while (--n >= 0) {
 
4777
      if (frame == aDeletedFrame) {
 
4778
        goto found_frame;
 
4779
      }
 
4780
      prevSibling = frame;
 
4781
      frame = frame->GetNextSibling();
 
4782
    }
 
4783
  }
 
4784
 found_frame:;
 
4785
  if (line == line_end) {
 
4786
    NS_ERROR("can't find deleted frame in lines");
 
4787
    return NS_ERROR_FAILURE;
 
4788
  }
 
4789
  NS_ASSERTION(!prevSibling || prevSibling->GetNextSibling() == aDeletedFrame, "bad prevSibling");
 
4790
 
 
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");
 
4796
 
 
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;
 
4801
      }
 
4802
      else if (line->LastChild() == aDeletedFrame) {
 
4803
        isLastFrameOnLine = PR_TRUE;
 
4804
      }
 
4805
 
 
4806
      // Remove aDeletedFrame from the line
 
4807
      nsIFrame* nextFrame = aDeletedFrame->GetNextSibling();
 
4808
      if (line->mFirstChild == aDeletedFrame) {
 
4809
        line->mFirstChild = nextFrame;
 
4810
      }
 
4811
 
 
4812
      --line;
 
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.
 
4816
        line->MarkDirty();
 
4817
      }
 
4818
      ++line;
 
4819
 
 
4820
      // Take aDeletedFrame out of the sibling list. Note that
 
4821
      // prevSibling will only be nsnull when we are deleting the very
 
4822
      // first frame.
 
4823
      if (prevSibling) {
 
4824
        prevSibling->SetNextSibling(nextFrame);
 
4825
      }
 
4826
 
 
4827
      // Update the child count of the line to be accurate
 
4828
      PRInt32 lineChildCount = line->GetChildCount();
 
4829
      lineChildCount--;
 
4830
      line->SetChildCount(lineChildCount);
 
4831
 
 
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);
 
4840
#endif
 
4841
      aDeletedFrame->Destroy(aPresContext);
 
4842
      aDeletedFrame = nextInFlow;
 
4843
 
 
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
 
4851
        // cases...
 
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);
 
4856
#endif
 
4857
        Invalidate(lineCombinedArea);
 
4858
        cur->Destroy(presShell);
 
4859
 
 
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
 
4863
        // there is one.
 
4864
        if (line != line_end)
 
4865
          line->MarkPreviousMarginDirty();
 
4866
      }
 
4867
      else {
 
4868
        // Make the line that just lost a frame dirty
 
4869
        line->MarkDirty();
 
4870
 
 
4871
        // If we just removed the last frame on the line then we need
 
4872
        // to advance to the next line.
 
4873
        if (isLastFrameOnLine) {
 
4874
          ++line;
 
4875
        }
 
4876
      }
 
4877
 
 
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");
 
4886
          break;
 
4887
        }
 
4888
      }
 
4889
    }
 
4890
 
 
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
 
4896
      if (flow) {
 
4897
        lines = flow->mLines;
 
4898
        line = lines.begin();
 
4899
        line_end = lines.end();
 
4900
        prevSibling = nsnull;
 
4901
      } else {
 
4902
        aDeletedFrame = nsnull;
 
4903
      }
 
4904
    }
 
4905
  }
 
4906
 
 
4907
#ifdef DEBUG
 
4908
  VerifyLines(PR_TRUE);
 
4909
#endif
 
4910
  return NS_OK;
 
4911
}
 
4912
 
 
4913
void
 
4914
nsBlockFrame::DeleteNextInFlowChild(nsIPresContext* aPresContext,
 
4915
                                    nsIFrame*       aNextInFlow)
 
4916
{
 
4917
  nsIFrame* prevInFlow;
 
4918
  aNextInFlow->GetPrevInFlow(&prevInFlow);
 
4919
  NS_PRECONDITION(prevInFlow, "bad next-in-flow");
 
4920
  NS_PRECONDITION(IsChild(aPresContext, aNextInFlow), "bad geometric parent");
 
4921
 
 
4922
#ifdef IBMBIDI
 
4923
  nsIFrame* nextBidi;
 
4924
  prevInFlow->GetBidiProperty(aPresContext, nsLayoutAtoms::nextBidi,
 
4925
                              (void**) &nextBidi,sizeof(nextBidi));
 
4926
  if (nextBidi != aNextInFlow) {
 
4927
#endif // IBMBIDI
 
4928
  DoRemoveFrame(aPresContext, aNextInFlow);
 
4929
#ifdef IBMBIDI
 
4930
  }
 
4931
#endif // IBMBIDI
 
4932
}
 
4933
 
 
4934
////////////////////////////////////////////////////////////////////////
 
4935
// Float support
 
4936
 
 
4937
nsresult
 
4938
nsBlockFrame::ReflowFloat(nsBlockReflowState& aState,
 
4939
                          nsPlaceholderFrame* aPlaceholder,
 
4940
                          nsFloatCache*     aFloatCache,
 
4941
                          nsReflowStatus&     aReflowStatus)
 
4942
{
 
4943
  // Delete the placeholder's next in flows, if any
 
4944
  nsIFrame* nextInFlow;
 
4945
  aPlaceholder->GetNextInFlow(&nextInFlow);
 
4946
  if (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);
 
4950
  }
 
4951
  // Reflow the float.
 
4952
  nsIFrame* floatFrame = aPlaceholder->GetOutOfFlowFrame();
 
4953
  aReflowStatus = NS_FRAME_COMPLETE;
 
4954
 
 
4955
#ifdef NOISY_FLOAT
 
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
 
4960
  );
 
4961
#endif
 
4962
 
 
4963
  // Compute the available width. By default, assume the width of the
 
4964
  // containing block.
 
4965
  nscoord availWidth;
 
4966
  if (aState.GetFlag(BRS_UNCONSTRAINEDWIDTH)) {
 
4967
    availWidth = NS_UNCONSTRAINEDSIZE;
 
4968
  }
 
4969
  else {
 
4970
    const nsStyleDisplay* floatDisplay = floatFrame->GetStyleDisplay();
 
4971
 
 
4972
    nsIFrame* prevInFlow;
 
4973
    floatFrame->GetPrevInFlow(&prevInFlow);
 
4974
    // if the float is continued, constrain its width to the prev-in-flow's width
 
4975
    if (prevInFlow) {
 
4976
      availWidth = prevInFlow->GetRect().width;
 
4977
    }
 
4978
    else if (NS_STYLE_DISPLAY_TABLE != floatDisplay->mDisplay ||
 
4979
             eCompatibility_NavQuirks != aState.mPresContext->CompatibilityMode() ) {
 
4980
      availWidth = aState.mContentArea.width;
 
4981
    }
 
4982
    else {
 
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)
 
4992
      nscoord twp;
 
4993
      float p2t;
 
4994
      aState.mPresContext->GetScaledPixelsToTwips(&p2t);
 
4995
      twp = NSIntPixelsToTwips(1,p2t);
 
4996
      availWidth -=  availWidth % twp;
 
4997
    }
 
4998
  }
 
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);
 
5003
 
 
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());
 
5008
 
 
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);
 
5013
 
 
5014
  nsRect availSpace(aState.BorderPadding().left,
 
5015
                    aState.BorderPadding().top,
 
5016
                    availWidth, availHeight);
 
5017
 
 
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,
 
5021
                            floatFrame, 
 
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));
 
5028
 
 
5029
  // Reflow the float
 
5030
  PRBool isAdjacentWithTop = aState.IsAdjacentWithTop();
 
5031
 
 
5032
  nsCollapsingMargin margin;
 
5033
  nsresult rv = brc.ReflowBlock(availSpace, PR_TRUE, margin, isAdjacentWithTop, 
 
5034
                                aFloatCache->mOffsets, floatRS,
 
5035
                                aReflowStatus);
 
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;
 
5040
 
 
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
 
5046
      // maxElementWidth.
 
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,
 
5052
                                    floatFrame, 
 
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,
 
5057
                           aReflowStatus);
 
5058
    }
 
5059
  }
 
5060
 
 
5061
  // Remove the float from the reflow tree.
 
5062
  if (aState.mReflowState.path)
 
5063
    aState.mReflowState.path->RemoveChild(floatFrame);
 
5064
 
 
5065
  if (NS_FAILED(rv)) {
 
5066
    return rv;
 
5067
  }
 
5068
 
 
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;
 
5076
 
 
5077
  const nsHTMLReflowMetrics& metrics = brc.GetMetrics();
 
5078
  aFloatCache->mCombinedArea = metrics.mOverflowArea;
 
5079
 
 
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);
 
5092
  }
 
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);
 
5096
 
 
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;
 
5101
 
 
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
 
5106
 
 
5107
    // Allow the float width to be restored in state recovery.
 
5108
    aFloatCache->mMaxElementWidth = mew;
 
5109
  }
 
5110
#ifdef NOISY_FLOAT
 
5111
  printf("end ReflowFloat %p, sized to %d,%d\n",
 
5112
         floatFrame, metrics.width, metrics.height);
 
5113
#endif
 
5114
 
 
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();
 
5124
    if (next) {
 
5125
      if (nsLayoutAtoms::placeholderFrame == next->GetType()) {
 
5126
        lastPlaceholder = PR_FALSE;
 
5127
      }
 
5128
    }
 
5129
    if (lastPlaceholder) {
 
5130
      // get the containing block of prevPlaceholder which is our prev-in-flow
 
5131
      if (mPrevInFlow) {
 
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;
 
5139
        }
 
5140
      }
 
5141
      else NS_ASSERTION(PR_FALSE, "no prev in flow");
 
5142
    }
 
5143
  }
 
5144
  return NS_OK;
 
5145
}
 
5146
 
 
5147
//////////////////////////////////////////////////////////////////////
 
5148
// Painting, event handling
 
5149
 
 
5150
PRIntn
 
5151
nsBlockFrame::GetSkipSides() const
 
5152
{
 
5153
  PRIntn skip = 0;
 
5154
  if (nsnull != mPrevInFlow) {
 
5155
    skip |= 1 << NS_SIDE_TOP;
 
5156
  }
 
5157
  if (nsnull != mNextInFlow) {
 
5158
    skip |= 1 << NS_SIDE_BOTTOM;
 
5159
  }
 
5160
  return skip;
 
5161
}
 
5162
 
 
5163
#ifdef DEBUG
 
5164
static void ComputeCombinedArea(nsLineList& aLines,
 
5165
                                nscoord aWidth, nscoord aHeight,
 
5166
                                nsRect& aResult)
 
5167
{
 
5168
  nscoord xa = 0, ya = 0, xb = aWidth, yb = aHeight;
 
5169
  for (nsLineList::iterator line = aLines.begin(), line_end = aLines.end();
 
5170
       line != line_end;
 
5171
       ++line) {
 
5172
    // Compute min and max x/y values for the reflowed frame's
 
5173
    // combined areas
 
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;
 
5179
    if (x < xa) {
 
5180
      xa = x;
 
5181
    }
 
5182
    if (xmost > xb) {
 
5183
      xb = xmost;
 
5184
    }
 
5185
    if (y < ya) {
 
5186
      ya = y;
 
5187
    }
 
5188
    if (ymost > yb) {
 
5189
      yb = ymost;
 
5190
    }
 
5191
  }
 
5192
 
 
5193
  aResult.x = xa;
 
5194
  aResult.y = ya;
 
5195
  aResult.width = xb - xa;
 
5196
  aResult.height = yb - ya;
 
5197
}
 
5198
#endif
 
5199
 
 
5200
NS_IMETHODIMP
 
5201
nsBlockFrame::IsVisibleForPainting(nsIPresContext *     aPresContext, 
 
5202
                                   nsIRenderingContext& aRenderingContext,
 
5203
                                   PRBool               aCheckVis,
 
5204
                                   PRBool*              aIsVisible)
 
5205
{
 
5206
  // first check to see if we are visible
 
5207
  if (aCheckVis) {
 
5208
    if (!GetStyleVisibility()->IsVisible()) {
 
5209
      *aIsVisible = PR_FALSE;
 
5210
      return NS_OK;
 
5211
    }
 
5212
  }
 
5213
 
 
5214
  // Start by assuming we are visible and need to be painted
 
5215
  *aIsVisible = PR_TRUE;
 
5216
 
 
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));
 
5224
 
 
5225
    nsCOMPtr<nsIDOMHTMLHtmlElement> html(do_QueryInterface(mContent));
 
5226
    nsCOMPtr<nsIDOMHTMLBodyElement> body(do_QueryInterface(mContent));
 
5227
 
 
5228
    if (!html && !body) {
 
5229
      rv = selection->ContainsNode(node, PR_TRUE, aIsVisible);
 
5230
    }
 
5231
  }
 
5232
 
 
5233
  return rv;
 
5234
}
 
5235
 
 
5236
/* virtual */ void
 
5237
nsBlockFrame::PaintTextDecorationLines(nsIRenderingContext& aRenderingContext, 
 
5238
                                       nscolor aColor, 
 
5239
                                       nscoord aOffset, 
 
5240
                                       nscoord aAscent, 
 
5241
                                       nscoord aSize) 
 
5242
{
 
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);
 
5250
    }
 
5251
  }
 
5252
}
 
5253
 
 
5254
NS_IMETHODIMP
 
5255
nsBlockFrame::Paint(nsIPresContext*      aPresContext,
 
5256
                    nsIRenderingContext& aRenderingContext,
 
5257
                    const nsRect&        aDirtyRect,
 
5258
                    nsFramePaintLayer    aWhichLayer,
 
5259
                    PRUint32             aFlags)
 
5260
{
 
5261
  if (NS_FRAME_IS_UNFLOWABLE & mState) {
 
5262
    return NS_OK;
 
5263
  }
 
5264
 
 
5265
#ifdef DEBUG
 
5266
  if (gNoisyDamageRepair) {
 
5267
    if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) {
 
5268
      PRInt32 depth = GetDepth();
 
5269
      nsRect ca;
 
5270
      ::ComputeCombinedArea(mLines, mRect.width, mRect.height, ca);
 
5271
      nsFrame::IndentBy(stdout, depth);
 
5272
      ListTag(stdout);
 
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);
 
5277
    }
 
5278
  }
 
5279
#endif  
 
5280
 
 
5281
  if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) {
 
5282
    PaintSelf(aPresContext, aRenderingContext, aDirtyRect);
 
5283
  }
 
5284
 
 
5285
  PRBool paintingSuppressed = PR_FALSE;  
 
5286
  aPresContext->PresShell()->IsPaintingSuppressed(&paintingSuppressed);
 
5287
  if (paintingSuppressed)
 
5288
    return NS_OK;
 
5289
 
 
5290
  const nsStyleDisplay* disp = GetStyleDisplay();
 
5291
 
 
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);
 
5298
  }
 
5299
 
 
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);
 
5304
  }
 
5305
 
 
5306
  PaintDecorationsAndChildren(aPresContext, aRenderingContext,
 
5307
                              aDirtyRect, aWhichLayer, PR_TRUE);
 
5308
 
 
5309
  if (NS_STYLE_OVERFLOW_HIDDEN == disp->mOverflow) {
 
5310
    PRBool clipState;
 
5311
    aRenderingContext.PopState(clipState);
 
5312
  }
 
5313
 
 
5314
#if 0
 
5315
  if ((NS_FRAME_PAINT_LAYER_DEBUG == aWhichLayer) && GetShowFrameBorders()) {
 
5316
    // Render the bands in the spacemanager
 
5317
    nsSpaceManager* sm = mSpaceManager;
 
5318
 
 
5319
    if (nsnull != sm) {
 
5320
      nsBlockBandData band;
 
5321
      band.Init(sm, nsSize(mRect.width, mRect.height));
 
5322
      nscoord y = 0;
 
5323
      while (y < mRect.height) {
 
5324
        nsRect availArea;
 
5325
        band.GetAvailableSpace(y, availArea);
 
5326
  
 
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());
 
5333
  
 
5334
        // Render boxes and opposite diagonal lines around the
 
5335
        // unavailable parts of the band.
 
5336
        PRInt32 i;
 
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));
 
5343
            }
 
5344
            else {
 
5345
              aRenderingContext.SetColor(NS_RGB(128,255,0));
 
5346
            }
 
5347
            aRenderingContext.DrawRect(r);
 
5348
            aRenderingContext.DrawLine(r.x, r.YMost(), r.XMost(), r.y);
 
5349
          }
 
5350
        }
 
5351
        y = availArea.YMost();
 
5352
      }
 
5353
    }
 
5354
  }
 
5355
#endif
 
5356
 
 
5357
  return NS_OK;
 
5358
}
 
5359
 
 
5360
void
 
5361
nsBlockFrame::PaintFloats(nsIPresContext* aPresContext,
 
5362
                          nsIRenderingContext& aRenderingContext,
 
5363
                          const nsRect& aDirtyRect)
 
5364
{
 
5365
  for (nsIFrame* floatFrame = mFloats.FirstChild();
 
5366
       floatFrame;
 
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);
 
5374
  }
 
5375
}
 
5376
 
 
5377
#ifdef DEBUG
 
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);
 
5391
  }
 
5392
}
 
5393
#endif
 
5394
 
 
5395
static inline void
 
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)) {
 
5405
#ifdef DEBUG
 
5406
    DebugOutputDrawLine(aWhichLayer, aDepth, aLine.get(), PR_TRUE);
 
5407
    if (nsBlockFrame::gLamePaintMetrics) {
 
5408
      aDrawnLines++;
 
5409
    }
 
5410
#endif
 
5411
    nsIFrame* kid = aLine->mFirstChild;
 
5412
    PRInt32 n = aLine->GetChildCount();
 
5413
    while (--n >= 0) {
 
5414
      aFrame->PaintChild(aPresContext, aRenderingContext, aDirtyRect, kid,
 
5415
                         aWhichLayer);
 
5416
      kid = kid->GetNextSibling();
 
5417
    }
 
5418
  }
 
5419
#ifdef DEBUG
 
5420
  else {
 
5421
    DebugOutputDrawLine(aWhichLayer, aDepth, aLine.get(), PR_FALSE);
 
5422
  }
 
5423
#endif  
 
5424
}
 
5425
 
 
5426
void
 
5427
nsBlockFrame::PaintChildren(nsIPresContext*      aPresContext,
 
5428
                            nsIRenderingContext& aRenderingContext,
 
5429
                            const nsRect&        aDirtyRect,
 
5430
                            nsFramePaintLayer    aWhichLayer,
 
5431
                            PRUint32             aFlags)
 
5432
{
 
5433
  PRInt32 drawnLines; // Will only be used if set (gLamePaintMetrics).
 
5434
  PRInt32 depth = 0;
 
5435
#ifdef DEBUG
 
5436
  if (gNoisyDamageRepair) {
 
5437
    if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) {
 
5438
      depth = GetDepth();
 
5439
    }
 
5440
  }
 
5441
  PRTime start = LL_ZERO; // Initialize these variables to silence the compiler.
 
5442
  if (gLamePaintMetrics) {
 
5443
    start = PR_Now();
 
5444
    drawnLines = 0;
 
5445
  }
 
5446
#endif
 
5447
 
 
5448
  nsLineBox* cursor = GetFirstLineContaining(aDirtyRect.y);
 
5449
  line_iterator line_end = end_lines();
 
5450
 
 
5451
  if (cursor) {
 
5452
    for (line_iterator line = mLines.begin(cursor);
 
5453
         line != line_end;
 
5454
         ++line) {
 
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()) {
 
5460
          break;
 
5461
        }
 
5462
        PaintLine(lineArea, aDirtyRect, line, depth, drawnLines, aPresContext,
 
5463
                  aRenderingContext, aWhichLayer, this);
 
5464
      }
 
5465
    }
 
5466
  } else {
 
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();
 
5472
         line != line_end;
 
5473
         ++line) {
 
5474
      nsRect lineArea = line->GetCombinedArea();
 
5475
      if (!lineArea.IsEmpty()) {
 
5476
        if (lineArea.y < lastY
 
5477
            || lineArea.YMost() < lastYMost) {
 
5478
          nonDecreasingYs = PR_FALSE;
 
5479
        }
 
5480
        lastY = lineArea.y;
 
5481
        lastYMost = lineArea.YMost();
 
5482
 
 
5483
        PaintLine(lineArea, aDirtyRect, line, depth, drawnLines, aPresContext,
 
5484
                  aRenderingContext, aWhichLayer, this);
 
5485
      }
 
5486
      lineCount++;
 
5487
    }
 
5488
 
 
5489
    if (nonDecreasingYs && lineCount >= MIN_LINES_NEEDING_CURSOR) {
 
5490
      SetupLineCursor();
 
5491
    }
 
5492
  }
 
5493
 
 
5494
  if (NS_FRAME_PAINT_LAYER_FOREGROUND == aWhichLayer) {
 
5495
    if ((nsnull != mBullet) && HaveOutsideBullet()) {
 
5496
      // Paint outside bullets manually
 
5497
      PaintChild(aPresContext, aRenderingContext, aDirtyRect, mBullet,
 
5498
                 aWhichLayer);
 
5499
    }
 
5500
  }
 
5501
 
 
5502
#ifdef DEBUG
 
5503
  if (gLamePaintMetrics) {
 
5504
    PRTime end = PR_Now();
 
5505
 
 
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);
 
5512
 
 
5513
    ListTag(stdout);
 
5514
    char buf[400];
 
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);
 
5520
  }
 
5521
#endif
 
5522
}
 
5523
 
 
5524
// XXXldb Does this handle all overlap cases correctly?  (How?)
 
5525
nsresult
 
5526
nsBlockFrame::GetClosestLine(nsILineIterator *aLI, 
 
5527
                             const nsPoint &aOrigin, 
 
5528
                             const nsPoint &aPoint, 
 
5529
                             PRInt32 &aClosestLine)
 
5530
{
 
5531
  if (!aLI)
 
5532
    return NS_ERROR_NULL_POINTER;
 
5533
 
 
5534
  nsRect rect;
 
5535
  PRInt32 numLines;
 
5536
  PRInt32 lineFrameCount;
 
5537
  nsIFrame *firstFrame;
 
5538
  PRUint32 flags;
 
5539
 
 
5540
  nsresult result = aLI->GetNumLines(&numLines);
 
5541
 
 
5542
  if (NS_FAILED(result) || numLines < 0)
 
5543
    return NS_OK;//do not handle
 
5544
 
 
5545
  PRInt32 shifted = numLines;
 
5546
  PRInt32 start = 0, midpoint = 0;
 
5547
  PRInt32 y = 0;
 
5548
 
 
5549
  while(shifted > 0)
 
5550
  {
 
5551
    // Cut the number of lines to look at in half and
 
5552
    // calculate the midpoint of the region we are looking at.
 
5553
 
 
5554
    shifted >>= 1; //divide by 2
 
5555
    midpoint  = start + shifted;
 
5556
 
 
5557
    // Get the dimensions of the line that is at the half
 
5558
    // point of the region we are looking at.
 
5559
 
 
5560
    result = aLI->GetLine(midpoint, &firstFrame, &lineFrameCount,rect,&flags);
 
5561
    if (NS_FAILED(result))
 
5562
      break;//do not handle
 
5563
 
 
5564
    // Check to see if our point lies with the line's Y bounds.
 
5565
 
 
5566
    rect+=aOrigin; //offset origin to get comparative coordinates
 
5567
 
 
5568
    y = aPoint.y - rect.y;
 
5569
    if (y >=0 && (aPoint.y < (rect.y+rect.height)))
 
5570
    {
 
5571
      aClosestLine = midpoint; //spot on!
 
5572
      return NS_OK;
 
5573
    }
 
5574
 
 
5575
    if (y > 0)
 
5576
    {
 
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.
 
5580
 
 
5581
      start = midpoint;
 
5582
 
 
5583
      if (numLines > 1 && start < (numLines - 1))
 
5584
        ++start;
 
5585
      else
 
5586
        shifted = 0;
 
5587
    }
 
5588
  }
 
5589
 
 
5590
  // Make sure we don't go off the edge in either direction!
 
5591
 
 
5592
  NS_ASSERTION(start >=0 && start <= numLines, "Invalid start calculated.");
 
5593
 
 
5594
  if (start < 0)
 
5595
    start = 0;
 
5596
  else if (start >= numLines)
 
5597
    start = numLines - 1; 
 
5598
 
 
5599
  aClosestLine = start; //close as we could come
 
5600
 
 
5601
  return NS_OK;
 
5602
}
 
5603
 
 
5604
NS_IMETHODIMP
 
5605
nsBlockFrame::HandleEvent(nsIPresContext* aPresContext, 
 
5606
                          nsGUIEvent*     aEvent,
 
5607
                          nsEventStatus*  aEventStatus)
 
5608
{
 
5609
 
 
5610
  nsresult result;
 
5611
  nsIPresShell *shell = nsnull;
 
5612
  if (aEvent->message == NS_MOUSE_MOVE) {
 
5613
    shell = aPresContext->GetPresShell();
 
5614
    if (!shell)
 
5615
      return NS_OK;
 
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)
 
5620
    {
 
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);
 
5626
    }
 
5627
    else
 
5628
      shell->GetFrameSelection(getter_AddRefs(frameSelection));
 
5629
    if (!frameSelection || NS_FAILED(frameSelection->GetMouseDownState(&mouseDown)) || !mouseDown) 
 
5630
      return NS_OK;//do not handle
 
5631
  }
 
5632
 
 
5633
  if (aEvent->message == NS_MOUSE_LEFT_BUTTON_DOWN || aEvent->message == NS_MOUSE_MOVE ||
 
5634
    aEvent->message == NS_MOUSE_LEFT_DOUBLECLICK ) {
 
5635
 
 
5636
    nsMouseEvent *me = (nsMouseEvent *)aEvent;
 
5637
 
 
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();
 
5642
    if (!shell)
 
5643
      return NS_OK;
 
5644
    nsCOMPtr<nsIFocusTracker> tracker( do_QueryInterface(shell, &result) );
 
5645
 
 
5646
    nsCOMPtr<nsILineIterator> it( do_QueryInterface(mainframe, &result) );
 
5647
    nsIView* parentWithView;
 
5648
    nsPoint origin;
 
5649
    nsPeekOffsetStruct pos;
 
5650
 
 
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);
 
5654
 
 
5655
      if (NS_FAILED(result))
 
5656
        return NS_OK;//do not handle
 
5657
      PRInt32 closestLine;
 
5658
 
 
5659
      if (NS_FAILED(result = GetClosestLine(it,origin,aEvent->point,closestLine)))
 
5660
        return result;
 
5661
      
 
5662
      
 
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,
 
5671
                                          &pos,
 
5672
                                          mainframe, 
 
5673
                                          closestLine-1, 
 
5674
                                          0
 
5675
                                          );
 
5676
      
 
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;
 
5682
      }
 
5683
      else
 
5684
        break;//time to go nothing was found
 
5685
    }
 
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.
 
5688
 
 
5689
 
 
5690
    if (resultFrame)
 
5691
    {
 
5692
      if (NS_POSITION_BEFORE_TABLE == result)
 
5693
      {
 
5694
        nsCOMPtr<nsISelectionController> selCon;
 
5695
        result = GetSelectionController(aPresContext, getter_AddRefs(selCon));
 
5696
        //get the selection controller
 
5697
        if (NS_SUCCEEDED(result) && selCon) 
 
5698
        {
 
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
 
5703
        }
 
5704
        nsCOMPtr<nsIFrameSelection> frameselection;
 
5705
        shell->GetFrameSelection(getter_AddRefs(frameselection));
 
5706
        PRBool mouseDown = aEvent->message == NS_MOUSE_MOVE;
 
5707
        if (frameselection)
 
5708
        {
 
5709
          result = frameselection->HandleClick(pos.mResultContent, pos.mContentOffset, 
 
5710
                                               pos.mContentOffsetEnd, mouseDown || me->isShift, PR_FALSE, pos.mPreferLeft);
 
5711
        }
 
5712
      }
 
5713
      else
 
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);
 
5717
      return result;
 
5718
    }
 
5719
    else
 
5720
    {
 
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
 
5725
    }
 
5726
  }
 
5727
  return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
 
5728
}
 
5729
 
 
5730
void nsBlockFrame::ClearLineCursor() {
 
5731
  if (!(GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR)) {
 
5732
    return;
 
5733
  }
 
5734
 
 
5735
  GetProperty(GetPresContext(), nsLayoutAtoms::lineCursorProperty, PR_TRUE);
 
5736
  RemoveStateBits(NS_BLOCK_HAS_LINE_CURSOR);
 
5737
}
 
5738
 
 
5739
void nsBlockFrame::SetupLineCursor() {
 
5740
  if (GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR
 
5741
      || mLines.empty()) {
 
5742
    return;
 
5743
  }
 
5744
   
 
5745
  SetProperty(GetPresContext(), nsLayoutAtoms::lineCursorProperty,
 
5746
              mLines.front(), nsnull);
 
5747
  AddStateBits(NS_BLOCK_HAS_LINE_CURSOR);
 
5748
}
 
5749
 
 
5750
nsLineBox* nsBlockFrame::GetFirstLineContaining(nscoord y) {
 
5751
  if (!(GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR)) {
 
5752
    return nsnull;
 
5753
  }
 
5754
 
 
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();
 
5759
 
 
5760
  while ((cursorArea.IsEmpty() || cursorArea.YMost() > y)
 
5761
         && cursor != mLines.front()) {
 
5762
    cursor = cursor.prev();
 
5763
    cursorArea = cursor->GetCombinedArea();
 
5764
  }
 
5765
  while ((cursorArea.IsEmpty() || cursorArea.YMost() <= y)
 
5766
         && cursor != mLines.back()) {
 
5767
    cursor = cursor.next();
 
5768
    cursorArea = cursor->GetCombinedArea();
 
5769
  }
 
5770
 
 
5771
  if (cursor.get() != property) {
 
5772
    SetProperty(GetPresContext(), nsLayoutAtoms::lineCursorProperty,
 
5773
                cursor.get(), nsnull);
 
5774
  }
 
5775
 
 
5776
  return cursor.get();
 
5777
}
 
5778
 
 
5779
static inline void
 
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();
 
5786
    while (--n >= 0) {
 
5787
      nsIFrame *hit;
 
5788
      nsresult rv = kid->GetFrameForPoint(aPresContext, aTmp, aWhichLayer, &hit);
 
5789
      
 
5790
      if (NS_SUCCEEDED(rv) && hit) {
 
5791
        *aFrame = hit;
 
5792
      }
 
5793
      kid = kid->GetNextSibling();
 
5794
    }
 
5795
  }
 
5796
}
 
5797
 
 
5798
// Optimized function that uses line combined areas to skip lines
 
5799
// we know can't contain the point
 
5800
nsresult
 
5801
nsBlockFrame::GetFrameForPointUsing(nsIPresContext* aPresContext,
 
5802
                                    const nsPoint& aPoint,
 
5803
                                    nsIAtom*       aList,
 
5804
                                    nsFramePaintLayer aWhichLayer,
 
5805
                                    PRBool         aConsiderSelf,
 
5806
                                    nsIFrame**     aFrame)
 
5807
{
 
5808
  if (aList) {
 
5809
    return nsContainerFrame::GetFrameForPointUsing(aPresContext,
 
5810
      aPoint, aList, aWhichLayer, aConsiderSelf, aFrame);
 
5811
  }
 
5812
 
 
5813
  PRBool inThisFrame = mRect.Contains(aPoint);
 
5814
 
 
5815
  if (! ((mState & NS_FRAME_OUTSIDE_CHILDREN) || inThisFrame ) ) {
 
5816
    return NS_ERROR_FAILURE;
 
5817
  }
 
5818
 
 
5819
  *aFrame = nsnull;
 
5820
  nsPoint tmp(aPoint.x - mRect.x, aPoint.y - mRect.y);
 
5821
 
 
5822
  nsPoint originOffset;
 
5823
  nsIView *view = nsnull;
 
5824
  nsresult rv = GetOriginToViewOffset(aPresContext, originOffset, &view);
 
5825
 
 
5826
  if (NS_SUCCEEDED(rv) && view)
 
5827
    tmp += originOffset;
 
5828
 
 
5829
  nsLineBox* cursor = GetFirstLineContaining(tmp.y);
 
5830
  line_iterator line_end = end_lines();
 
5831
 
 
5832
  if (cursor) {
 
5833
    // This is the fast path for large blocks
 
5834
    for (line_iterator line = mLines.begin(cursor);
 
5835
         line != line_end;
 
5836
         ++line) {
 
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) {
 
5842
          break;
 
5843
        }
 
5844
        GetFrameFromLine(lineArea, tmp, line, aPresContext, aWhichLayer, aFrame);
 
5845
      }
 
5846
    }
 
5847
  } else {
 
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();
 
5853
         line != line_end;
 
5854
         ++line) {
 
5855
      nsRect lineArea = line->GetCombinedArea();
 
5856
      if (!lineArea.IsEmpty()) {
 
5857
        if (lineArea.y < lastY
 
5858
            || lineArea.YMost() < lastYMost) {
 
5859
          nonDecreasingYs = PR_FALSE;
 
5860
        }
 
5861
        lastY = lineArea.y;
 
5862
        lastYMost = lineArea.YMost();
 
5863
 
 
5864
        GetFrameFromLine(lineArea, tmp, line, aPresContext, aWhichLayer, aFrame);
 
5865
      }
 
5866
      lineCount++;
 
5867
    }
 
5868
 
 
5869
    if (nonDecreasingYs && lineCount >= MIN_LINES_NEEDING_CURSOR) {
 
5870
      SetupLineCursor();
 
5871
    }
 
5872
  }
 
5873
  
 
5874
  if (*aFrame) {
 
5875
    return NS_OK;
 
5876
  }
 
5877
 
 
5878
  if ( inThisFrame && aConsiderSelf ) {
 
5879
    if (GetStyleVisibility()->IsVisible()) {
 
5880
      *aFrame = this;
 
5881
      return NS_OK;
 
5882
    }
 
5883
  }
 
5884
 
 
5885
  return NS_ERROR_FAILURE;
 
5886
}
 
5887
 
 
5888
NS_IMETHODIMP
 
5889
nsBlockFrame::GetFrameForPoint(nsIPresContext* aPresContext,
 
5890
                               const nsPoint& aPoint,
 
5891
                               nsFramePaintLayer aWhichLayer,
 
5892
                               nsIFrame** aFrame)
 
5893
{
 
5894
  nsresult rv;
 
5895
 
 
5896
  switch (aWhichLayer) {
 
5897
    case NS_FRAME_PAINT_LAYER_FOREGROUND:
 
5898
      rv = GetFrameForPointUsing(aPresContext, aPoint, nsnull, NS_FRAME_PAINT_LAYER_FOREGROUND, PR_FALSE, aFrame);
 
5899
      if (NS_OK == rv) {
 
5900
        return NS_OK;
 
5901
      }
 
5902
      if (nsnull != mBullet) {
 
5903
        rv = GetFrameForPointUsing(aPresContext, aPoint, nsLayoutAtoms::bulletList, NS_FRAME_PAINT_LAYER_FOREGROUND, PR_FALSE, aFrame);
 
5904
      }
 
5905
      return rv;
 
5906
      break;
 
5907
 
 
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);
 
5912
      if (NS_OK == rv) {
 
5913
        return NS_OK;
 
5914
      }
 
5915
      if (mFloats.NotEmpty()) {
 
5916
 
 
5917
        rv = GetFrameForPointUsing(aPresContext, aPoint,
 
5918
                                   nsLayoutAtoms::floatList,
 
5919
                                   NS_FRAME_PAINT_LAYER_FOREGROUND,
 
5920
                                   PR_FALSE, aFrame);
 
5921
        if (NS_OK == rv) {
 
5922
          return NS_OK;
 
5923
        }
 
5924
 
 
5925
        rv = GetFrameForPointUsing(aPresContext, aPoint,
 
5926
                                   nsLayoutAtoms::floatList,
 
5927
                                   NS_FRAME_PAINT_LAYER_FLOATS,
 
5928
                                   PR_FALSE, aFrame);
 
5929
        if (NS_OK == rv) {
 
5930
          return NS_OK;
 
5931
        }
 
5932
 
 
5933
        return GetFrameForPointUsing(aPresContext, aPoint,
 
5934
                                     nsLayoutAtoms::floatList,
 
5935
                                     NS_FRAME_PAINT_LAYER_BACKGROUND,
 
5936
                                     PR_FALSE, aFrame);
 
5937
 
 
5938
      } else {
 
5939
        return NS_ERROR_FAILURE;
 
5940
      }
 
5941
      break;
 
5942
 
 
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,
 
5947
                                   PR_TRUE, aFrame);
 
5948
      break;
 
5949
  }
 
5950
  // we shouldn't get here
 
5951
  NS_ASSERTION(PR_FALSE, "aWhichLayer was not understood");
 
5952
  return NS_ERROR_FAILURE;
 
5953
}
 
5954
 
 
5955
NS_IMETHODIMP
 
5956
nsBlockFrame::ReflowDirtyChild(nsIPresShell* aPresShell, nsIFrame* aChild)
 
5957
{
 
5958
#ifdef DEBUG
 
5959
  if (gNoisyReflow) {
 
5960
    IndentBy(stdout, gNoiseIndent);
 
5961
    ListTag(stdout);
 
5962
    printf(": ReflowDirtyChild (");
 
5963
    if (aChild)
 
5964
      nsFrame::ListTag(stdout, aChild);
 
5965
    else
 
5966
      printf("null");
 
5967
    printf(")\n");
 
5968
 
 
5969
    gNoiseIndent++;
 
5970
  }
 
5971
#endif
 
5972
 
 
5973
  if (aChild) {
 
5974
    // See if the child is absolutely positioned
 
5975
    if (aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
 
5976
      const nsStyleDisplay* disp = aChild->GetStyleDisplay();
 
5977
 
 
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
 
5984
        // dirty...
 
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);
 
5991
        }
 
5992
 
 
5993
#ifdef DEBUG
 
5994
        if (gNoisyReflow) {
 
5995
          IndentBy(stdout, gNoiseIndent);
 
5996
          printf("scheduled reflow command for absolutely positioned frame\n");
 
5997
          --gNoiseIndent;
 
5998
        }
 
5999
#endif
 
6000
 
 
6001
        return rv;
 
6002
      }
 
6003
    }
 
6004
 
 
6005
    // Mark the line containing the child frame dirty.
 
6006
    line_iterator fline = FindLineFor(aChild);
 
6007
    if (fline != end_lines())
 
6008
      MarkLineDirty(fline);
 
6009
  }
 
6010
 
 
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;
 
6017
 
 
6018
    nsFrame::CreateAndPostReflowCommand(aPresShell, this, 
 
6019
      eReflowType_ReflowDirty, nsnull, nsnull, nsnull);
 
6020
 
 
6021
#ifdef DEBUG
 
6022
    if (gNoisyReflow) {
 
6023
      IndentBy(stdout, gNoiseIndent);
 
6024
      printf("scheduled reflow command targeted at self\n");
 
6025
    }
 
6026
#endif
 
6027
  }
 
6028
 
 
6029
#ifdef DEBUG
 
6030
  if (gNoisyReflow) {
 
6031
    --gNoiseIndent;
 
6032
  }
 
6033
#endif
 
6034
  
 
6035
  return NS_OK;
 
6036
}
 
6037
 
 
6038
//////////////////////////////////////////////////////////////////////
 
6039
// Start Debugging
 
6040
 
 
6041
#ifdef NS_DEBUG
 
6042
static PRBool
 
6043
InLineList(nsLineList& aLines, nsIFrame* aFrame)
 
6044
{
 
6045
  for (nsLineList::iterator line = aLines.begin(), line_end = aLines.end();
 
6046
       line != line_end;
 
6047
       ++line) {
 
6048
    nsIFrame* frame = line->mFirstChild;
 
6049
    PRInt32 n = line->GetChildCount();
 
6050
    while (--n >= 0) {
 
6051
      if (frame == aFrame) {
 
6052
        return PR_TRUE;
 
6053
      }
 
6054
      frame = frame->GetNextSibling();
 
6055
    }
 
6056
  }
 
6057
  return PR_FALSE;
 
6058
}
 
6059
 
 
6060
static PRBool
 
6061
InSiblingList(nsLineList& aLines, nsIFrame* aFrame)
 
6062
{
 
6063
  if (! aLines.empty()) {
 
6064
    nsIFrame* frame = aLines.front()->mFirstChild;
 
6065
    while (frame) {
 
6066
      if (frame == aFrame) {
 
6067
        return PR_TRUE;
 
6068
      }
 
6069
      frame = frame->GetNextSibling();
 
6070
    }
 
6071
  }
 
6072
  return PR_FALSE;
 
6073
}
 
6074
 
 
6075
PRBool
 
6076
nsBlockFrame::IsChild(nsIPresContext* aPresContext, nsIFrame* aFrame)
 
6077
{
 
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);
 
6084
  if (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);
 
6089
  }
 
6090
 
 
6091
  if (aFrame->GetParent() != (nsIFrame*)this) {
 
6092
    return PR_FALSE;
 
6093
  }
 
6094
  if ((skipLineList || InLineList(mLines, aFrame)) && 
 
6095
      (skipSiblingList || InSiblingList(mLines, aFrame))) {
 
6096
    return PR_TRUE;
 
6097
  }
 
6098
  nsLineList* overflowLines = GetOverflowLines(aPresContext, PR_FALSE);
 
6099
  if (overflowLines && (skipLineList || InLineList(*overflowLines, aFrame)) && 
 
6100
      (skipSiblingList || InSiblingList(*overflowLines, aFrame))) {
 
6101
    return PR_TRUE;
 
6102
  }
 
6103
  return PR_FALSE;
 
6104
}
 
6105
 
 
6106
NS_IMETHODIMP
 
6107
nsBlockFrame::VerifyTree() const
 
6108
{
 
6109
  // XXX rewrite this
 
6110
  return NS_OK;
 
6111
}
 
6112
#endif
 
6113
 
 
6114
// End Debugging
 
6115
//////////////////////////////////////////////////////////////////////
 
6116
 
 
6117
NS_IMETHODIMP
 
6118
nsBlockFrame::Init(nsIPresContext*  aPresContext,
 
6119
                   nsIContent*      aContent,
 
6120
                   nsIFrame*        aParent,
 
6121
                   nsStyleContext*  aContext,
 
6122
                   nsIFrame*        aPrevInFlow)
 
6123
{
 
6124
  if (aPrevInFlow) {
 
6125
    // Copy over the block/area frame type flags
 
6126
    nsBlockFrame*  blockFrame = (nsBlockFrame*)aPrevInFlow;
 
6127
 
 
6128
    SetFlags(blockFrame->mState & NS_BLOCK_FLAGS_MASK);
 
6129
  }
 
6130
 
 
6131
  nsresult rv = nsBlockFrameSuper::Init(aPresContext, aContent, aParent,
 
6132
                                        aContext, aPrevInFlow);
 
6133
  return rv;
 
6134
}
 
6135
 
 
6136
NS_IMETHODIMP
 
6137
nsBlockFrame::SetInitialChildList(nsIPresContext* aPresContext,
 
6138
                                  nsIAtom*        aListName,
 
6139
                                  nsIFrame*       aChildList)
 
6140
{
 
6141
  nsresult rv = NS_OK;
 
6142
 
 
6143
  if (mAbsoluteContainer.GetChildListName() == aListName) {
 
6144
    mAbsoluteContainer.SetInitialChildList(this, aPresContext, aListName, aChildList);
 
6145
  }
 
6146
  else if (nsLayoutAtoms::floatList == aListName) {
 
6147
    mFloats.SetFrames(aChildList);
 
6148
  }
 
6149
  else {
 
6150
 
 
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
 
6157
        ListTag(stdout);
 
6158
        printf(": first-letter style found\n");
 
6159
#endif
 
6160
      }
 
6161
    }
 
6162
 
 
6163
    rv = AddFrames(aPresContext, aChildList, nsnull);
 
6164
    if (NS_FAILED(rv)) {
 
6165
      return rv;
 
6166
    }
 
6167
 
 
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;
 
6183
          break;
 
6184
        default:
 
6185
          pseudoElement = nsCSSPseudoElements::mozListNumber;
 
6186
          break;
 
6187
      }
 
6188
 
 
6189
      nsIPresShell *shell = aPresContext->PresShell();
 
6190
 
 
6191
      nsRefPtr<nsStyleContext> kidSC = shell->StyleSet()->
 
6192
        ResolvePseudoStyleFor(mContent, pseudoElement, mStyleContext);
 
6193
 
 
6194
      // Create bullet frame
 
6195
      mBullet = new (shell) nsBulletFrame;
 
6196
 
 
6197
      if (nsnull == mBullet) {
 
6198
        return NS_ERROR_OUT_OF_MEMORY;
 
6199
      }
 
6200
      mBullet->Init(aPresContext, mContent, this, kidSC, nsnull);
 
6201
 
 
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;
 
6208
      }
 
6209
      else {
 
6210
        mState |= NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET;
 
6211
      }
 
6212
    }
 
6213
  }
 
6214
 
 
6215
  return NS_OK;
 
6216
}
 
6217
 
 
6218
PRBool
 
6219
nsBlockFrame::FrameStartsCounterScope(nsIFrame* aFrame)
 
6220
{
 
6221
  const nsStyleContent* styleContent = aFrame->GetStyleContent();
 
6222
  if (0 != styleContent->CounterResetCount()) {
 
6223
    // Winner
 
6224
    return PR_TRUE;
 
6225
  }
 
6226
  return PR_FALSE;
 
6227
}
 
6228
 
 
6229
void
 
6230
nsBlockFrame::RenumberLists(nsIPresContext* aPresContext)
 
6231
{
 
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.
 
6235
    return;
 
6236
  }
 
6237
 
 
6238
  // Setup initial list ordinal value
 
6239
  // XXX Map html's start property to counter-reset style
 
6240
  PRInt32 ordinal = 1;
 
6241
 
 
6242
  nsCOMPtr<nsIHTMLContent> hc(do_QueryInterface(mContent));
 
6243
 
 
6244
  if (hc) {
 
6245
    nsHTMLValue value;
 
6246
    if (NS_CONTENT_ATTR_HAS_VALUE ==
 
6247
        hc->GetHTMLAttribute(nsHTMLAtoms::start, value)) {
 
6248
      if (eHTMLUnit_Integer == value.GetUnit()) {
 
6249
        ordinal = value.GetIntValue();
 
6250
      }
 
6251
    }
 
6252
  }
 
6253
 
 
6254
  // Get to first-in-flow
 
6255
  nsBlockFrame* block = (nsBlockFrame*) GetFirstInFlow();
 
6256
  RenumberListsInBlock(aPresContext, block, &ordinal, 0);
 
6257
}
 
6258
 
 
6259
PRBool
 
6260
nsBlockFrame::RenumberListsInBlock(nsIPresContext* aPresContext,
 
6261
                                   nsBlockFrame* aBlockFrame,
 
6262
                                   PRInt32* aOrdinal,
 
6263
                                   PRInt32 aDepth)
 
6264
{
 
6265
  PRBool renumberedABullet = PR_FALSE;
 
6266
 
 
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();
 
6271
         line != line_end;
 
6272
         ++line) {
 
6273
      nsIFrame* kid = line->mFirstChild;
 
6274
      PRInt32 n = line->GetChildCount();
 
6275
      while (--n >= 0) {
 
6276
        PRBool kidRenumberedABullet = RenumberListsFor(aPresContext, kid, aOrdinal, aDepth);
 
6277
        if (kidRenumberedABullet) {
 
6278
          line->MarkDirty();
 
6279
          renumberedABullet = PR_TRUE;
 
6280
        }
 
6281
        kid = kid->GetNextSibling();
 
6282
      }
 
6283
    }
 
6284
 
 
6285
    // Advance to the next continuation
 
6286
    aBlockFrame->GetNextInFlow((nsIFrame**) &aBlockFrame);
 
6287
  }
 
6288
 
 
6289
  return renumberedABullet;
 
6290
}
 
6291
 
 
6292
PRBool
 
6293
nsBlockFrame::RenumberListsFor(nsIPresContext* aPresContext,
 
6294
                               nsIFrame* aKid,
 
6295
                               PRInt32* aOrdinal,
 
6296
                               PRInt32 aDepth)
 
6297
{
 
6298
  NS_PRECONDITION(aPresContext && aKid && aOrdinal, "null params are immoral!");
 
6299
 
 
6300
  // add in a sanity check for absurdly deep frame trees.  See bug 42138
 
6301
  if (MAX_DEPTH_FOR_LIST_RENUMBERING < aDepth)
 
6302
    return PR_FALSE;
 
6303
 
 
6304
  PRBool kidRenumberedABullet = PR_FALSE;
 
6305
  nsIFrame* kid = aKid;
 
6306
 
 
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");
 
6311
  }
 
6312
 
 
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
 
6315
  // ordinal.
 
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) {
 
6324
        PRBool changed;
 
6325
        *aOrdinal = listItem->mBullet->SetListItemOrdinal(*aOrdinal,
 
6326
                                                          &changed);
 
6327
        if (changed) {
 
6328
          kidRenumberedABullet = PR_TRUE;
 
6329
 
 
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);
 
6333
        }
 
6334
      }
 
6335
 
 
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);
 
6340
      if (meToo) {
 
6341
        kidRenumberedABullet = PR_TRUE;
 
6342
      }
 
6343
    }
 
6344
  }
 
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
 
6349
      // it.
 
6350
    }
 
6351
    else {
 
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);
 
6358
      }
 
6359
    }
 
6360
  }
 
6361
  return kidRenumberedABullet;
 
6362
}
 
6363
 
 
6364
void
 
6365
nsBlockFrame::ReflowBullet(nsBlockReflowState& aState,
 
6366
                           nsHTMLReflowMetrics& aMetrics)
 
6367
{
 
6368
  // Reflow the bullet now
 
6369
  nsSize availSize;
 
6370
  availSize.width = NS_UNCONSTRAINEDSIZE;
 
6371
  availSize.height = NS_UNCONSTRAINEDSIZE;
 
6372
 
 
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;
 
6383
    }
 
6384
 
 
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;
 
6388
    if (command) {
 
6389
      nsReflowType type;
 
6390
      command->GetType(type);
 
6391
      if (type == eReflowType_StyleChanged)
 
6392
        reason = eReflowReason_StyleChange;
 
6393
    }
 
6394
  }
 
6395
 
 
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);
 
6401
 
 
6402
  // Place the bullet now; use its right margin to distance it
 
6403
  // from the rest of the frames in the line
 
6404
  nscoord x = 
 
6405
#ifdef IBMBIDI
 
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 :
 
6413
#endif
 
6414
             - reflowState.mComputedMargin.right - aMetrics.width;
 
6415
 
 
6416
  // Approximate the bullets position; vertical alignment will provide
 
6417
  // the final vertical location.
 
6418
  const nsMargin& bp = aState.BorderPadding();
 
6419
  nscoord y = bp.top;
 
6420
  mBullet->SetRect(nsRect(x, y, aMetrics.width, aMetrics.height));
 
6421
  mBullet->DidReflow(aState.mPresContext, &aState.mReflowState, NS_FRAME_REFLOW_FINISHED);
 
6422
}
 
6423
 
 
6424
//XXX get rid of this -- its slow
 
6425
void
 
6426
nsBlockFrame::BuildFloatList()
 
6427
{
 
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();
 
6433
       line != line_end;
 
6434
       ++line) {
 
6435
    if (line->HasFloats()) {
 
6436
      nsFloatCache* fc = line->GetFirstFloat();
 
6437
      while (fc) {
 
6438
        nsIFrame* floatFrame = fc->mPlaceholder->GetOutOfFlowFrame();
 
6439
        if (!head) {
 
6440
          current = head = floatFrame;
 
6441
        } else {
 
6442
          current->SetNextSibling(floatFrame);
 
6443
          current = floatFrame;
 
6444
        }
 
6445
        fc = fc->Next();
 
6446
      }
 
6447
    }
 
6448
  }
 
6449
 
 
6450
  // Terminate end of float list just in case a float was removed
 
6451
  if (current) {
 
6452
    current->SetNextSibling(nsnull);
 
6453
  }
 
6454
  mFloats.SetFrames(head);
 
6455
 
 
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) {
 
6462
    head = nsnull;
 
6463
    current = nsnull;
 
6464
 
 
6465
    nsIFrame* frame = overflowLines->front()->mFirstChild;
 
6466
    while (frame) {
 
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
 
6472
          // must be a float!
 
6473
          // XXX This is a lame-o way of detecting a float, but it's the only way
 
6474
          // apparently
 
6475
          if (!head) {
 
6476
            head = current = outOfFlowFrame;
 
6477
          } else {
 
6478
            current->SetNextSibling(outOfFlowFrame);
 
6479
            current = outOfFlowFrame;
 
6480
          }
 
6481
        }
 
6482
      }
 
6483
      frame = frame->GetNextSibling();
 
6484
    }
 
6485
    
 
6486
    if (current) {
 
6487
      current->SetNextSibling(nsnull);
 
6488
      nsFrameList* frameList = new nsFrameList(head);
 
6489
      if (frameList) {
 
6490
        SetOverflowOutOfFlows(frameList);
 
6491
      }
 
6492
    }
 
6493
  }
 
6494
}
 
6495
 
 
6496
// XXX keep the text-run data in the first-in-flow of the block
 
6497
 
 
6498
#ifdef DEBUG
 
6499
void
 
6500
nsBlockFrame::VerifyLines(PRBool aFinalCheckOK)
 
6501
{
 
6502
  if (!gVerifyLines) {
 
6503
    return;
 
6504
  }
 
6505
  if (mLines.empty()) {
 
6506
    return;
 
6507
  }
 
6508
 
 
6509
  // Add up the counts on each line. Also validate that IsFirstLine is
 
6510
  // set properly.
 
6511
  PRInt32 count = 0;
 
6512
  PRBool seenBlock = PR_FALSE;
 
6513
  line_iterator line, line_end;
 
6514
  for (line = begin_lines(), line_end = end_lines();
 
6515
       line != line_end;
 
6516
       ++line) {
 
6517
    if (aFinalCheckOK) {
 
6518
      NS_ABORT_IF_FALSE(line->GetChildCount(), "empty line");
 
6519
      if (line->IsBlock()) {
 
6520
        seenBlock = PR_TRUE;
 
6521
      }
 
6522
      if (line->IsBlock()) {
 
6523
        NS_ASSERTION(1 == line->GetChildCount(), "bad first line");
 
6524
      }
 
6525
    }
 
6526
    count += line->GetChildCount();
 
6527
  }
 
6528
 
 
6529
  // Then count the frames
 
6530
  PRInt32 frameCount = 0;
 
6531
  nsIFrame* frame = mLines.front()->mFirstChild;
 
6532
  while (frame) {
 
6533
    frameCount++;
 
6534
    frame = frame->GetNextSibling();
 
6535
  }
 
6536
  NS_ASSERTION(count == frameCount, "bad line list");
 
6537
 
 
6538
  // Next: test that each line has right number of frames on it
 
6539
  for (line = begin_lines(), line_end = end_lines();
 
6540
       line != line_end;
 
6541
        ) {
 
6542
    count = line->GetChildCount();
 
6543
    frame = line->mFirstChild;
 
6544
    while (--count >= 0) {
 
6545
      frame = frame->GetNextSibling();
 
6546
    }
 
6547
    ++line;
 
6548
    if ((line != line_end) && (0 != line->GetChildCount())) {
 
6549
      NS_ASSERTION(frame == line->mFirstChild, "bad line list");
 
6550
    }
 
6551
  }
 
6552
}
 
6553
 
 
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.
 
6557
void
 
6558
nsBlockFrame::VerifyOverflowSituation(nsIPresContext* aPresContext)
 
6559
{
 
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");
 
6566
    }
 
6567
    flow = (nsBlockFrame*) flow->mNextInFlow;
 
6568
  }
 
6569
}
 
6570
 
 
6571
PRInt32
 
6572
nsBlockFrame::GetDepth() const
 
6573
{
 
6574
  PRInt32 depth = 0;
 
6575
  nsIFrame* parent = mParent;
 
6576
  while (parent) {
 
6577
    parent = parent->GetParent();
 
6578
    depth++;
 
6579
  }
 
6580
  return depth;
 
6581
}
 
6582
#endif