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