layout/generic/nsLineBox.cpp

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

mercurial