layout/generic/nsLineBox.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/layout/generic/nsLineBox.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,988 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +// vim:cindent:ts=2:et:sw=2:
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +/* representation of one line within a block frame, a CSS line box */
    1.11 +
    1.12 +#include "nsLineBox.h"
    1.13 +#include "prprf.h"
    1.14 +#include "nsFrame.h"
    1.15 +#include "nsPresArena.h"
    1.16 +#include "nsBidiPresUtils.h"
    1.17 +#include "nsIFrameInlines.h"
    1.18 +#include "WritingModes.h"
    1.19 +#include "mozilla/Assertions.h"
    1.20 +#include "mozilla/Likely.h"
    1.21 +#include "nsPrintfCString.h"
    1.22 +
    1.23 +#ifdef DEBUG
    1.24 +static int32_t ctorCount;
    1.25 +int32_t nsLineBox::GetCtorCount() { return ctorCount; }
    1.26 +#endif
    1.27 +
    1.28 +#ifndef _MSC_VER
    1.29 +// static nsLineBox constant; initialized in the header file.
    1.30 +const uint32_t nsLineBox::kMinChildCountForHashtable;
    1.31 +#endif
    1.32 +
    1.33 +using namespace mozilla;
    1.34 +
    1.35 +nsLineBox::nsLineBox(nsIFrame* aFrame, int32_t aCount, bool aIsBlock)
    1.36 +  : mFirstChild(aFrame)
    1.37 +  , mContainerWidth(-1)
    1.38 +  , mBounds(WritingMode()) // mBounds will be initialized with the correct
    1.39 +                           // writing mode when it is set
    1.40 +// NOTE: memory is already zeroed since we allocate with AllocateByObjectID.
    1.41 +{
    1.42 +  MOZ_COUNT_CTOR(nsLineBox);
    1.43 +#ifdef DEBUG
    1.44 +  ++ctorCount;
    1.45 +  NS_ASSERTION(!aIsBlock || aCount == 1, "Blocks must have exactly one child");
    1.46 +  nsIFrame* f = aFrame;
    1.47 +  for (int32_t n = aCount; n > 0; f = f->GetNextSibling(), --n) {
    1.48 +    NS_ASSERTION(aIsBlock == f->IsBlockOutside(),
    1.49 +                 "wrong kind of child frame");
    1.50 +  }
    1.51 +#endif
    1.52 +
    1.53 +  static_assert(NS_STYLE_CLEAR_MAX <= 15,
    1.54 +                "FlagBits needs more bits to store the full range of "
    1.55 +                "break type ('clear') values");
    1.56 +#if NS_STYLE_CLEAR_NONE > 0
    1.57 +  mFlags.mBreakType = NS_STYLE_CLEAR_NONE;
    1.58 +#endif
    1.59 +  mChildCount = aCount;
    1.60 +  MarkDirty();
    1.61 +  mFlags.mBlock = aIsBlock;
    1.62 +}
    1.63 +
    1.64 +nsLineBox::~nsLineBox()
    1.65 +{
    1.66 +  MOZ_COUNT_DTOR(nsLineBox);
    1.67 +  if (MOZ_UNLIKELY(mFlags.mHasHashedFrames)) {
    1.68 +    delete mFrames;
    1.69 +  }  
    1.70 +  Cleanup();
    1.71 +}
    1.72 +
    1.73 +nsLineBox*
    1.74 +NS_NewLineBox(nsIPresShell* aPresShell, nsIFrame* aFrame, bool aIsBlock)
    1.75 +{
    1.76 +  return new (aPresShell) nsLineBox(aFrame, 1, aIsBlock);
    1.77 +}
    1.78 +
    1.79 +nsLineBox*
    1.80 +NS_NewLineBox(nsIPresShell* aPresShell, nsLineBox* aFromLine,
    1.81 +              nsIFrame* aFrame, int32_t aCount)
    1.82 +{
    1.83 +  nsLineBox* newLine = new (aPresShell) nsLineBox(aFrame, aCount, false);
    1.84 +  newLine->NoteFramesMovedFrom(aFromLine);
    1.85 +  newLine->mContainerWidth = aFromLine->mContainerWidth;
    1.86 +  return newLine;
    1.87 +}
    1.88 +
    1.89 +void
    1.90 +nsLineBox::StealHashTableFrom(nsLineBox* aFromLine, uint32_t aFromLineNewCount)
    1.91 +{
    1.92 +  MOZ_ASSERT(!mFlags.mHasHashedFrames);
    1.93 +  MOZ_ASSERT(GetChildCount() >= int32_t(aFromLineNewCount));
    1.94 +  mFrames = aFromLine->mFrames;
    1.95 +  mFlags.mHasHashedFrames = 1;
    1.96 +  aFromLine->mFlags.mHasHashedFrames = 0;
    1.97 +  aFromLine->mChildCount = aFromLineNewCount;
    1.98 +  // remove aFromLine's frames that aren't on this line
    1.99 +  nsIFrame* f = aFromLine->mFirstChild;
   1.100 +  for (uint32_t i = 0; i < aFromLineNewCount; f = f->GetNextSibling(), ++i) {
   1.101 +    mFrames->RemoveEntry(f);
   1.102 +  }
   1.103 +}
   1.104 +
   1.105 +void
   1.106 +nsLineBox::NoteFramesMovedFrom(nsLineBox* aFromLine)
   1.107 +{
   1.108 +  uint32_t fromCount = aFromLine->GetChildCount();
   1.109 +  uint32_t toCount = GetChildCount();
   1.110 +  MOZ_ASSERT(toCount <= fromCount, "moved more frames than aFromLine has");
   1.111 +  uint32_t fromNewCount = fromCount - toCount;
   1.112 +  if (MOZ_LIKELY(!aFromLine->mFlags.mHasHashedFrames)) {
   1.113 +    aFromLine->mChildCount = fromNewCount;
   1.114 +    MOZ_ASSERT(toCount < kMinChildCountForHashtable);
   1.115 +  } else if (fromNewCount < kMinChildCountForHashtable) {
   1.116 +    // aFromLine has a hash table but will not have it after moving the frames
   1.117 +    // so this line can steal the hash table if it needs it.
   1.118 +    if (toCount >= kMinChildCountForHashtable) {
   1.119 +      StealHashTableFrom(aFromLine, fromNewCount);
   1.120 +    } else {
   1.121 +      delete aFromLine->mFrames;
   1.122 +      aFromLine->mFlags.mHasHashedFrames = 0;
   1.123 +      aFromLine->mChildCount = fromNewCount;
   1.124 +    }
   1.125 +  } else {
   1.126 +    // aFromLine still needs a hash table.
   1.127 +    if (toCount < kMinChildCountForHashtable) {
   1.128 +      // remove the moved frames from it
   1.129 +      nsIFrame* f = mFirstChild;
   1.130 +      for (uint32_t i = 0; i < toCount; f = f->GetNextSibling(), ++i) {
   1.131 +        aFromLine->mFrames->RemoveEntry(f);
   1.132 +      }
   1.133 +    } else if (toCount <= fromNewCount) {
   1.134 +      // This line needs a hash table, allocate a hash table for it since that
   1.135 +      // means fewer hash ops.
   1.136 +      nsIFrame* f = mFirstChild;
   1.137 +      for (uint32_t i = 0; i < toCount; f = f->GetNextSibling(), ++i) {
   1.138 +        aFromLine->mFrames->RemoveEntry(f); // toCount RemoveEntry
   1.139 +      }
   1.140 +      SwitchToHashtable(); // toCount PutEntry
   1.141 +    } else {
   1.142 +      // This line needs a hash table, but it's fewer hash ops to steal
   1.143 +      // aFromLine's hash table and allocate a new hash table for that line.
   1.144 +      StealHashTableFrom(aFromLine, fromNewCount); // fromNewCount RemoveEntry
   1.145 +      aFromLine->SwitchToHashtable(); // fromNewCount PutEntry
   1.146 +    }
   1.147 +  }
   1.148 +}
   1.149 +
   1.150 +// Overloaded new operator. Uses an arena (which comes from the presShell)
   1.151 +// to perform the allocation.
   1.152 +void*
   1.153 +nsLineBox::operator new(size_t sz, nsIPresShell* aPresShell) CPP_THROW_NEW
   1.154 +{
   1.155 +  return aPresShell->AllocateByObjectID(nsPresArena::nsLineBox_id, sz);
   1.156 +}
   1.157 +
   1.158 +void
   1.159 +nsLineBox::Destroy(nsIPresShell* aPresShell)
   1.160 +{
   1.161 +  this->nsLineBox::~nsLineBox();
   1.162 +  aPresShell->FreeByObjectID(nsPresArena::nsLineBox_id, this);
   1.163 +}
   1.164 +
   1.165 +void
   1.166 +nsLineBox::Cleanup()
   1.167 +{
   1.168 +  if (mData) {
   1.169 +    if (IsBlock()) {
   1.170 +      delete mBlockData;
   1.171 +    }
   1.172 +    else {
   1.173 +      delete mInlineData;
   1.174 +    }
   1.175 +    mData = nullptr;
   1.176 +  }
   1.177 +}
   1.178 +
   1.179 +#ifdef DEBUG_FRAME_DUMP
   1.180 +static void
   1.181 +ListFloats(FILE* out, const char* aPrefix, const nsFloatCacheList& aFloats)
   1.182 +{
   1.183 +  nsFloatCache* fc = aFloats.Head();
   1.184 +  while (fc) {
   1.185 +    nsCString str(aPrefix);
   1.186 +    nsIFrame* frame = fc->mFloat;
   1.187 +    str += nsPrintfCString("floatframe@%p ", static_cast<void*>(frame));
   1.188 +    if (frame) {
   1.189 +      nsAutoString frameName;
   1.190 +      frame->GetFrameName(frameName);
   1.191 +      str += NS_ConvertUTF16toUTF8(frameName).get();
   1.192 +    }
   1.193 +    else {
   1.194 +      str += "\n###!!! NULL out-of-flow frame";
   1.195 +    }
   1.196 +    fprintf_stderr(out, "%s\n", str.get());
   1.197 +    fc = fc->Next();
   1.198 +  }
   1.199 +}
   1.200 +
   1.201 +const char *
   1.202 +BreakTypeToString(uint8_t aBreakType)
   1.203 +{
   1.204 +  switch (aBreakType) {
   1.205 +  case NS_STYLE_CLEAR_NONE: return "nobr";
   1.206 +  case NS_STYLE_CLEAR_LEFT: return "leftbr";
   1.207 +  case NS_STYLE_CLEAR_RIGHT: return "rightbr";
   1.208 +  case NS_STYLE_CLEAR_BOTH: return "leftbr+rightbr";
   1.209 +  case NS_STYLE_CLEAR_LINE: return "linebr";
   1.210 +  default:
   1.211 +    break;
   1.212 +  }
   1.213 +  return "unknown";
   1.214 +}
   1.215 +
   1.216 +char*
   1.217 +nsLineBox::StateToString(char* aBuf, int32_t aBufSize) const
   1.218 +{
   1.219 +  PR_snprintf(aBuf, aBufSize, "%s,%s,%s,%s,%s,before:%s,after:%s[0x%x]",
   1.220 +              IsBlock() ? "block" : "inline",
   1.221 +              IsDirty() ? "dirty" : "clean",
   1.222 +              IsPreviousMarginDirty() ? "prevmargindirty" : "prevmarginclean",
   1.223 +              IsImpactedByFloat() ? "impacted" : "not impacted",
   1.224 +              IsLineWrapped() ? "wrapped" : "not wrapped",
   1.225 +              BreakTypeToString(GetBreakTypeBefore()),
   1.226 +              BreakTypeToString(GetBreakTypeAfter()),
   1.227 +              mAllFlags);
   1.228 +  return aBuf;
   1.229 +}
   1.230 +
   1.231 +void
   1.232 +nsLineBox::List(FILE* out, int32_t aIndent, uint32_t aFlags) const
   1.233 +{
   1.234 +  nsCString str;
   1.235 +  while (aIndent-- > 0) {
   1.236 +    str += "  ";
   1.237 +  }
   1.238 +  List(out, str.get(), aFlags);
   1.239 +}
   1.240 +
   1.241 +void
   1.242 +nsLineBox::List(FILE* out, const char* aPrefix, uint32_t aFlags) const
   1.243 +{
   1.244 +  nsCString str(aPrefix);
   1.245 +  char cbuf[100];
   1.246 +  str += nsPrintfCString("line %p: count=%d state=%s ",
   1.247 +          static_cast<const void*>(this), GetChildCount(),
   1.248 +          StateToString(cbuf, sizeof(cbuf)));
   1.249 +  if (IsBlock() && !GetCarriedOutBottomMargin().IsZero()) {
   1.250 +    str += nsPrintfCString("bm=%d ", GetCarriedOutBottomMargin().get());
   1.251 +  }
   1.252 +  nsRect bounds = GetPhysicalBounds();
   1.253 +  str += nsPrintfCString("{%d,%d,%d,%d} ",
   1.254 +          bounds.x, bounds.y, bounds.width, bounds.height);
   1.255 +  if (mData &&
   1.256 +      (!mData->mOverflowAreas.VisualOverflow().IsEqualEdges(bounds) ||
   1.257 +       !mData->mOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds))) {
   1.258 +    str += nsPrintfCString("vis-overflow=%d,%d,%d,%d scr-overflow=%d,%d,%d,%d ",
   1.259 +            mData->mOverflowAreas.VisualOverflow().x,
   1.260 +            mData->mOverflowAreas.VisualOverflow().y,
   1.261 +            mData->mOverflowAreas.VisualOverflow().width,
   1.262 +            mData->mOverflowAreas.VisualOverflow().height,
   1.263 +            mData->mOverflowAreas.ScrollableOverflow().x,
   1.264 +            mData->mOverflowAreas.ScrollableOverflow().y,
   1.265 +            mData->mOverflowAreas.ScrollableOverflow().width,
   1.266 +            mData->mOverflowAreas.ScrollableOverflow().height);
   1.267 +  }
   1.268 +  fprintf_stderr(out, "%s<\n", str.get());
   1.269 +
   1.270 +  nsIFrame* frame = mFirstChild;
   1.271 +  int32_t n = GetChildCount();
   1.272 +  nsCString pfx(aPrefix);
   1.273 +  pfx += "  ";
   1.274 +  while (--n >= 0) {
   1.275 +    frame->List(out, pfx.get(), aFlags);
   1.276 +    frame = frame->GetNextSibling();
   1.277 +  }
   1.278 +
   1.279 +  if (HasFloats()) {
   1.280 +    fprintf_stderr(out, "%s> floats <\n", aPrefix);
   1.281 +    ListFloats(out, pfx.get(), mInlineData->mFloats);
   1.282 +  }
   1.283 +  fprintf_stderr(out, "%s>\n", aPrefix);
   1.284 +}
   1.285 +#endif
   1.286 +
   1.287 +#ifdef DEBUG
   1.288 +nsIFrame*
   1.289 +nsLineBox::LastChild() const
   1.290 +{
   1.291 +  nsIFrame* frame = mFirstChild;
   1.292 +  int32_t n = GetChildCount() - 1;
   1.293 +  while (--n >= 0) {
   1.294 +    frame = frame->GetNextSibling();
   1.295 +  }
   1.296 +  return frame;
   1.297 +}
   1.298 +#endif
   1.299 +
   1.300 +int32_t
   1.301 +nsLineBox::IndexOf(nsIFrame* aFrame) const
   1.302 +{
   1.303 +  int32_t i, n = GetChildCount();
   1.304 +  nsIFrame* frame = mFirstChild;
   1.305 +  for (i = 0; i < n; i++) {
   1.306 +    if (frame == aFrame) {
   1.307 +      return i;
   1.308 +    }
   1.309 +    frame = frame->GetNextSibling();
   1.310 +  }
   1.311 +  return -1;
   1.312 +}
   1.313 +
   1.314 +bool
   1.315 +nsLineBox::IsEmpty() const
   1.316 +{
   1.317 +  if (IsBlock())
   1.318 +    return mFirstChild->IsEmpty();
   1.319 +
   1.320 +  int32_t n;
   1.321 +  nsIFrame *kid;
   1.322 +  for (n = GetChildCount(), kid = mFirstChild;
   1.323 +       n > 0;
   1.324 +       --n, kid = kid->GetNextSibling())
   1.325 +  {
   1.326 +    if (!kid->IsEmpty())
   1.327 +      return false;
   1.328 +  }
   1.329 +  if (HasBullet()) {
   1.330 +    return false;
   1.331 +  }
   1.332 +  return true;
   1.333 +}
   1.334 +
   1.335 +bool
   1.336 +nsLineBox::CachedIsEmpty()
   1.337 +{
   1.338 +  if (mFlags.mDirty) {
   1.339 +    return IsEmpty();
   1.340 +  }
   1.341 +  
   1.342 +  if (mFlags.mEmptyCacheValid) {
   1.343 +    return mFlags.mEmptyCacheState;
   1.344 +  }
   1.345 +
   1.346 +  bool result;
   1.347 +  if (IsBlock()) {
   1.348 +    result = mFirstChild->CachedIsEmpty();
   1.349 +  } else {
   1.350 +    int32_t n;
   1.351 +    nsIFrame *kid;
   1.352 +    result = true;
   1.353 +    for (n = GetChildCount(), kid = mFirstChild;
   1.354 +         n > 0;
   1.355 +         --n, kid = kid->GetNextSibling())
   1.356 +      {
   1.357 +        if (!kid->CachedIsEmpty()) {
   1.358 +          result = false;
   1.359 +          break;
   1.360 +        }
   1.361 +      }
   1.362 +    if (HasBullet()) {
   1.363 +      result = false;
   1.364 +    }
   1.365 +  }
   1.366 +
   1.367 +  mFlags.mEmptyCacheValid = true;
   1.368 +  mFlags.mEmptyCacheState = result;
   1.369 +  return result;
   1.370 +}
   1.371 +
   1.372 +void
   1.373 +nsLineBox::DeleteLineList(nsPresContext* aPresContext, nsLineList& aLines,
   1.374 +                          nsIFrame* aDestructRoot, nsFrameList* aFrames)
   1.375 +{
   1.376 +  nsIPresShell* shell = aPresContext->PresShell();
   1.377 +
   1.378 +  // Keep our line list and frame list up to date as we
   1.379 +  // remove frames, in case something wants to traverse the
   1.380 +  // frame tree while we're destroying.
   1.381 +  while (!aLines.empty()) {
   1.382 +    nsLineBox* line = aLines.front();
   1.383 +    if (MOZ_UNLIKELY(line->mFlags.mHasHashedFrames)) {
   1.384 +      line->SwitchToCounter();  // Avoid expensive has table removals.
   1.385 +    }
   1.386 +    while (line->GetChildCount() > 0) {
   1.387 +      nsIFrame* child = aFrames->RemoveFirstChild();
   1.388 +      MOZ_ASSERT(child == line->mFirstChild, "Lines out of sync");
   1.389 +      line->mFirstChild = aFrames->FirstChild();
   1.390 +      line->NoteFrameRemoved(child);
   1.391 +      child->DestroyFrom(aDestructRoot);
   1.392 +    }
   1.393 +
   1.394 +    aLines.pop_front();
   1.395 +    line->Destroy(shell);
   1.396 +  }
   1.397 +}
   1.398 +
   1.399 +bool
   1.400 +nsLineBox::RFindLineContaining(nsIFrame* aFrame,
   1.401 +                               const nsLineList::iterator& aBegin,
   1.402 +                               nsLineList::iterator& aEnd,
   1.403 +                               nsIFrame* aLastFrameBeforeEnd,
   1.404 +                               int32_t* aFrameIndexInLine)
   1.405 +{
   1.406 +  NS_PRECONDITION(aFrame, "null ptr");
   1.407 +
   1.408 +  nsIFrame* curFrame = aLastFrameBeforeEnd;
   1.409 +  while (aBegin != aEnd) {
   1.410 +    --aEnd;
   1.411 +    NS_ASSERTION(aEnd->LastChild() == curFrame, "Unexpected curFrame");
   1.412 +    if (MOZ_UNLIKELY(aEnd->mFlags.mHasHashedFrames) &&
   1.413 +        !aEnd->Contains(aFrame)) {
   1.414 +      if (aEnd->mFirstChild) {
   1.415 +        curFrame = aEnd->mFirstChild->GetPrevSibling();
   1.416 +      }
   1.417 +      continue;
   1.418 +    }
   1.419 +    // i is the index of curFrame in aEnd
   1.420 +    int32_t i = aEnd->GetChildCount() - 1;
   1.421 +    while (i >= 0) {
   1.422 +      if (curFrame == aFrame) {
   1.423 +        *aFrameIndexInLine = i;
   1.424 +        return true;
   1.425 +      }
   1.426 +      --i;
   1.427 +      curFrame = curFrame->GetPrevSibling();
   1.428 +    }
   1.429 +    MOZ_ASSERT(!aEnd->mFlags.mHasHashedFrames, "Contains lied to us!");
   1.430 +  }
   1.431 +  *aFrameIndexInLine = -1;
   1.432 +  return false;
   1.433 +}
   1.434 +
   1.435 +nsCollapsingMargin
   1.436 +nsLineBox::GetCarriedOutBottomMargin() const
   1.437 +{
   1.438 +  NS_ASSERTION(IsBlock(),
   1.439 +               "GetCarriedOutBottomMargin called on non-block line.");
   1.440 +  return (IsBlock() && mBlockData)
   1.441 +    ? mBlockData->mCarriedOutBottomMargin
   1.442 +    : nsCollapsingMargin();
   1.443 +}
   1.444 +
   1.445 +bool
   1.446 +nsLineBox::SetCarriedOutBottomMargin(nsCollapsingMargin aValue)
   1.447 +{
   1.448 +  bool changed = false;
   1.449 +  if (IsBlock()) {
   1.450 +    if (!aValue.IsZero()) {
   1.451 +      if (!mBlockData) {
   1.452 +        mBlockData = new ExtraBlockData(GetPhysicalBounds());
   1.453 +      }
   1.454 +      changed = aValue != mBlockData->mCarriedOutBottomMargin;
   1.455 +      mBlockData->mCarriedOutBottomMargin = aValue;
   1.456 +    }
   1.457 +    else if (mBlockData) {
   1.458 +      changed = aValue != mBlockData->mCarriedOutBottomMargin;
   1.459 +      mBlockData->mCarriedOutBottomMargin = aValue;
   1.460 +      MaybeFreeData();
   1.461 +    }
   1.462 +  }
   1.463 +  return changed;
   1.464 +}
   1.465 +
   1.466 +void
   1.467 +nsLineBox::MaybeFreeData()
   1.468 +{
   1.469 +  nsRect bounds = GetPhysicalBounds();
   1.470 +  if (mData && mData->mOverflowAreas == nsOverflowAreas(bounds, bounds)) {
   1.471 +    if (IsInline()) {
   1.472 +      if (mInlineData->mFloats.IsEmpty()) {
   1.473 +        delete mInlineData;
   1.474 +        mInlineData = nullptr;
   1.475 +      }
   1.476 +    }
   1.477 +    else if (mBlockData->mCarriedOutBottomMargin.IsZero()) {
   1.478 +      delete mBlockData;
   1.479 +      mBlockData = nullptr;
   1.480 +    }
   1.481 +  }
   1.482 +}
   1.483 +
   1.484 +// XXX get rid of this???
   1.485 +nsFloatCache*
   1.486 +nsLineBox::GetFirstFloat()
   1.487 +{
   1.488 +  NS_ABORT_IF_FALSE(IsInline(), "block line can't have floats");
   1.489 +  return mInlineData ? mInlineData->mFloats.Head() : nullptr;
   1.490 +}
   1.491 +
   1.492 +// XXX this might be too eager to free memory
   1.493 +void
   1.494 +nsLineBox::FreeFloats(nsFloatCacheFreeList& aFreeList)
   1.495 +{
   1.496 +  NS_ABORT_IF_FALSE(IsInline(), "block line can't have floats");
   1.497 +  if (IsInline() && mInlineData) {
   1.498 +    if (mInlineData->mFloats.NotEmpty()) {
   1.499 +      aFreeList.Append(mInlineData->mFloats);
   1.500 +    }
   1.501 +    MaybeFreeData();
   1.502 +  }
   1.503 +}
   1.504 +
   1.505 +void
   1.506 +nsLineBox::AppendFloats(nsFloatCacheFreeList& aFreeList)
   1.507 +{ 
   1.508 +  NS_ABORT_IF_FALSE(IsInline(), "block line can't have floats");
   1.509 +  if (IsInline()) {
   1.510 +    if (aFreeList.NotEmpty()) {
   1.511 +      if (!mInlineData) {
   1.512 +        mInlineData = new ExtraInlineData(GetPhysicalBounds());
   1.513 +      }
   1.514 +      mInlineData->mFloats.Append(aFreeList);
   1.515 +    }
   1.516 +  }
   1.517 +}
   1.518 +
   1.519 +bool
   1.520 +nsLineBox::RemoveFloat(nsIFrame* aFrame)
   1.521 +{
   1.522 +  NS_ABORT_IF_FALSE(IsInline(), "block line can't have floats");
   1.523 +  if (IsInline() && mInlineData) {
   1.524 +    nsFloatCache* fc = mInlineData->mFloats.Find(aFrame);
   1.525 +    if (fc) {
   1.526 +      // Note: the placeholder is part of the line's child list
   1.527 +      // and will be removed later.
   1.528 +      mInlineData->mFloats.Remove(fc);
   1.529 +      delete fc;
   1.530 +      MaybeFreeData();
   1.531 +      return true;
   1.532 +    }
   1.533 +  }
   1.534 +  return false;
   1.535 +}
   1.536 +
   1.537 +void
   1.538 +nsLineBox::SetOverflowAreas(const nsOverflowAreas& aOverflowAreas)
   1.539 +{
   1.540 +  NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
   1.541 +    NS_ASSERTION(aOverflowAreas.Overflow(otype).width >= 0,
   1.542 +                 "illegal width for combined area");
   1.543 +    NS_ASSERTION(aOverflowAreas.Overflow(otype).height >= 0,
   1.544 +                 "illegal height for combined area");
   1.545 +  }
   1.546 +  nsRect bounds = GetPhysicalBounds();
   1.547 +  if (!aOverflowAreas.VisualOverflow().IsEqualInterior(bounds) ||
   1.548 +      !aOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds)) {
   1.549 +    if (!mData) {
   1.550 +      if (IsInline()) {
   1.551 +        mInlineData = new ExtraInlineData(bounds);
   1.552 +      }
   1.553 +      else {
   1.554 +        mBlockData = new ExtraBlockData(bounds);
   1.555 +      }
   1.556 +    }
   1.557 +    mData->mOverflowAreas = aOverflowAreas;
   1.558 +  }
   1.559 +  else if (mData) {
   1.560 +    // Store away new value so that MaybeFreeData compares against
   1.561 +    // the right value.
   1.562 +    mData->mOverflowAreas = aOverflowAreas;
   1.563 +    MaybeFreeData();
   1.564 +  }
   1.565 +}
   1.566 +
   1.567 +//----------------------------------------------------------------------
   1.568 +
   1.569 +
   1.570 +static nsLineBox* gDummyLines[1];
   1.571 +
   1.572 +nsLineIterator::nsLineIterator()
   1.573 +{
   1.574 +  mLines = gDummyLines;
   1.575 +  mNumLines = 0;
   1.576 +  mIndex = 0;
   1.577 +  mRightToLeft = false;
   1.578 +}
   1.579 +
   1.580 +nsLineIterator::~nsLineIterator()
   1.581 +{
   1.582 +  if (mLines != gDummyLines) {
   1.583 +    delete [] mLines;
   1.584 +  }
   1.585 +}
   1.586 +
   1.587 +/* virtual */ void
   1.588 +nsLineIterator::DisposeLineIterator()
   1.589 +{
   1.590 +  delete this;
   1.591 +}
   1.592 +
   1.593 +nsresult
   1.594 +nsLineIterator::Init(nsLineList& aLines, bool aRightToLeft)
   1.595 +{
   1.596 +  mRightToLeft = aRightToLeft;
   1.597 +
   1.598 +  // Count the lines
   1.599 +  int32_t numLines = aLines.size();
   1.600 +  if (0 == numLines) {
   1.601 +    // Use gDummyLines so that we don't need null pointer checks in
   1.602 +    // the accessor methods
   1.603 +    mLines = gDummyLines;
   1.604 +    return NS_OK;
   1.605 +  }
   1.606 +
   1.607 +  // Make a linear array of the lines
   1.608 +  mLines = new nsLineBox*[numLines];
   1.609 +  if (!mLines) {
   1.610 +    // Use gDummyLines so that we don't need null pointer checks in
   1.611 +    // the accessor methods
   1.612 +    mLines = gDummyLines;
   1.613 +    return NS_ERROR_OUT_OF_MEMORY;
   1.614 +  }
   1.615 +  nsLineBox** lp = mLines;
   1.616 +  for (nsLineList::iterator line = aLines.begin(), line_end = aLines.end() ;
   1.617 +       line != line_end;
   1.618 +       ++line)
   1.619 +  {
   1.620 +    *lp++ = line;
   1.621 +  }
   1.622 +  mNumLines = numLines;
   1.623 +  return NS_OK;
   1.624 +}
   1.625 +
   1.626 +int32_t
   1.627 +nsLineIterator::GetNumLines()
   1.628 +{
   1.629 +  return mNumLines;
   1.630 +}
   1.631 +
   1.632 +bool
   1.633 +nsLineIterator::GetDirection()
   1.634 +{
   1.635 +  return mRightToLeft;
   1.636 +}
   1.637 +
   1.638 +NS_IMETHODIMP
   1.639 +nsLineIterator::GetLine(int32_t aLineNumber,
   1.640 +                        nsIFrame** aFirstFrameOnLine,
   1.641 +                        int32_t* aNumFramesOnLine,
   1.642 +                        nsRect& aLineBounds,
   1.643 +                        uint32_t* aLineFlags)
   1.644 +{
   1.645 +  NS_ENSURE_ARG_POINTER(aFirstFrameOnLine);
   1.646 +  NS_ENSURE_ARG_POINTER(aNumFramesOnLine);
   1.647 +  NS_ENSURE_ARG_POINTER(aLineFlags);
   1.648 +
   1.649 +  if ((aLineNumber < 0) || (aLineNumber >= mNumLines)) {
   1.650 +    *aFirstFrameOnLine = nullptr;
   1.651 +    *aNumFramesOnLine = 0;
   1.652 +    aLineBounds.SetRect(0, 0, 0, 0);
   1.653 +    return NS_OK;
   1.654 +  }
   1.655 +  nsLineBox* line = mLines[aLineNumber];
   1.656 +  *aFirstFrameOnLine = line->mFirstChild;
   1.657 +  *aNumFramesOnLine = line->GetChildCount();
   1.658 +  aLineBounds = line->GetPhysicalBounds();
   1.659 +
   1.660 +  uint32_t flags = 0;
   1.661 +  if (line->IsBlock()) {
   1.662 +    flags |= NS_LINE_FLAG_IS_BLOCK;
   1.663 +  }
   1.664 +  else {
   1.665 +    if (line->HasBreakAfter())
   1.666 +      flags |= NS_LINE_FLAG_ENDS_IN_BREAK;
   1.667 +  }
   1.668 +  *aLineFlags = flags;
   1.669 +
   1.670 +  return NS_OK;
   1.671 +}
   1.672 +
   1.673 +int32_t
   1.674 +nsLineIterator::FindLineContaining(nsIFrame* aFrame, int32_t aStartLine)
   1.675 +{
   1.676 +  NS_PRECONDITION(aStartLine <= mNumLines, "Bogus line numbers");
   1.677 +  int32_t lineNumber = aStartLine;
   1.678 +  while (lineNumber != mNumLines) {
   1.679 +    nsLineBox* line = mLines[lineNumber];
   1.680 +    if (line->Contains(aFrame)) {
   1.681 +      return lineNumber;
   1.682 +    }
   1.683 +    ++lineNumber;
   1.684 +  }
   1.685 +  return -1;
   1.686 +}
   1.687 +
   1.688 +NS_IMETHODIMP
   1.689 +nsLineIterator::CheckLineOrder(int32_t                  aLine,
   1.690 +                               bool                     *aIsReordered,
   1.691 +                               nsIFrame                 **aFirstVisual,
   1.692 +                               nsIFrame                 **aLastVisual)
   1.693 +{
   1.694 +  NS_ASSERTION (aLine >= 0 && aLine < mNumLines, "aLine out of range!");
   1.695 +  nsLineBox* line = mLines[aLine];
   1.696 +
   1.697 +  if (!line->mFirstChild) { // empty line
   1.698 +    *aIsReordered = false;
   1.699 +    *aFirstVisual = nullptr;
   1.700 +    *aLastVisual = nullptr;
   1.701 +    return NS_OK;
   1.702 +  }
   1.703 +
   1.704 +  nsIFrame* leftmostFrame;
   1.705 +  nsIFrame* rightmostFrame;
   1.706 +  *aIsReordered = nsBidiPresUtils::CheckLineOrder(line->mFirstChild, line->GetChildCount(), &leftmostFrame, &rightmostFrame);
   1.707 +
   1.708 +  // map leftmost/rightmost to first/last according to paragraph direction
   1.709 +  *aFirstVisual = mRightToLeft ? rightmostFrame : leftmostFrame;
   1.710 +  *aLastVisual = mRightToLeft ? leftmostFrame : rightmostFrame;
   1.711 +
   1.712 +  return NS_OK;
   1.713 +}
   1.714 +
   1.715 +NS_IMETHODIMP
   1.716 +nsLineIterator::FindFrameAt(int32_t aLineNumber,
   1.717 +                            nscoord aX,
   1.718 +                            nsIFrame** aFrameFound,
   1.719 +                            bool* aXIsBeforeFirstFrame,
   1.720 +                            bool* aXIsAfterLastFrame)
   1.721 +{
   1.722 +  NS_PRECONDITION(aFrameFound && aXIsBeforeFirstFrame && aXIsAfterLastFrame,
   1.723 +                  "null OUT ptr");
   1.724 +  if (!aFrameFound || !aXIsBeforeFirstFrame || !aXIsAfterLastFrame) {
   1.725 +    return NS_ERROR_NULL_POINTER;
   1.726 +  }
   1.727 +  if ((aLineNumber < 0) || (aLineNumber >= mNumLines)) {
   1.728 +    return NS_ERROR_INVALID_ARG;
   1.729 +  }
   1.730 +
   1.731 +  nsLineBox* line = mLines[aLineNumber];
   1.732 +  if (!line) {
   1.733 +    *aFrameFound = nullptr;
   1.734 +    *aXIsBeforeFirstFrame = true;
   1.735 +    *aXIsAfterLastFrame = false;
   1.736 +    return NS_OK;
   1.737 +  }
   1.738 +
   1.739 +  if (line->ISize() == 0 && line->BSize() == 0)
   1.740 +    return NS_ERROR_FAILURE;
   1.741 +
   1.742 +  nsIFrame* frame = line->mFirstChild;
   1.743 +  nsIFrame* closestFromLeft = nullptr;
   1.744 +  nsIFrame* closestFromRight = nullptr;
   1.745 +  int32_t n = line->GetChildCount();
   1.746 +  while (n--) {
   1.747 +    nsRect rect = frame->GetRect();
   1.748 +    if (rect.width > 0) {
   1.749 +      // If aX is inside this frame - this is it
   1.750 +      if (rect.x <= aX && rect.XMost() > aX) {
   1.751 +        closestFromLeft = closestFromRight = frame;
   1.752 +        break;
   1.753 +      }
   1.754 +      if (rect.x < aX) {
   1.755 +        if (!closestFromLeft || 
   1.756 +            rect.XMost() > closestFromLeft->GetRect().XMost())
   1.757 +          closestFromLeft = frame;
   1.758 +      }
   1.759 +      else {
   1.760 +        if (!closestFromRight ||
   1.761 +            rect.x < closestFromRight->GetRect().x)
   1.762 +          closestFromRight = frame;
   1.763 +      }
   1.764 +    }
   1.765 +    frame = frame->GetNextSibling();
   1.766 +  }
   1.767 +  if (!closestFromLeft && !closestFromRight) {
   1.768 +    // All frames were zero-width. Just take the first one.
   1.769 +    closestFromLeft = closestFromRight = line->mFirstChild;
   1.770 +  }
   1.771 +  *aXIsBeforeFirstFrame = mRightToLeft ? !closestFromRight : !closestFromLeft;
   1.772 +  *aXIsAfterLastFrame = mRightToLeft ? !closestFromLeft : !closestFromRight;
   1.773 +  if (closestFromLeft == closestFromRight) {
   1.774 +    *aFrameFound = closestFromLeft;
   1.775 +  }
   1.776 +  else if (!closestFromLeft) {
   1.777 +    *aFrameFound = closestFromRight;
   1.778 +  }
   1.779 +  else if (!closestFromRight) {
   1.780 +    *aFrameFound = closestFromLeft;
   1.781 +  }
   1.782 +  else { // we're between two frames
   1.783 +    nscoord delta = closestFromRight->GetRect().x - closestFromLeft->GetRect().XMost();
   1.784 +    if (aX < closestFromLeft->GetRect().XMost() + delta/2)
   1.785 +      *aFrameFound = closestFromLeft;
   1.786 +    else
   1.787 +      *aFrameFound = closestFromRight;
   1.788 +  }
   1.789 +  return NS_OK;
   1.790 +}
   1.791 +
   1.792 +NS_IMETHODIMP
   1.793 +nsLineIterator::GetNextSiblingOnLine(nsIFrame*& aFrame, int32_t aLineNumber)
   1.794 +{
   1.795 +  aFrame = aFrame->GetNextSibling();
   1.796 +  return NS_OK;
   1.797 +}
   1.798 +
   1.799 +//----------------------------------------------------------------------
   1.800 +
   1.801 +#ifdef NS_BUILD_REFCNT_LOGGING
   1.802 +nsFloatCacheList::nsFloatCacheList() :
   1.803 +  mHead(nullptr)
   1.804 +{
   1.805 +  MOZ_COUNT_CTOR(nsFloatCacheList);
   1.806 +}
   1.807 +#endif
   1.808 +
   1.809 +nsFloatCacheList::~nsFloatCacheList()
   1.810 +{
   1.811 +  DeleteAll();
   1.812 +  MOZ_COUNT_DTOR(nsFloatCacheList);
   1.813 +}
   1.814 +
   1.815 +void
   1.816 +nsFloatCacheList::DeleteAll()
   1.817 +{
   1.818 +  nsFloatCache* c = mHead;
   1.819 +  while (c) {
   1.820 +    nsFloatCache* next = c->Next();
   1.821 +    delete c;
   1.822 +    c = next;
   1.823 +  }
   1.824 +  mHead = nullptr;
   1.825 +}
   1.826 +
   1.827 +nsFloatCache*
   1.828 +nsFloatCacheList::Tail() const
   1.829 +{
   1.830 +  nsFloatCache* fc = mHead;
   1.831 +  while (fc) {
   1.832 +    if (!fc->mNext) {
   1.833 +      break;
   1.834 +    }
   1.835 +    fc = fc->mNext;
   1.836 +  }
   1.837 +  return fc;
   1.838 +}
   1.839 +
   1.840 +void
   1.841 +nsFloatCacheList::Append(nsFloatCacheFreeList& aList)
   1.842 +{
   1.843 +  NS_PRECONDITION(aList.NotEmpty(), "Appending empty list will fail");
   1.844 +  
   1.845 +  nsFloatCache* tail = Tail();
   1.846 +  if (tail) {
   1.847 +    NS_ASSERTION(!tail->mNext, "Bogus!");
   1.848 +    tail->mNext = aList.mHead;
   1.849 +  }
   1.850 +  else {
   1.851 +    NS_ASSERTION(!mHead, "Bogus!");
   1.852 +    mHead = aList.mHead;
   1.853 +  }
   1.854 +  aList.mHead = nullptr;
   1.855 +  aList.mTail = nullptr;
   1.856 +}
   1.857 +
   1.858 +nsFloatCache*
   1.859 +nsFloatCacheList::Find(nsIFrame* aOutOfFlowFrame)
   1.860 +{
   1.861 +  nsFloatCache* fc = mHead;
   1.862 +  while (fc) {
   1.863 +    if (fc->mFloat == aOutOfFlowFrame) {
   1.864 +      break;
   1.865 +    }
   1.866 +    fc = fc->Next();
   1.867 +  }
   1.868 +  return fc;
   1.869 +}
   1.870 +
   1.871 +nsFloatCache*
   1.872 +nsFloatCacheList::RemoveAndReturnPrev(nsFloatCache* aElement)
   1.873 +{
   1.874 +  nsFloatCache* fc = mHead;
   1.875 +  nsFloatCache* prev = nullptr;
   1.876 +  while (fc) {
   1.877 +    if (fc == aElement) {
   1.878 +      if (prev) {
   1.879 +        prev->mNext = fc->mNext;
   1.880 +      } else {
   1.881 +        mHead = fc->mNext;
   1.882 +      }
   1.883 +      return prev;
   1.884 +    }
   1.885 +    prev = fc;
   1.886 +    fc = fc->mNext;
   1.887 +  }
   1.888 +  return nullptr;
   1.889 +}
   1.890 +
   1.891 +//----------------------------------------------------------------------
   1.892 +
   1.893 +#ifdef NS_BUILD_REFCNT_LOGGING
   1.894 +nsFloatCacheFreeList::nsFloatCacheFreeList() :
   1.895 +  mTail(nullptr)
   1.896 +{
   1.897 +  MOZ_COUNT_CTOR(nsFloatCacheFreeList);
   1.898 +}
   1.899 +
   1.900 +nsFloatCacheFreeList::~nsFloatCacheFreeList()
   1.901 +{
   1.902 +  MOZ_COUNT_DTOR(nsFloatCacheFreeList);
   1.903 +}
   1.904 +#endif
   1.905 +  
   1.906 +void
   1.907 +nsFloatCacheFreeList::Append(nsFloatCacheList& aList)
   1.908 +{
   1.909 +  NS_PRECONDITION(aList.NotEmpty(), "Appending empty list will fail");
   1.910 +  
   1.911 +  if (mTail) {
   1.912 +    NS_ASSERTION(!mTail->mNext, "Bogus");
   1.913 +    mTail->mNext = aList.mHead;
   1.914 +  }
   1.915 +  else {
   1.916 +    NS_ASSERTION(!mHead, "Bogus");
   1.917 +    mHead = aList.mHead;
   1.918 +  }
   1.919 +  mTail = aList.Tail();
   1.920 +  aList.mHead = nullptr;
   1.921 +}
   1.922 +
   1.923 +void
   1.924 +nsFloatCacheFreeList::Remove(nsFloatCache* aElement)
   1.925 +{
   1.926 +  nsFloatCache* prev = nsFloatCacheList::RemoveAndReturnPrev(aElement);
   1.927 +  if (mTail == aElement) {
   1.928 +    mTail = prev;
   1.929 +  }
   1.930 +}
   1.931 +
   1.932 +void
   1.933 +nsFloatCacheFreeList::DeleteAll()
   1.934 +{
   1.935 +  nsFloatCacheList::DeleteAll();
   1.936 +  mTail = nullptr;
   1.937 +}
   1.938 +
   1.939 +nsFloatCache*
   1.940 +nsFloatCacheFreeList::Alloc(nsIFrame* aFloat)
   1.941 +{
   1.942 +  NS_PRECONDITION(aFloat->GetStateBits() & NS_FRAME_OUT_OF_FLOW,
   1.943 +                  "This is a float cache, why isn't the frame out-of-flow?");
   1.944 +  nsFloatCache* fc = mHead;
   1.945 +  if (mHead) {
   1.946 +    if (mHead == mTail) {
   1.947 +      mHead = mTail = nullptr;
   1.948 +    }
   1.949 +    else {
   1.950 +      mHead = fc->mNext;
   1.951 +    }
   1.952 +    fc->mNext = nullptr;
   1.953 +  }
   1.954 +  else {
   1.955 +    fc = new nsFloatCache();
   1.956 +  }
   1.957 +  fc->mFloat = aFloat;
   1.958 +  return fc;
   1.959 +}
   1.960 +
   1.961 +void
   1.962 +nsFloatCacheFreeList::Append(nsFloatCache* aFloat)
   1.963 +{
   1.964 +  NS_ASSERTION(!aFloat->mNext, "Bogus!");
   1.965 +  aFloat->mNext = nullptr;
   1.966 +  if (mTail) {
   1.967 +    NS_ASSERTION(!mTail->mNext, "Bogus!");
   1.968 +    mTail->mNext = aFloat;
   1.969 +    mTail = aFloat;
   1.970 +  }
   1.971 +  else {
   1.972 +    NS_ASSERTION(!mHead, "Bogus!");
   1.973 +    mHead = mTail = aFloat;
   1.974 +  }
   1.975 +}
   1.976 +
   1.977 +//----------------------------------------------------------------------
   1.978 +
   1.979 +nsFloatCache::nsFloatCache()
   1.980 +  : mFloat(nullptr),
   1.981 +    mNext(nullptr)
   1.982 +{
   1.983 +  MOZ_COUNT_CTOR(nsFloatCache);
   1.984 +}
   1.985 +
   1.986 +#ifdef NS_BUILD_REFCNT_LOGGING
   1.987 +nsFloatCache::~nsFloatCache()
   1.988 +{
   1.989 +  MOZ_COUNT_DTOR(nsFloatCache);
   1.990 +}
   1.991 +#endif

mercurial