1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : // vim:cindent:ts=2:et:sw=2:
3 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla Communicator client code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1998
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * L. David Baron <dbaron@dbaron.org>
25 : * Pierre Phaneuf <pp@ludusdesign.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : /* representation of one line within a block frame, a CSS line box */
42 :
43 : #include "nsLineBox.h"
44 : #include "nsLineLayout.h"
45 : #include "prprf.h"
46 : #include "nsBlockFrame.h"
47 : #include "nsGkAtoms.h"
48 : #include "nsFrameManager.h"
49 : #ifdef IBMBIDI
50 : #include "nsBidiPresUtils.h"
51 : #endif
52 :
53 : #ifdef DEBUG
54 : static PRInt32 ctorCount;
55 0 : PRInt32 nsLineBox::GetCtorCount() { return ctorCount; }
56 : #endif
57 :
58 : #ifndef _MSC_VER
59 : // static nsLineBox constant; initialized in the header file.
60 : const PRUint32 nsLineBox::kMinChildCountForHashtable;
61 : #endif
62 :
63 0 : nsLineBox::nsLineBox(nsIFrame* aFrame, PRInt32 aCount, bool aIsBlock)
64 : : mFirstChild(aFrame),
65 : mBounds(0, 0, 0, 0),
66 : mAscent(0),
67 0 : mData(nsnull)
68 : {
69 0 : MOZ_COUNT_CTOR(nsLineBox);
70 : #ifdef DEBUG
71 0 : ++ctorCount;
72 0 : NS_ASSERTION(!aIsBlock || aCount == 1, "Blocks must have exactly one child");
73 0 : nsIFrame* f = aFrame;
74 0 : for (PRInt32 n = aCount; n > 0; f = f->GetNextSibling(), --n) {
75 0 : NS_ASSERTION(aIsBlock == f->GetStyleDisplay()->IsBlockOutside(),
76 : "wrong kind of child frame");
77 : }
78 : #endif
79 :
80 0 : mAllFlags = 0;
81 : #if NS_STYLE_CLEAR_NONE > 0
82 : mFlags.mBreakType = NS_STYLE_CLEAR_NONE;
83 : #endif
84 0 : mChildCount = aCount;
85 0 : MarkDirty();
86 0 : mFlags.mBlock = aIsBlock;
87 0 : }
88 :
89 0 : nsLineBox::~nsLineBox()
90 : {
91 0 : MOZ_COUNT_DTOR(nsLineBox);
92 0 : if (NS_UNLIKELY(mFlags.mHasHashedFrames)) {
93 0 : delete mFrames;
94 : }
95 0 : Cleanup();
96 0 : }
97 :
98 : nsLineBox*
99 0 : NS_NewLineBox(nsIPresShell* aPresShell, nsIFrame* aFrame, bool aIsBlock)
100 : {
101 0 : return new (aPresShell) nsLineBox(aFrame, 1, aIsBlock);
102 : }
103 :
104 : nsLineBox*
105 0 : NS_NewLineBox(nsIPresShell* aPresShell, nsLineBox* aFromLine,
106 : nsIFrame* aFrame, PRInt32 aCount)
107 : {
108 0 : nsLineBox* newLine = new (aPresShell) nsLineBox(aFrame, aCount, false);
109 0 : if (newLine) {
110 0 : newLine->NoteFramesMovedFrom(aFromLine);
111 : }
112 0 : return newLine;
113 : }
114 :
115 : void
116 0 : nsLineBox::StealHashTableFrom(nsLineBox* aFromLine, PRUint32 aFromLineNewCount)
117 : {
118 0 : MOZ_ASSERT(!mFlags.mHasHashedFrames);
119 0 : MOZ_ASSERT(GetChildCount() >= PRInt32(aFromLineNewCount));
120 0 : mFrames = aFromLine->mFrames;
121 0 : mFlags.mHasHashedFrames = 1;
122 0 : aFromLine->mFlags.mHasHashedFrames = 0;
123 0 : aFromLine->mChildCount = aFromLineNewCount;
124 : // remove aFromLine's frames that aren't on this line
125 0 : nsIFrame* f = aFromLine->mFirstChild;
126 0 : for (PRUint32 i = 0; i < aFromLineNewCount; f = f->GetNextSibling(), ++i) {
127 0 : mFrames->RemoveEntry(f);
128 : }
129 0 : }
130 :
131 : void
132 0 : nsLineBox::NoteFramesMovedFrom(nsLineBox* aFromLine)
133 : {
134 0 : PRUint32 fromCount = aFromLine->GetChildCount();
135 0 : PRUint32 toCount = GetChildCount();
136 0 : MOZ_ASSERT(toCount <= fromCount, "moved more frames than aFromLine has");
137 0 : PRUint32 fromNewCount = fromCount - toCount;
138 0 : if (NS_LIKELY(!aFromLine->mFlags.mHasHashedFrames)) {
139 0 : aFromLine->mChildCount = fromNewCount;
140 0 : MOZ_ASSERT(toCount < kMinChildCountForHashtable);
141 0 : } else if (fromNewCount < kMinChildCountForHashtable) {
142 : // aFromLine has a hash table but will not have it after moving the frames
143 : // so this line can steal the hash table if it needs it.
144 0 : if (toCount >= kMinChildCountForHashtable) {
145 0 : StealHashTableFrom(aFromLine, fromNewCount);
146 : } else {
147 0 : delete aFromLine->mFrames;
148 0 : aFromLine->mFlags.mHasHashedFrames = 0;
149 0 : aFromLine->mChildCount = fromNewCount;
150 : }
151 : } else {
152 : // aFromLine still needs a hash table.
153 0 : if (toCount < kMinChildCountForHashtable) {
154 : // remove the moved frames from it
155 0 : nsIFrame* f = mFirstChild;
156 0 : for (PRUint32 i = 0; i < toCount; f = f->GetNextSibling(), ++i) {
157 0 : aFromLine->mFrames->RemoveEntry(f);
158 : }
159 0 : } else if (toCount <= fromNewCount) {
160 : // This line needs a hash table, allocate a hash table for it since that
161 : // means fewer hash ops.
162 0 : nsIFrame* f = mFirstChild;
163 0 : for (PRUint32 i = 0; i < toCount; f = f->GetNextSibling(), ++i) {
164 0 : aFromLine->mFrames->RemoveEntry(f); // toCount RemoveEntry
165 : }
166 0 : SwitchToHashtable(); // toCount PutEntry
167 : } else {
168 : // This line needs a hash table, but it's fewer hash ops to steal
169 : // aFromLine's hash table and allocate a new hash table for that line.
170 0 : StealHashTableFrom(aFromLine, fromNewCount); // fromNewCount RemoveEntry
171 0 : aFromLine->SwitchToHashtable(); // fromNewCount PutEntry
172 : }
173 : }
174 0 : }
175 :
176 : // Overloaded new operator. Uses an arena (which comes from the presShell)
177 : // to perform the allocation.
178 : void*
179 0 : nsLineBox::operator new(size_t sz, nsIPresShell* aPresShell) CPP_THROW_NEW
180 : {
181 0 : return aPresShell->AllocateMisc(sz);
182 : }
183 :
184 : void
185 0 : nsLineBox::Destroy(nsIPresShell* aPresShell)
186 : {
187 0 : this->nsLineBox::~nsLineBox();
188 0 : aPresShell->FreeMisc(sizeof(*this), this);
189 0 : }
190 :
191 : void
192 0 : nsLineBox::Cleanup()
193 : {
194 0 : if (mData) {
195 0 : if (IsBlock()) {
196 0 : delete mBlockData;
197 : }
198 : else {
199 0 : delete mInlineData;
200 : }
201 0 : mData = nsnull;
202 : }
203 0 : }
204 :
205 : #ifdef DEBUG
206 : static void
207 0 : ListFloats(FILE* out, PRInt32 aIndent, const nsFloatCacheList& aFloats)
208 : {
209 0 : nsFloatCache* fc = aFloats.Head();
210 0 : while (fc) {
211 0 : nsFrame::IndentBy(out, aIndent);
212 0 : nsIFrame* frame = fc->mFloat;
213 0 : fprintf(out, "floatframe@%p ", static_cast<void*>(frame));
214 0 : if (frame) {
215 0 : nsAutoString frameName;
216 0 : frame->GetFrameName(frameName);
217 0 : fputs(NS_LossyConvertUTF16toASCII(frameName).get(), out);
218 : }
219 : else {
220 0 : fputs("\n###!!! NULL out-of-flow frame", out);
221 : }
222 0 : fprintf(out, "\n");
223 0 : fc = fc->Next();
224 : }
225 0 : }
226 : #endif
227 :
228 : #ifdef DEBUG
229 : const char *
230 0 : BreakTypeToString(PRUint8 aBreakType)
231 : {
232 0 : switch (aBreakType) {
233 0 : case NS_STYLE_CLEAR_NONE: return "nobr";
234 0 : case NS_STYLE_CLEAR_LEFT: return "leftbr";
235 0 : case NS_STYLE_CLEAR_RIGHT: return "rightbr";
236 0 : case NS_STYLE_CLEAR_LEFT_AND_RIGHT: return "leftbr+rightbr";
237 0 : case NS_STYLE_CLEAR_LINE: return "linebr";
238 0 : case NS_STYLE_CLEAR_BLOCK: return "blockbr";
239 0 : case NS_STYLE_CLEAR_COLUMN: return "columnbr";
240 0 : case NS_STYLE_CLEAR_PAGE: return "pagebr";
241 : default:
242 : break;
243 : }
244 0 : return "unknown";
245 : }
246 :
247 : char*
248 0 : nsLineBox::StateToString(char* aBuf, PRInt32 aBufSize) const
249 : {
250 : PR_snprintf(aBuf, aBufSize, "%s,%s,%s,%s,%s,before:%s,after:%s[0x%x]",
251 0 : IsBlock() ? "block" : "inline",
252 0 : IsDirty() ? "dirty" : "clean",
253 0 : IsPreviousMarginDirty() ? "prevmargindirty" : "prevmarginclean",
254 0 : IsImpactedByFloat() ? "impacted" : "not impacted",
255 0 : IsLineWrapped() ? "wrapped" : "not wrapped",
256 0 : BreakTypeToString(GetBreakTypeBefore()),
257 0 : BreakTypeToString(GetBreakTypeAfter()),
258 0 : mAllFlags);
259 0 : return aBuf;
260 : }
261 :
262 : void
263 0 : nsLineBox::List(FILE* out, PRInt32 aIndent) const
264 : {
265 : PRInt32 i;
266 :
267 0 : for (i = aIndent; --i >= 0; ) fputs(" ", out);
268 : char cbuf[100];
269 : fprintf(out, "line %p: count=%d state=%s ",
270 : static_cast<const void*>(this), GetChildCount(),
271 0 : StateToString(cbuf, sizeof(cbuf)));
272 0 : if (IsBlock() && !GetCarriedOutBottomMargin().IsZero()) {
273 0 : fprintf(out, "bm=%d ", GetCarriedOutBottomMargin().get());
274 : }
275 : fprintf(out, "{%d,%d,%d,%d} ",
276 0 : mBounds.x, mBounds.y, mBounds.width, mBounds.height);
277 0 : if (mData) {
278 : fprintf(out, "vis-overflow={%d,%d,%d,%d} scr-overflow={%d,%d,%d,%d} ",
279 0 : mData->mOverflowAreas.VisualOverflow().x,
280 0 : mData->mOverflowAreas.VisualOverflow().y,
281 0 : mData->mOverflowAreas.VisualOverflow().width,
282 0 : mData->mOverflowAreas.VisualOverflow().height,
283 0 : mData->mOverflowAreas.ScrollableOverflow().x,
284 0 : mData->mOverflowAreas.ScrollableOverflow().y,
285 0 : mData->mOverflowAreas.ScrollableOverflow().width,
286 0 : mData->mOverflowAreas.ScrollableOverflow().height);
287 : }
288 0 : fprintf(out, "<\n");
289 :
290 0 : nsIFrame* frame = mFirstChild;
291 0 : PRInt32 n = GetChildCount();
292 0 : while (--n >= 0) {
293 0 : frame->List(out, aIndent + 1);
294 0 : frame = frame->GetNextSibling();
295 : }
296 :
297 0 : for (i = aIndent; --i >= 0; ) fputs(" ", out);
298 0 : if (HasFloats()) {
299 0 : fputs("> floats <\n", out);
300 0 : ListFloats(out, aIndent + 1, mInlineData->mFloats);
301 0 : for (i = aIndent; --i >= 0; ) fputs(" ", out);
302 : }
303 0 : fputs(">\n", out);
304 0 : }
305 : #endif
306 :
307 : #ifdef DEBUG
308 : nsIFrame*
309 0 : nsLineBox::LastChild() const
310 : {
311 0 : nsIFrame* frame = mFirstChild;
312 0 : PRInt32 n = GetChildCount() - 1;
313 0 : while (--n >= 0) {
314 0 : frame = frame->GetNextSibling();
315 : }
316 0 : return frame;
317 : }
318 : #endif
319 :
320 : PRInt32
321 0 : nsLineBox::IndexOf(nsIFrame* aFrame) const
322 : {
323 0 : PRInt32 i, n = GetChildCount();
324 0 : nsIFrame* frame = mFirstChild;
325 0 : for (i = 0; i < n; i++) {
326 0 : if (frame == aFrame) {
327 0 : return i;
328 : }
329 0 : frame = frame->GetNextSibling();
330 : }
331 0 : return -1;
332 : }
333 :
334 : bool
335 0 : nsLineBox::IsEmpty() const
336 : {
337 0 : if (IsBlock())
338 0 : return mFirstChild->IsEmpty();
339 :
340 : PRInt32 n;
341 : nsIFrame *kid;
342 0 : for (n = GetChildCount(), kid = mFirstChild;
343 : n > 0;
344 : --n, kid = kid->GetNextSibling())
345 : {
346 0 : if (!kid->IsEmpty())
347 0 : return false;
348 : }
349 0 : if (HasBullet()) {
350 0 : return false;
351 : }
352 0 : return true;
353 : }
354 :
355 : bool
356 0 : nsLineBox::CachedIsEmpty()
357 : {
358 0 : if (mFlags.mDirty) {
359 0 : return IsEmpty();
360 : }
361 :
362 0 : if (mFlags.mEmptyCacheValid) {
363 0 : return mFlags.mEmptyCacheState;
364 : }
365 :
366 : bool result;
367 0 : if (IsBlock()) {
368 0 : result = mFirstChild->CachedIsEmpty();
369 : } else {
370 : PRInt32 n;
371 : nsIFrame *kid;
372 0 : result = true;
373 0 : for (n = GetChildCount(), kid = mFirstChild;
374 : n > 0;
375 : --n, kid = kid->GetNextSibling())
376 : {
377 0 : if (!kid->CachedIsEmpty()) {
378 0 : result = false;
379 0 : break;
380 : }
381 : }
382 0 : if (HasBullet()) {
383 0 : result = false;
384 : }
385 : }
386 :
387 0 : mFlags.mEmptyCacheValid = true;
388 0 : mFlags.mEmptyCacheState = result;
389 0 : return result;
390 : }
391 :
392 : void
393 0 : nsLineBox::DeleteLineList(nsPresContext* aPresContext, nsLineList& aLines,
394 : nsIFrame* aDestructRoot)
395 : {
396 0 : if (! aLines.empty()) {
397 : // Delete our child frames before doing anything else. In particular
398 : // we do all of this before our base class releases it's hold on the
399 : // view.
400 : #ifdef DEBUG
401 0 : PRInt32 numFrames = 0;
402 : #endif
403 0 : for (nsIFrame* child = aLines.front()->mFirstChild; child; ) {
404 0 : nsIFrame* nextChild = child->GetNextSibling();
405 0 : child->SetNextSibling(nsnull);
406 0 : child->DestroyFrom((aDestructRoot) ? aDestructRoot : child);
407 0 : child = nextChild;
408 : #ifdef DEBUG
409 0 : numFrames++;
410 : #endif
411 : }
412 :
413 0 : nsIPresShell *shell = aPresContext->PresShell();
414 :
415 0 : do {
416 0 : nsLineBox* line = aLines.front();
417 : #ifdef DEBUG
418 0 : numFrames -= line->GetChildCount();
419 : #endif
420 0 : aLines.pop_front();
421 0 : line->Destroy(shell);
422 0 : } while (! aLines.empty());
423 : #ifdef DEBUG
424 0 : NS_ASSERTION(numFrames == 0, "number of frames deleted does not match");
425 : #endif
426 : }
427 0 : }
428 :
429 : bool
430 0 : nsLineBox::RFindLineContaining(nsIFrame* aFrame,
431 : const nsLineList::iterator& aBegin,
432 : nsLineList::iterator& aEnd,
433 : nsIFrame* aLastFrameBeforeEnd,
434 : PRInt32* aFrameIndexInLine)
435 : {
436 0 : NS_PRECONDITION(aFrame, "null ptr");
437 :
438 0 : nsIFrame* curFrame = aLastFrameBeforeEnd;
439 0 : while (aBegin != aEnd) {
440 0 : --aEnd;
441 0 : NS_ASSERTION(aEnd->LastChild() == curFrame, "Unexpected curFrame");
442 0 : if (NS_UNLIKELY(aEnd->mFlags.mHasHashedFrames) &&
443 0 : !aEnd->Contains(aFrame)) {
444 0 : if (aEnd->mFirstChild) {
445 0 : curFrame = aEnd->mFirstChild->GetPrevSibling();
446 : }
447 0 : continue;
448 : }
449 : // i is the index of curFrame in aEnd
450 0 : PRInt32 i = aEnd->GetChildCount() - 1;
451 0 : while (i >= 0) {
452 0 : if (curFrame == aFrame) {
453 0 : *aFrameIndexInLine = i;
454 0 : return true;
455 : }
456 0 : --i;
457 0 : curFrame = curFrame->GetPrevSibling();
458 : }
459 0 : MOZ_ASSERT(!aEnd->mFlags.mHasHashedFrames, "Contains lied to us!");
460 : }
461 0 : *aFrameIndexInLine = -1;
462 0 : return false;
463 : }
464 :
465 : nsCollapsingMargin
466 0 : nsLineBox::GetCarriedOutBottomMargin() const
467 : {
468 0 : NS_ASSERTION(IsBlock(),
469 : "GetCarriedOutBottomMargin called on non-block line.");
470 0 : return (IsBlock() && mBlockData)
471 : ? mBlockData->mCarriedOutBottomMargin
472 0 : : nsCollapsingMargin();
473 : }
474 :
475 : bool
476 0 : nsLineBox::SetCarriedOutBottomMargin(nsCollapsingMargin aValue)
477 : {
478 0 : bool changed = false;
479 0 : if (IsBlock()) {
480 0 : if (!aValue.IsZero()) {
481 0 : if (!mBlockData) {
482 0 : mBlockData = new ExtraBlockData(mBounds);
483 : }
484 0 : changed = aValue != mBlockData->mCarriedOutBottomMargin;
485 0 : mBlockData->mCarriedOutBottomMargin = aValue;
486 : }
487 0 : else if (mBlockData) {
488 0 : changed = aValue != mBlockData->mCarriedOutBottomMargin;
489 0 : mBlockData->mCarriedOutBottomMargin = aValue;
490 0 : MaybeFreeData();
491 : }
492 : }
493 0 : return changed;
494 : }
495 :
496 : void
497 0 : nsLineBox::MaybeFreeData()
498 : {
499 0 : if (mData && mData->mOverflowAreas == nsOverflowAreas(mBounds, mBounds)) {
500 0 : if (IsInline()) {
501 0 : if (mInlineData->mFloats.IsEmpty()) {
502 0 : delete mInlineData;
503 0 : mInlineData = nsnull;
504 : }
505 : }
506 0 : else if (mBlockData->mCarriedOutBottomMargin.IsZero()) {
507 0 : delete mBlockData;
508 0 : mBlockData = nsnull;
509 : }
510 : }
511 0 : }
512 :
513 : // XXX get rid of this???
514 : nsFloatCache*
515 0 : nsLineBox::GetFirstFloat()
516 : {
517 0 : NS_ABORT_IF_FALSE(IsInline(), "block line can't have floats");
518 0 : return mInlineData ? mInlineData->mFloats.Head() : nsnull;
519 : }
520 :
521 : // XXX this might be too eager to free memory
522 : void
523 0 : nsLineBox::FreeFloats(nsFloatCacheFreeList& aFreeList)
524 : {
525 0 : NS_ABORT_IF_FALSE(IsInline(), "block line can't have floats");
526 0 : if (IsInline() && mInlineData) {
527 0 : if (mInlineData->mFloats.NotEmpty()) {
528 0 : aFreeList.Append(mInlineData->mFloats);
529 : }
530 0 : MaybeFreeData();
531 : }
532 0 : }
533 :
534 : void
535 0 : nsLineBox::AppendFloats(nsFloatCacheFreeList& aFreeList)
536 : {
537 0 : NS_ABORT_IF_FALSE(IsInline(), "block line can't have floats");
538 0 : if (IsInline()) {
539 0 : if (aFreeList.NotEmpty()) {
540 0 : if (!mInlineData) {
541 0 : mInlineData = new ExtraInlineData(mBounds);
542 : }
543 0 : mInlineData->mFloats.Append(aFreeList);
544 : }
545 : }
546 0 : }
547 :
548 : bool
549 0 : nsLineBox::RemoveFloat(nsIFrame* aFrame)
550 : {
551 0 : NS_ABORT_IF_FALSE(IsInline(), "block line can't have floats");
552 0 : if (IsInline() && mInlineData) {
553 0 : nsFloatCache* fc = mInlineData->mFloats.Find(aFrame);
554 0 : if (fc) {
555 : // Note: the placeholder is part of the line's child list
556 : // and will be removed later.
557 0 : mInlineData->mFloats.Remove(fc);
558 0 : delete fc;
559 0 : MaybeFreeData();
560 0 : return true;
561 : }
562 : }
563 0 : return false;
564 : }
565 :
566 : void
567 0 : nsLineBox::SetOverflowAreas(const nsOverflowAreas& aOverflowAreas)
568 : {
569 0 : NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
570 0 : NS_ASSERTION(aOverflowAreas.Overflow(otype).width >= 0,
571 : "illegal width for combined area");
572 0 : NS_ASSERTION(aOverflowAreas.Overflow(otype).height >= 0,
573 : "illegal height for combined area");
574 : }
575 0 : if (!aOverflowAreas.VisualOverflow().IsEqualInterior(mBounds) ||
576 0 : !aOverflowAreas.ScrollableOverflow().IsEqualEdges(mBounds)) {
577 0 : if (!mData) {
578 0 : if (IsInline()) {
579 0 : mInlineData = new ExtraInlineData(mBounds);
580 : }
581 : else {
582 0 : mBlockData = new ExtraBlockData(mBounds);
583 : }
584 : }
585 0 : mData->mOverflowAreas = aOverflowAreas;
586 : }
587 0 : else if (mData) {
588 : // Store away new value so that MaybeFreeData compares against
589 : // the right value.
590 0 : mData->mOverflowAreas = aOverflowAreas;
591 0 : MaybeFreeData();
592 : }
593 0 : }
594 :
595 : //----------------------------------------------------------------------
596 :
597 :
598 : static nsLineBox* gDummyLines[1];
599 :
600 0 : nsLineIterator::nsLineIterator()
601 : {
602 0 : mLines = gDummyLines;
603 0 : mNumLines = 0;
604 0 : mIndex = 0;
605 0 : mRightToLeft = false;
606 0 : }
607 :
608 0 : nsLineIterator::~nsLineIterator()
609 : {
610 0 : if (mLines != gDummyLines) {
611 0 : delete [] mLines;
612 : }
613 0 : }
614 :
615 : /* virtual */ void
616 0 : nsLineIterator::DisposeLineIterator()
617 : {
618 0 : delete this;
619 0 : }
620 :
621 : nsresult
622 0 : nsLineIterator::Init(nsLineList& aLines, bool aRightToLeft)
623 : {
624 0 : mRightToLeft = aRightToLeft;
625 :
626 : // Count the lines
627 0 : PRInt32 numLines = aLines.size();
628 0 : if (0 == numLines) {
629 : // Use gDummyLines so that we don't need null pointer checks in
630 : // the accessor methods
631 0 : mLines = gDummyLines;
632 0 : return NS_OK;
633 : }
634 :
635 : // Make a linear array of the lines
636 0 : mLines = new nsLineBox*[numLines];
637 0 : if (!mLines) {
638 : // Use gDummyLines so that we don't need null pointer checks in
639 : // the accessor methods
640 0 : mLines = gDummyLines;
641 0 : return NS_ERROR_OUT_OF_MEMORY;
642 : }
643 0 : nsLineBox** lp = mLines;
644 0 : for (nsLineList::iterator line = aLines.begin(), line_end = aLines.end() ;
645 : line != line_end;
646 : ++line)
647 : {
648 0 : *lp++ = line;
649 : }
650 0 : mNumLines = numLines;
651 0 : return NS_OK;
652 : }
653 :
654 : PRInt32
655 0 : nsLineIterator::GetNumLines()
656 : {
657 0 : return mNumLines;
658 : }
659 :
660 : bool
661 0 : nsLineIterator::GetDirection()
662 : {
663 0 : return mRightToLeft;
664 : }
665 :
666 : NS_IMETHODIMP
667 0 : nsLineIterator::GetLine(PRInt32 aLineNumber,
668 : nsIFrame** aFirstFrameOnLine,
669 : PRInt32* aNumFramesOnLine,
670 : nsRect& aLineBounds,
671 : PRUint32* aLineFlags)
672 : {
673 0 : NS_ENSURE_ARG_POINTER(aFirstFrameOnLine);
674 0 : NS_ENSURE_ARG_POINTER(aNumFramesOnLine);
675 0 : NS_ENSURE_ARG_POINTER(aLineFlags);
676 :
677 0 : if ((aLineNumber < 0) || (aLineNumber >= mNumLines)) {
678 0 : *aFirstFrameOnLine = nsnull;
679 0 : *aNumFramesOnLine = 0;
680 0 : aLineBounds.SetRect(0, 0, 0, 0);
681 0 : return NS_OK;
682 : }
683 0 : nsLineBox* line = mLines[aLineNumber];
684 0 : *aFirstFrameOnLine = line->mFirstChild;
685 0 : *aNumFramesOnLine = line->GetChildCount();
686 0 : aLineBounds = line->mBounds;
687 :
688 0 : PRUint32 flags = 0;
689 0 : if (line->IsBlock()) {
690 0 : flags |= NS_LINE_FLAG_IS_BLOCK;
691 : }
692 : else {
693 0 : if (line->HasBreakAfter())
694 0 : flags |= NS_LINE_FLAG_ENDS_IN_BREAK;
695 : }
696 0 : *aLineFlags = flags;
697 :
698 0 : return NS_OK;
699 : }
700 :
701 : PRInt32
702 0 : nsLineIterator::FindLineContaining(nsIFrame* aFrame, PRInt32 aStartLine)
703 : {
704 0 : NS_PRECONDITION(aStartLine <= mNumLines, "Bogus line numbers");
705 0 : PRInt32 lineNumber = aStartLine;
706 0 : while (lineNumber != mNumLines) {
707 0 : nsLineBox* line = mLines[lineNumber];
708 0 : if (line->Contains(aFrame)) {
709 0 : return lineNumber;
710 : }
711 0 : ++lineNumber;
712 : }
713 0 : return -1;
714 : }
715 :
716 : #ifdef IBMBIDI
717 : NS_IMETHODIMP
718 0 : nsLineIterator::CheckLineOrder(PRInt32 aLine,
719 : bool *aIsReordered,
720 : nsIFrame **aFirstVisual,
721 : nsIFrame **aLastVisual)
722 : {
723 0 : NS_ASSERTION (aLine >= 0 && aLine < mNumLines, "aLine out of range!");
724 0 : nsLineBox* line = mLines[aLine];
725 :
726 0 : if (!line->mFirstChild) { // empty line
727 0 : *aIsReordered = false;
728 0 : *aFirstVisual = nsnull;
729 0 : *aLastVisual = nsnull;
730 0 : return NS_OK;
731 : }
732 :
733 : nsIFrame* leftmostFrame;
734 : nsIFrame* rightmostFrame;
735 0 : *aIsReordered = nsBidiPresUtils::CheckLineOrder(line->mFirstChild, line->GetChildCount(), &leftmostFrame, &rightmostFrame);
736 :
737 : // map leftmost/rightmost to first/last according to paragraph direction
738 0 : *aFirstVisual = mRightToLeft ? rightmostFrame : leftmostFrame;
739 0 : *aLastVisual = mRightToLeft ? leftmostFrame : rightmostFrame;
740 :
741 0 : return NS_OK;
742 : }
743 : #endif // IBMBIDI
744 :
745 : NS_IMETHODIMP
746 0 : nsLineIterator::FindFrameAt(PRInt32 aLineNumber,
747 : nscoord aX,
748 : nsIFrame** aFrameFound,
749 : bool* aXIsBeforeFirstFrame,
750 : bool* aXIsAfterLastFrame)
751 : {
752 0 : NS_PRECONDITION(aFrameFound && aXIsBeforeFirstFrame && aXIsAfterLastFrame,
753 : "null OUT ptr");
754 0 : if (!aFrameFound || !aXIsBeforeFirstFrame || !aXIsAfterLastFrame) {
755 0 : return NS_ERROR_NULL_POINTER;
756 : }
757 0 : if ((aLineNumber < 0) || (aLineNumber >= mNumLines)) {
758 0 : return NS_ERROR_INVALID_ARG;
759 : }
760 :
761 0 : nsLineBox* line = mLines[aLineNumber];
762 0 : if (!line) {
763 0 : *aFrameFound = nsnull;
764 0 : *aXIsBeforeFirstFrame = true;
765 0 : *aXIsAfterLastFrame = false;
766 0 : return NS_OK;
767 : }
768 :
769 0 : if (line->mBounds.width == 0 && line->mBounds.height == 0)
770 0 : return NS_ERROR_FAILURE;
771 :
772 0 : nsIFrame* frame = line->mFirstChild;
773 0 : nsIFrame* closestFromLeft = nsnull;
774 0 : nsIFrame* closestFromRight = nsnull;
775 0 : PRInt32 n = line->GetChildCount();
776 0 : while (n--) {
777 0 : nsRect rect = frame->GetRect();
778 0 : if (rect.width > 0) {
779 : // If aX is inside this frame - this is it
780 0 : if (rect.x <= aX && rect.XMost() > aX) {
781 0 : closestFromLeft = closestFromRight = frame;
782 : break;
783 : }
784 0 : if (rect.x < aX) {
785 0 : if (!closestFromLeft ||
786 0 : rect.XMost() > closestFromLeft->GetRect().XMost())
787 0 : closestFromLeft = frame;
788 : }
789 : else {
790 0 : if (!closestFromRight ||
791 0 : rect.x < closestFromRight->GetRect().x)
792 0 : closestFromRight = frame;
793 : }
794 : }
795 0 : frame = frame->GetNextSibling();
796 : }
797 0 : if (!closestFromLeft && !closestFromRight) {
798 : // All frames were zero-width. Just take the first one.
799 0 : closestFromLeft = closestFromRight = line->mFirstChild;
800 : }
801 0 : *aXIsBeforeFirstFrame = mRightToLeft ? !closestFromRight : !closestFromLeft;
802 0 : *aXIsAfterLastFrame = mRightToLeft ? !closestFromLeft : !closestFromRight;
803 0 : if (closestFromLeft == closestFromRight) {
804 0 : *aFrameFound = closestFromLeft;
805 : }
806 0 : else if (!closestFromLeft) {
807 0 : *aFrameFound = closestFromRight;
808 : }
809 0 : else if (!closestFromRight) {
810 0 : *aFrameFound = closestFromLeft;
811 : }
812 : else { // we're between two frames
813 0 : nscoord delta = closestFromRight->GetRect().x - closestFromLeft->GetRect().XMost();
814 0 : if (aX < closestFromLeft->GetRect().XMost() + delta/2)
815 0 : *aFrameFound = closestFromLeft;
816 : else
817 0 : *aFrameFound = closestFromRight;
818 : }
819 0 : return NS_OK;
820 : }
821 :
822 : NS_IMETHODIMP
823 0 : nsLineIterator::GetNextSiblingOnLine(nsIFrame*& aFrame, PRInt32 aLineNumber)
824 : {
825 0 : aFrame = aFrame->GetNextSibling();
826 0 : return NS_OK;
827 : }
828 :
829 : //----------------------------------------------------------------------
830 :
831 : #ifdef NS_BUILD_REFCNT_LOGGING
832 0 : nsFloatCacheList::nsFloatCacheList() :
833 0 : mHead(nsnull)
834 : {
835 0 : MOZ_COUNT_CTOR(nsFloatCacheList);
836 0 : }
837 : #endif
838 :
839 0 : nsFloatCacheList::~nsFloatCacheList()
840 : {
841 0 : DeleteAll();
842 0 : MOZ_COUNT_DTOR(nsFloatCacheList);
843 0 : }
844 :
845 : void
846 0 : nsFloatCacheList::DeleteAll()
847 : {
848 0 : nsFloatCache* c = mHead;
849 0 : while (c) {
850 0 : nsFloatCache* next = c->Next();
851 0 : delete c;
852 0 : c = next;
853 : }
854 0 : mHead = nsnull;
855 0 : }
856 :
857 : nsFloatCache*
858 0 : nsFloatCacheList::Tail() const
859 : {
860 0 : nsFloatCache* fc = mHead;
861 0 : while (fc) {
862 0 : if (!fc->mNext) {
863 0 : break;
864 : }
865 0 : fc = fc->mNext;
866 : }
867 0 : return fc;
868 : }
869 :
870 : void
871 0 : nsFloatCacheList::Append(nsFloatCacheFreeList& aList)
872 : {
873 0 : NS_PRECONDITION(aList.NotEmpty(), "Appending empty list will fail");
874 :
875 0 : nsFloatCache* tail = Tail();
876 0 : if (tail) {
877 0 : NS_ASSERTION(!tail->mNext, "Bogus!");
878 0 : tail->mNext = aList.mHead;
879 : }
880 : else {
881 0 : NS_ASSERTION(!mHead, "Bogus!");
882 0 : mHead = aList.mHead;
883 : }
884 0 : aList.mHead = nsnull;
885 0 : aList.mTail = nsnull;
886 0 : }
887 :
888 : nsFloatCache*
889 0 : nsFloatCacheList::Find(nsIFrame* aOutOfFlowFrame)
890 : {
891 0 : nsFloatCache* fc = mHead;
892 0 : while (fc) {
893 0 : if (fc->mFloat == aOutOfFlowFrame) {
894 0 : break;
895 : }
896 0 : fc = fc->Next();
897 : }
898 0 : return fc;
899 : }
900 :
901 : nsFloatCache*
902 0 : nsFloatCacheList::RemoveAndReturnPrev(nsFloatCache* aElement)
903 : {
904 0 : nsFloatCache* fc = mHead;
905 0 : nsFloatCache* prev = nsnull;
906 0 : while (fc) {
907 0 : if (fc == aElement) {
908 0 : if (prev) {
909 0 : prev->mNext = fc->mNext;
910 : } else {
911 0 : mHead = fc->mNext;
912 : }
913 0 : return prev;
914 : }
915 0 : prev = fc;
916 0 : fc = fc->mNext;
917 : }
918 0 : return nsnull;
919 : }
920 :
921 : //----------------------------------------------------------------------
922 :
923 : #ifdef NS_BUILD_REFCNT_LOGGING
924 0 : nsFloatCacheFreeList::nsFloatCacheFreeList() :
925 0 : mTail(nsnull)
926 : {
927 0 : MOZ_COUNT_CTOR(nsFloatCacheFreeList);
928 0 : }
929 :
930 0 : nsFloatCacheFreeList::~nsFloatCacheFreeList()
931 : {
932 0 : MOZ_COUNT_DTOR(nsFloatCacheFreeList);
933 0 : }
934 : #endif
935 :
936 : void
937 0 : nsFloatCacheFreeList::Append(nsFloatCacheList& aList)
938 : {
939 0 : NS_PRECONDITION(aList.NotEmpty(), "Appending empty list will fail");
940 :
941 0 : if (mTail) {
942 0 : NS_ASSERTION(!mTail->mNext, "Bogus");
943 0 : mTail->mNext = aList.mHead;
944 : }
945 : else {
946 0 : NS_ASSERTION(!mHead, "Bogus");
947 0 : mHead = aList.mHead;
948 : }
949 0 : mTail = aList.Tail();
950 0 : aList.mHead = nsnull;
951 0 : }
952 :
953 : void
954 0 : nsFloatCacheFreeList::Remove(nsFloatCache* aElement)
955 : {
956 0 : nsFloatCache* prev = nsFloatCacheList::RemoveAndReturnPrev(aElement);
957 0 : if (mTail == aElement) {
958 0 : mTail = prev;
959 : }
960 0 : }
961 :
962 : void
963 0 : nsFloatCacheFreeList::DeleteAll()
964 : {
965 0 : nsFloatCacheList::DeleteAll();
966 0 : mTail = nsnull;
967 0 : }
968 :
969 : nsFloatCache*
970 0 : nsFloatCacheFreeList::Alloc(nsIFrame* aFloat)
971 : {
972 0 : NS_PRECONDITION(aFloat->GetStateBits() & NS_FRAME_OUT_OF_FLOW,
973 : "This is a float cache, why isn't the frame out-of-flow?");
974 0 : nsFloatCache* fc = mHead;
975 0 : if (mHead) {
976 0 : if (mHead == mTail) {
977 0 : mHead = mTail = nsnull;
978 : }
979 : else {
980 0 : mHead = fc->mNext;
981 : }
982 0 : fc->mNext = nsnull;
983 : }
984 : else {
985 0 : fc = new nsFloatCache();
986 : }
987 0 : fc->mFloat = aFloat;
988 0 : return fc;
989 : }
990 :
991 : void
992 0 : nsFloatCacheFreeList::Append(nsFloatCache* aFloat)
993 : {
994 0 : NS_ASSERTION(!aFloat->mNext, "Bogus!");
995 0 : aFloat->mNext = nsnull;
996 0 : if (mTail) {
997 0 : NS_ASSERTION(!mTail->mNext, "Bogus!");
998 0 : mTail->mNext = aFloat;
999 0 : mTail = aFloat;
1000 : }
1001 : else {
1002 0 : NS_ASSERTION(!mHead, "Bogus!");
1003 0 : mHead = mTail = aFloat;
1004 : }
1005 0 : }
1006 :
1007 : //----------------------------------------------------------------------
1008 :
1009 0 : nsFloatCache::nsFloatCache()
1010 : : mFloat(nsnull),
1011 0 : mNext(nsnull)
1012 : {
1013 0 : MOZ_COUNT_CTOR(nsFloatCache);
1014 0 : }
1015 :
1016 : #ifdef NS_BUILD_REFCNT_LOGGING
1017 0 : nsFloatCache::~nsFloatCache()
1018 : {
1019 0 : MOZ_COUNT_DTOR(nsFloatCache);
1020 0 : }
1021 : #endif
|