Line data Source code
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/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 0 : 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 0 : , 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 0 : MOZ_COUNT_CTOR(nsLineBox);
54 : #ifdef DEBUG
55 0 : ++ctorCount;
56 0 : NS_ASSERTION(!aIsBlock || aCount == 1, "Blocks must have exactly one child");
57 : nsIFrame* f = aFrame;
58 0 : for (int32_t n = aCount; n > 0; f = f->GetNextSibling(), --n) {
59 0 : 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 0 : mChildCount = aCount;
67 0 : MarkDirty();
68 0 : mFlags.mBlock = aIsBlock;
69 0 : }
70 :
71 0 : nsLineBox::~nsLineBox()
72 : {
73 0 : MOZ_COUNT_DTOR(nsLineBox);
74 0 : if (MOZ_UNLIKELY(mFlags.mHasHashedFrames)) {
75 0 : delete mFrames;
76 : }
77 0 : Cleanup();
78 0 : }
79 :
80 : nsLineBox*
81 0 : NS_NewLineBox(nsIPresShell* aPresShell, nsIFrame* aFrame, bool aIsBlock)
82 : {
83 0 : 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 0 : nsLineBox::operator new(size_t sz, nsIPresShell* aPresShell)
159 : {
160 0 : return aPresShell->AllocateByObjectID(eArenaObjectID_nsLineBox, sz);
161 : }
162 :
163 : void
164 0 : nsLineBox::Destroy(nsIPresShell* aPresShell)
165 : {
166 0 : this->nsLineBox::~nsLineBox();
167 0 : aPresShell->FreeByObjectID(eArenaObjectID_nsLineBox, this);
168 0 : }
169 :
170 : void
171 0 : nsLineBox::Cleanup()
172 : {
173 0 : if (mData) {
174 0 : if (IsBlock()) {
175 0 : delete mBlockData;
176 : }
177 : else {
178 0 : delete mInlineData;
179 : }
180 0 : mData = nullptr;
181 : }
182 0 : }
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 : /* static */ const char*
207 0 : nsLineBox::BreakTypeToString(StyleClear aBreakType)
208 : {
209 0 : switch (aBreakType) {
210 : 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 0 : nsLineBox::IndexOf(nsIFrame* aFrame) const
312 : {
313 0 : int32_t i, n = GetChildCount();
314 0 : nsIFrame* frame = mFirstChild;
315 0 : for (i = 0; i < n; i++) {
316 0 : if (frame == aFrame) {
317 : return i;
318 : }
319 0 : frame = frame->GetNextSibling();
320 : }
321 : return -1;
322 : }
323 :
324 : bool
325 0 : nsLineBox::IsEmpty() const
326 : {
327 0 : if (IsBlock())
328 0 : return mFirstChild->IsEmpty();
329 :
330 : int32_t n;
331 : nsIFrame *kid;
332 0 : for (n = GetChildCount(), kid = mFirstChild;
333 0 : n > 0;
334 : --n, kid = kid->GetNextSibling())
335 : {
336 0 : if (!kid->IsEmpty())
337 : return false;
338 : }
339 0 : if (HasBullet()) {
340 : return false;
341 : }
342 0 : return true;
343 : }
344 :
345 : bool
346 0 : nsLineBox::CachedIsEmpty()
347 : {
348 0 : if (mFlags.mDirty) {
349 0 : return IsEmpty();
350 : }
351 :
352 0 : if (mFlags.mEmptyCacheValid) {
353 0 : return mFlags.mEmptyCacheState;
354 : }
355 :
356 : bool result;
357 0 : if (IsBlock()) {
358 0 : result = mFirstChild->CachedIsEmpty();
359 : } else {
360 : int32_t n;
361 : nsIFrame *kid;
362 0 : result = true;
363 0 : for (n = GetChildCount(), kid = mFirstChild;
364 0 : n > 0;
365 : --n, kid = kid->GetNextSibling())
366 : {
367 0 : if (!kid->CachedIsEmpty()) {
368 : result = false;
369 : break;
370 : }
371 : }
372 0 : if (HasBullet()) {
373 0 : result = false;
374 : }
375 : }
376 :
377 0 : mFlags.mEmptyCacheValid = true;
378 0 : mFlags.mEmptyCacheState = result;
379 0 : return result;
380 : }
381 :
382 : void
383 0 : nsLineBox::DeleteLineList(nsPresContext* aPresContext, nsLineList& aLines,
384 : nsIFrame* aDestructRoot, nsFrameList* aFrames,
385 : PostDestroyData& aPostDestroyData)
386 : {
387 0 : nsIPresShell* shell = aPresContext->PresShell();
388 :
389 : // Keep our line list and frame list up to date as we
390 : // remove frames, in case something wants to traverse the
391 : // frame tree while we're destroying.
392 0 : while (!aLines.empty()) {
393 0 : nsLineBox* line = aLines.front();
394 0 : if (MOZ_UNLIKELY(line->mFlags.mHasHashedFrames)) {
395 0 : line->SwitchToCounter(); // Avoid expensive has table removals.
396 : }
397 0 : while (line->GetChildCount() > 0) {
398 0 : nsIFrame* child = aFrames->RemoveFirstChild();
399 0 : MOZ_DIAGNOSTIC_ASSERT(child->PresContext() == aPresContext);
400 0 : MOZ_DIAGNOSTIC_ASSERT(child == line->mFirstChild, "Lines out of sync");
401 0 : line->mFirstChild = aFrames->FirstChild();
402 0 : line->NoteFrameRemoved(child);
403 3 : child->DestroyFrom(aDestructRoot, aPostDestroyData);
404 : }
405 0 : MOZ_DIAGNOSTIC_ASSERT(line == aLines.front(),
406 : "destroying child frames messed up our lines!");
407 3 : aLines.pop_front();
408 0 : line->Destroy(shell);
409 : }
410 4 : }
411 :
412 : bool
413 0 : nsLineBox::RFindLineContaining(nsIFrame* aFrame,
414 : const nsLineList::iterator& aBegin,
415 : nsLineList::iterator& aEnd,
416 : nsIFrame* aLastFrameBeforeEnd,
417 : int32_t* aFrameIndexInLine)
418 : {
419 0 : MOZ_ASSERT(aFrame, "null ptr");
420 :
421 : nsIFrame* curFrame = aLastFrameBeforeEnd;
422 0 : while (aBegin != aEnd) {
423 0 : --aEnd;
424 0 : NS_ASSERTION(aEnd->LastChild() == curFrame, "Unexpected curFrame");
425 0 : if (MOZ_UNLIKELY(aEnd->mFlags.mHasHashedFrames) &&
426 0 : !aEnd->Contains(aFrame)) {
427 0 : if (aEnd->mFirstChild) {
428 0 : curFrame = aEnd->mFirstChild->GetPrevSibling();
429 : }
430 : continue;
431 : }
432 : // i is the index of curFrame in aEnd
433 0 : int32_t i = aEnd->GetChildCount() - 1;
434 0 : while (i >= 0) {
435 0 : if (curFrame == aFrame) {
436 0 : *aFrameIndexInLine = i;
437 0 : return true;
438 : }
439 0 : --i;
440 0 : curFrame = curFrame->GetPrevSibling();
441 : }
442 0 : MOZ_ASSERT(!aEnd->mFlags.mHasHashedFrames, "Contains lied to us!");
443 : }
444 0 : *aFrameIndexInLine = -1;
445 0 : return false;
446 : }
447 :
448 : nsCollapsingMargin
449 0 : nsLineBox::GetCarriedOutBEndMargin() const
450 : {
451 0 : NS_ASSERTION(IsBlock(),
452 : "GetCarriedOutBEndMargin called on non-block line.");
453 0 : return (IsBlock() && mBlockData)
454 2 : ? mBlockData->mCarriedOutBEndMargin
455 4 : : nsCollapsingMargin();
456 : }
457 :
458 : bool
459 0 : nsLineBox::SetCarriedOutBEndMargin(nsCollapsingMargin aValue)
460 : {
461 0 : bool changed = false;
462 0 : if (IsBlock()) {
463 0 : if (!aValue.IsZero()) {
464 5 : if (!mBlockData) {
465 0 : mBlockData = new ExtraBlockData(GetPhysicalBounds());
466 : }
467 5 : changed = aValue != mBlockData->mCarriedOutBEndMargin;
468 0 : mBlockData->mCarriedOutBEndMargin = aValue;
469 : }
470 0 : else if (mBlockData) {
471 0 : changed = aValue != mBlockData->mCarriedOutBEndMargin;
472 0 : mBlockData->mCarriedOutBEndMargin = aValue;
473 0 : MaybeFreeData();
474 : }
475 : }
476 5 : return changed;
477 : }
478 :
479 : void
480 0 : nsLineBox::MaybeFreeData()
481 : {
482 0 : nsRect bounds = GetPhysicalBounds();
483 0 : if (mData && mData->mOverflowAreas == nsOverflowAreas(bounds, bounds)) {
484 0 : if (IsInline()) {
485 0 : if (mInlineData->mFloats.IsEmpty()) {
486 0 : delete mInlineData;
487 0 : mInlineData = nullptr;
488 : }
489 : }
490 0 : else if (mBlockData->mCarriedOutBEndMargin.IsZero()) {
491 0 : delete mBlockData;
492 0 : mBlockData = nullptr;
493 : }
494 : }
495 4 : }
496 :
497 : // XXX get rid of this???
498 : nsFloatCache*
499 0 : nsLineBox::GetFirstFloat()
500 : {
501 0 : MOZ_ASSERT(IsInline(), "block line can't have floats");
502 0 : return mInlineData ? mInlineData->mFloats.Head() : nullptr;
503 : }
504 :
505 : // XXX this might be too eager to free memory
506 : void
507 0 : nsLineBox::FreeFloats(nsFloatCacheFreeList& aFreeList)
508 : {
509 0 : MOZ_ASSERT(IsInline(), "block line can't have floats");
510 0 : if (IsInline() && mInlineData) {
511 8 : if (mInlineData->mFloats.NotEmpty()) {
512 0 : aFreeList.Append(mInlineData->mFloats);
513 : }
514 0 : MaybeFreeData();
515 : }
516 23 : }
517 :
518 : void
519 0 : nsLineBox::AppendFloats(nsFloatCacheFreeList& aFreeList)
520 : {
521 0 : MOZ_ASSERT(IsInline(), "block line can't have floats");
522 0 : if (IsInline()) {
523 0 : if (aFreeList.NotEmpty()) {
524 0 : if (!mInlineData) {
525 0 : mInlineData = new ExtraInlineData(GetPhysicalBounds());
526 : }
527 0 : mInlineData->mFloats.Append(aFreeList);
528 : }
529 : }
530 23 : }
531 :
532 : bool
533 0 : nsLineBox::RemoveFloat(nsIFrame* aFrame)
534 : {
535 0 : MOZ_ASSERT(IsInline(), "block line can't have floats");
536 0 : if (IsInline() && mInlineData) {
537 0 : nsFloatCache* fc = mInlineData->mFloats.Find(aFrame);
538 0 : if (fc) {
539 : // Note: the placeholder is part of the line's child list
540 : // and will be removed later.
541 0 : mInlineData->mFloats.Remove(fc);
542 0 : delete fc;
543 0 : MaybeFreeData();
544 0 : return true;
545 : }
546 : }
547 : return false;
548 : }
549 :
550 : void
551 0 : nsLineBox::SetFloatEdges(nscoord aStart, nscoord aEnd)
552 : {
553 0 : MOZ_ASSERT(IsInline(), "block line can't have float edges");
554 0 : if (!mInlineData) {
555 0 : mInlineData = new ExtraInlineData(GetPhysicalBounds());
556 : }
557 0 : mInlineData->mFloatEdgeIStart = aStart;
558 0 : mInlineData->mFloatEdgeIEnd = aEnd;
559 0 : }
560 :
561 : void
562 0 : nsLineBox::ClearFloatEdges()
563 : {
564 0 : MOZ_ASSERT(IsInline(), "block line can't have float edges");
565 0 : if (mInlineData) {
566 8 : mInlineData->mFloatEdgeIStart = nscoord_MIN;
567 0 : mInlineData->mFloatEdgeIEnd = nscoord_MIN;
568 : }
569 23 : }
570 :
571 : void
572 0 : nsLineBox::SetOverflowAreas(const nsOverflowAreas& aOverflowAreas)
573 : {
574 84 : NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
575 0 : NS_ASSERTION(aOverflowAreas.Overflow(otype).width >= 0,
576 : "illegal width for combined area");
577 56 : NS_ASSERTION(aOverflowAreas.Overflow(otype).height >= 0,
578 : "illegal height for combined area");
579 : }
580 0 : nsRect bounds = GetPhysicalBounds();
581 0 : if (!aOverflowAreas.VisualOverflow().IsEqualInterior(bounds) ||
582 0 : !aOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds)) {
583 0 : if (!mData) {
584 4 : if (IsInline()) {
585 4 : mInlineData = new ExtraInlineData(bounds);
586 : }
587 : else {
588 0 : mBlockData = new ExtraBlockData(bounds);
589 : }
590 : }
591 0 : mData->mOverflowAreas = aOverflowAreas;
592 : }
593 15 : else if (mData) {
594 : // Store away new value so that MaybeFreeData compares against
595 : // the right value.
596 0 : mData->mOverflowAreas = aOverflowAreas;
597 0 : MaybeFreeData();
598 : }
599 28 : }
600 :
601 : //----------------------------------------------------------------------
602 :
603 :
604 : static nsLineBox* gDummyLines[1];
605 :
606 0 : nsLineIterator::nsLineIterator()
607 : {
608 0 : mLines = gDummyLines;
609 0 : mNumLines = 0;
610 0 : mIndex = 0;
611 4 : mRightToLeft = false;
612 0 : }
613 :
614 0 : nsLineIterator::~nsLineIterator()
615 : {
616 4 : if (mLines != gDummyLines) {
617 0 : delete [] mLines;
618 : }
619 4 : }
620 :
621 : /* virtual */ void
622 0 : nsLineIterator::DisposeLineIterator()
623 : {
624 4 : delete this;
625 4 : }
626 :
627 : nsresult
628 0 : nsLineIterator::Init(nsLineList& aLines, bool aRightToLeft)
629 : {
630 4 : mRightToLeft = aRightToLeft;
631 :
632 : // Count the lines
633 4 : int32_t numLines = aLines.size();
634 4 : if (0 == numLines) {
635 : // Use gDummyLines so that we don't need null pointer checks in
636 : // the accessor methods
637 1 : mLines = gDummyLines;
638 1 : return NS_OK;
639 : }
640 :
641 : // Make a linear array of the lines
642 6 : mLines = new nsLineBox*[numLines];
643 3 : if (!mLines) {
644 : // Use gDummyLines so that we don't need null pointer checks in
645 : // the accessor methods
646 0 : mLines = gDummyLines;
647 0 : return NS_ERROR_OUT_OF_MEMORY;
648 : }
649 3 : nsLineBox** lp = mLines;
650 3 : for (nsLineList::iterator line = aLines.begin(), line_end = aLines.end() ;
651 : line != line_end;
652 : ++line)
653 : {
654 0 : *lp++ = line;
655 : }
656 3 : mNumLines = numLines;
657 3 : return NS_OK;
658 : }
659 :
660 : int32_t
661 0 : nsLineIterator::GetNumLines()
662 : {
663 0 : return mNumLines;
664 : }
665 :
666 : bool
667 0 : nsLineIterator::GetDirection()
668 : {
669 0 : return mRightToLeft;
670 : }
671 :
672 : NS_IMETHODIMP
673 7 : nsLineIterator::GetLine(int32_t aLineNumber,
674 : nsIFrame** aFirstFrameOnLine,
675 : int32_t* aNumFramesOnLine,
676 : nsRect& aLineBounds)
677 : {
678 7 : NS_ENSURE_ARG_POINTER(aFirstFrameOnLine);
679 0 : NS_ENSURE_ARG_POINTER(aNumFramesOnLine);
680 :
681 0 : if ((aLineNumber < 0) || (aLineNumber >= mNumLines)) {
682 0 : *aFirstFrameOnLine = nullptr;
683 0 : *aNumFramesOnLine = 0;
684 8 : aLineBounds.SetRect(0, 0, 0, 0);
685 0 : return NS_OK;
686 : }
687 0 : nsLineBox* line = mLines[aLineNumber];
688 0 : *aFirstFrameOnLine = line->mFirstChild;
689 3 : *aNumFramesOnLine = line->GetChildCount();
690 0 : aLineBounds = line->GetPhysicalBounds();
691 :
692 3 : return NS_OK;
693 : }
694 :
695 : int32_t
696 0 : nsLineIterator::FindLineContaining(nsIFrame* aFrame, int32_t aStartLine)
697 : {
698 0 : MOZ_ASSERT(aStartLine <= mNumLines, "Bogus line numbers");
699 : int32_t lineNumber = aStartLine;
700 0 : while (lineNumber != mNumLines) {
701 0 : nsLineBox* line = mLines[lineNumber];
702 0 : if (line->Contains(aFrame)) {
703 : return lineNumber;
704 : }
705 0 : ++lineNumber;
706 : }
707 : return -1;
708 : }
709 :
710 : NS_IMETHODIMP
711 0 : nsLineIterator::CheckLineOrder(int32_t aLine,
712 : bool *aIsReordered,
713 : nsIFrame **aFirstVisual,
714 : nsIFrame **aLastVisual)
715 : {
716 0 : NS_ASSERTION (aLine >= 0 && aLine < mNumLines, "aLine out of range!");
717 0 : nsLineBox* line = mLines[aLine];
718 :
719 0 : if (!line->mFirstChild) { // empty line
720 0 : *aIsReordered = false;
721 0 : *aFirstVisual = nullptr;
722 0 : *aLastVisual = nullptr;
723 0 : return NS_OK;
724 : }
725 :
726 : nsIFrame* leftmostFrame;
727 : nsIFrame* rightmostFrame;
728 0 : *aIsReordered = nsBidiPresUtils::CheckLineOrder(line->mFirstChild, line->GetChildCount(), &leftmostFrame, &rightmostFrame);
729 :
730 : // map leftmost/rightmost to first/last according to paragraph direction
731 0 : *aFirstVisual = mRightToLeft ? rightmostFrame : leftmostFrame;
732 0 : *aLastVisual = mRightToLeft ? leftmostFrame : rightmostFrame;
733 :
734 0 : return NS_OK;
735 : }
736 :
737 : NS_IMETHODIMP
738 0 : nsLineIterator::FindFrameAt(int32_t aLineNumber,
739 : nsPoint aPos,
740 : nsIFrame** aFrameFound,
741 : bool* aPosIsBeforeFirstFrame,
742 : bool* aPosIsAfterLastFrame)
743 : {
744 0 : MOZ_ASSERT(aFrameFound && aPosIsBeforeFirstFrame && aPosIsAfterLastFrame,
745 : "null OUT ptr");
746 :
747 0 : if (!aFrameFound || !aPosIsBeforeFirstFrame || !aPosIsAfterLastFrame) {
748 : return NS_ERROR_NULL_POINTER;
749 : }
750 0 : if ((aLineNumber < 0) || (aLineNumber >= mNumLines)) {
751 : return NS_ERROR_INVALID_ARG;
752 : }
753 :
754 0 : nsLineBox* line = mLines[aLineNumber];
755 0 : if (!line) {
756 0 : *aFrameFound = nullptr;
757 0 : *aPosIsBeforeFirstFrame = true;
758 0 : *aPosIsAfterLastFrame = false;
759 0 : return NS_OK;
760 : }
761 :
762 0 : if (line->ISize() == 0 && line->BSize() == 0)
763 : return NS_ERROR_FAILURE;
764 :
765 0 : nsIFrame* frame = line->mFirstChild;
766 0 : nsIFrame* closestFromStart = nullptr;
767 0 : nsIFrame* closestFromEnd = nullptr;
768 :
769 0 : WritingMode wm = line->mWritingMode;
770 0 : nsSize containerSize = line->mContainerSize;
771 :
772 0 : LogicalPoint pos(wm, aPos, containerSize);
773 :
774 0 : int32_t n = line->GetChildCount();
775 0 : while (n--) {
776 0 : LogicalRect rect = frame->GetLogicalRect(wm, containerSize);
777 0 : if (rect.ISize(wm) > 0) {
778 : // If pos.I() is inside this frame - this is it
779 0 : if (rect.IStart(wm) <= pos.I(wm) && rect.IEnd(wm) > pos.I(wm)) {
780 0 : closestFromStart = closestFromEnd = frame;
781 0 : break;
782 : }
783 0 : if (rect.IStart(wm) < pos.I(wm)) {
784 0 : if (!closestFromStart ||
785 0 : rect.IEnd(wm) > closestFromStart->
786 0 : GetLogicalRect(wm, containerSize).IEnd(wm))
787 0 : closestFromStart = frame;
788 : }
789 : else {
790 0 : if (!closestFromEnd ||
791 0 : rect.IStart(wm) < closestFromEnd->
792 0 : GetLogicalRect(wm, containerSize).IStart(wm))
793 0 : closestFromEnd = frame;
794 : }
795 : }
796 0 : frame = frame->GetNextSibling();
797 : }
798 0 : if (!closestFromStart && !closestFromEnd) {
799 : // All frames were zero-width. Just take the first one.
800 0 : closestFromStart = closestFromEnd = line->mFirstChild;
801 : }
802 0 : *aPosIsBeforeFirstFrame = mRightToLeft ? !closestFromEnd : !closestFromStart;
803 0 : *aPosIsAfterLastFrame = mRightToLeft ? !closestFromStart : !closestFromEnd;
804 0 : if (closestFromStart == closestFromEnd) {
805 0 : *aFrameFound = closestFromStart;
806 : }
807 0 : else if (!closestFromStart) {
808 0 : *aFrameFound = closestFromEnd;
809 : }
810 0 : else if (!closestFromEnd) {
811 0 : *aFrameFound = closestFromStart;
812 : }
813 : else { // we're between two frames
814 : nscoord delta =
815 0 : closestFromEnd->GetLogicalRect(wm, containerSize).IStart(wm) -
816 0 : closestFromStart->GetLogicalRect(wm, containerSize).IEnd(wm);
817 0 : if (pos.I(wm) < closestFromStart->
818 0 : GetLogicalRect(wm, containerSize).IEnd(wm) + delta/2) {
819 0 : *aFrameFound = closestFromStart;
820 : } else {
821 0 : *aFrameFound = closestFromEnd;
822 : }
823 : }
824 : return NS_OK;
825 : }
826 :
827 : NS_IMETHODIMP
828 0 : nsLineIterator::GetNextSiblingOnLine(nsIFrame*& aFrame, int32_t aLineNumber)
829 : {
830 0 : aFrame = aFrame->GetNextSibling();
831 0 : return NS_OK;
832 : }
833 :
834 : //----------------------------------------------------------------------
835 :
836 : #ifdef NS_BUILD_REFCNT_LOGGING
837 142 : nsFloatCacheList::nsFloatCacheList() :
838 0 : mHead(nullptr)
839 : {
840 142 : MOZ_COUNT_CTOR(nsFloatCacheList);
841 142 : }
842 : #endif
843 :
844 0 : nsFloatCacheList::~nsFloatCacheList()
845 : {
846 0 : DeleteAll();
847 138 : MOZ_COUNT_DTOR(nsFloatCacheList);
848 138 : }
849 :
850 : void
851 0 : nsFloatCacheList::DeleteAll()
852 : {
853 0 : nsFloatCache* c = mHead;
854 0 : while (c) {
855 0 : nsFloatCache* next = c->Next();
856 0 : delete c;
857 0 : c = next;
858 : }
859 138 : mHead = nullptr;
860 138 : }
861 :
862 : nsFloatCache*
863 0 : nsFloatCacheList::Tail() const
864 : {
865 0 : nsFloatCache* fc = mHead;
866 0 : while (fc) {
867 0 : if (!fc->mNext) {
868 : break;
869 : }
870 : fc = fc->mNext;
871 : }
872 0 : return fc;
873 : }
874 :
875 : void
876 0 : nsFloatCacheList::Append(nsFloatCacheFreeList& aList)
877 : {
878 0 : MOZ_ASSERT(aList.NotEmpty(), "Appending empty list will fail");
879 :
880 0 : nsFloatCache* tail = Tail();
881 0 : if (tail) {
882 0 : NS_ASSERTION(!tail->mNext, "Bogus!");
883 0 : tail->mNext = aList.mHead;
884 : }
885 : else {
886 0 : NS_ASSERTION(!mHead, "Bogus!");
887 0 : mHead = aList.mHead;
888 : }
889 0 : aList.mHead = nullptr;
890 0 : aList.mTail = nullptr;
891 0 : }
892 :
893 : nsFloatCache*
894 0 : nsFloatCacheList::Find(nsIFrame* aOutOfFlowFrame)
895 : {
896 0 : nsFloatCache* fc = mHead;
897 0 : while (fc) {
898 0 : if (fc->mFloat == aOutOfFlowFrame) {
899 : break;
900 : }
901 0 : fc = fc->Next();
902 : }
903 0 : return fc;
904 : }
905 :
906 : nsFloatCache*
907 0 : nsFloatCacheList::RemoveAndReturnPrev(nsFloatCache* aElement)
908 : {
909 0 : nsFloatCache* fc = mHead;
910 0 : nsFloatCache* prev = nullptr;
911 0 : while (fc) {
912 0 : if (fc == aElement) {
913 0 : if (prev) {
914 0 : prev->mNext = fc->mNext;
915 : } else {
916 0 : mHead = fc->mNext;
917 : }
918 : return prev;
919 : }
920 0 : prev = fc;
921 0 : fc = fc->mNext;
922 : }
923 : return nullptr;
924 : }
925 :
926 : //----------------------------------------------------------------------
927 :
928 : #ifdef NS_BUILD_REFCNT_LOGGING
929 138 : nsFloatCacheFreeList::nsFloatCacheFreeList() :
930 0 : mTail(nullptr)
931 : {
932 138 : MOZ_COUNT_CTOR(nsFloatCacheFreeList);
933 0 : }
934 :
935 0 : nsFloatCacheFreeList::~nsFloatCacheFreeList()
936 : {
937 138 : MOZ_COUNT_DTOR(nsFloatCacheFreeList);
938 138 : }
939 : #endif
940 :
941 : void
942 0 : nsFloatCacheFreeList::Append(nsFloatCacheList& aList)
943 : {
944 0 : MOZ_ASSERT(aList.NotEmpty(), "Appending empty list will fail");
945 :
946 0 : if (mTail) {
947 0 : NS_ASSERTION(!mTail->mNext, "Bogus");
948 0 : mTail->mNext = aList.mHead;
949 : }
950 : else {
951 0 : NS_ASSERTION(!mHead, "Bogus");
952 0 : mHead = aList.mHead;
953 : }
954 0 : mTail = aList.Tail();
955 0 : aList.mHead = nullptr;
956 0 : }
957 :
958 : void
959 0 : nsFloatCacheFreeList::Remove(nsFloatCache* aElement)
960 : {
961 0 : nsFloatCache* prev = nsFloatCacheList::RemoveAndReturnPrev(aElement);
962 0 : if (mTail == aElement) {
963 0 : mTail = prev;
964 : }
965 0 : }
966 :
967 : void
968 0 : nsFloatCacheFreeList::DeleteAll()
969 : {
970 0 : nsFloatCacheList::DeleteAll();
971 0 : mTail = nullptr;
972 0 : }
973 :
974 : nsFloatCache*
975 0 : nsFloatCacheFreeList::Alloc(nsIFrame* aFloat)
976 : {
977 0 : MOZ_ASSERT(aFloat->GetStateBits() & NS_FRAME_OUT_OF_FLOW,
978 : "This is a float cache, why isn't the frame out-of-flow?");
979 :
980 0 : nsFloatCache* fc = mHead;
981 0 : if (mHead) {
982 0 : if (mHead == mTail) {
983 0 : mHead = mTail = nullptr;
984 : }
985 : else {
986 0 : mHead = fc->mNext;
987 : }
988 0 : fc->mNext = nullptr;
989 : }
990 : else {
991 0 : fc = new nsFloatCache();
992 : }
993 0 : fc->mFloat = aFloat;
994 0 : return fc;
995 : }
996 :
997 : void
998 0 : nsFloatCacheFreeList::Append(nsFloatCache* aFloat)
999 : {
1000 0 : NS_ASSERTION(!aFloat->mNext, "Bogus!");
1001 0 : aFloat->mNext = nullptr;
1002 0 : if (mTail) {
1003 0 : NS_ASSERTION(!mTail->mNext, "Bogus!");
1004 0 : mTail->mNext = aFloat;
1005 0 : mTail = aFloat;
1006 : }
1007 : else {
1008 0 : NS_ASSERTION(!mHead, "Bogus!");
1009 0 : mHead = mTail = aFloat;
1010 : }
1011 0 : }
1012 :
1013 : //----------------------------------------------------------------------
1014 :
1015 0 : nsFloatCache::nsFloatCache()
1016 : : mFloat(nullptr),
1017 0 : mNext(nullptr)
1018 : {
1019 0 : MOZ_COUNT_CTOR(nsFloatCache);
1020 0 : }
1021 :
1022 : #ifdef NS_BUILD_REFCNT_LOGGING
1023 0 : nsFloatCache::~nsFloatCache()
1024 : {
1025 : MOZ_COUNT_DTOR(nsFloatCache);
1026 : }
1027 : #endif
|