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