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