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