Bug 1173678 ContentCache should return union rect even if some character rects are not cached but the first character of the range is cached r=m_kato
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 17 Jun 2015 10:03:58 +0900
changeset 267391 12afd3e6ae244c1df56f3eadf07a15b8aa005442
parent 267390 5e35c779d79193d52fd20d5d73b7ff9a05642ae4
child 267392 eca8d9db5581148d45be58d0085fe3ab64c8876b
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-esr52@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersm_kato
bugs1173678
milestone41.0a1
Bug 1173678 ContentCache should return union rect even if some character rects are not cached but the first character of the range is cached r=m_kato
widget/ContentCache.cpp
widget/ContentCache.h
--- a/widget/ContentCache.cpp
+++ b/widget/ContentCache.cpp
@@ -781,16 +781,22 @@ ContentCache::GetUnionTextRects(uint32_t
   MOZ_LOG(sContentCacheLog, LogLevel::Info,
     ("ContentCache: 0x%p (mIsChrome=%s) GetUnionTextRects(aOffset=%u, "
      "aLength=%u), mTextRectArray={ mStart=%u, mRects.Length()=%u }, "
      "mSelection={ mAnchor=%u, mFocus=%u }",
      this, GetBoolName(mIsChrome), aOffset, aLength,
      mTextRectArray.mStart, mTextRectArray.mRects.Length(),
      mSelection.mAnchor, mSelection.mFocus));
 
+  CheckedInt<uint32_t> endOffset =
+    CheckedInt<uint32_t>(aOffset) + aLength;
+  if (!endOffset.isValid()) {
+    return false;
+  }
+
   if (!mSelection.Collapsed() &&
       aOffset == mSelection.StartOffset() && aLength == mSelection.Length()) {
     NS_WARN_IF(mSelection.mRect.IsEmpty());
     aUnionTextRect = mSelection.mRect;
     return !aUnionTextRect.IsEmpty();
   }
 
   if (aLength == 1) {
@@ -806,22 +812,44 @@ ContentCache::GetUnionTextRects(uint32_t
     }
     if (aOffset == mSelection.mFocus) {
       NS_WARN_IF(mSelection.mFocusCharRect.IsEmpty());
       aUnionTextRect = mSelection.mFocusCharRect;
       return !aUnionTextRect.IsEmpty();
     }
   }
 
-  if (!mTextRectArray.InRange(aOffset, aLength)) {
-    aUnionTextRect.SetEmpty();
+  // Even if some text rects are not cached of the queried range,
+  // we should return union rect when the first character's rect is cached
+  // since the first character rect is important and the others are not so
+  // in most cases.
+
+  if (!aOffset && aOffset != mSelection.mAnchor &&
+      aOffset != mSelection.mFocus && !mTextRectArray.InRange(aOffset)) {
+    // The first character rect isn't cached.
     return false;
   }
-  aUnionTextRect = mTextRectArray.GetUnionRect(aOffset, aLength);
-  return true;
+
+  if (mTextRectArray.IsOverlappingWith(aOffset, aLength)) {
+    aUnionTextRect =
+      mTextRectArray.GetUnionRectAsFarAsPossible(aOffset, aLength);
+  } else {
+    aUnionTextRect.SetEmpty();
+  }
+
+  if (!aOffset) {
+    aUnionTextRect = aUnionTextRect.Union(mFirstCharRect);
+  }
+  if (aOffset <= mSelection.mAnchor && mSelection.mAnchor < endOffset.value()) {
+    aUnionTextRect = aUnionTextRect.Union(mSelection.mAnchorCharRect);
+  }
+  if (aOffset <= mSelection.mFocus && mSelection.mFocus < endOffset.value()) {
+    aUnionTextRect = aUnionTextRect.Union(mSelection.mFocusCharRect);
+  }
+  return !aUnionTextRect.IsEmpty();
 }
 
 bool
 ContentCache::GetCaretRect(uint32_t aOffset,
                            LayoutDeviceIntRect& aCaretRect) const
 {
   MOZ_LOG(sContentCacheLog, LogLevel::Info,
     ("ContentCache: 0x%p (mIsChrome=%s) GetCaretRect(aOffset=%u), "
@@ -989,9 +1017,26 @@ ContentCache::TextRectArray::GetUnionRec
     return rect;
   }
   for (uint32_t i = 0; i < aLength; i++) {
     rect = rect.Union(mRects[aOffset - mStart + i]);
   }
   return rect;
 }
 
+LayoutDeviceIntRect
+ContentCache::TextRectArray::GetUnionRectAsFarAsPossible(
+                               uint32_t aOffset,
+                               uint32_t aLength) const
+{
+  LayoutDeviceIntRect rect;
+  if (!IsOverlappingWith(aOffset, aLength)) {
+    return rect;
+  }
+  uint32_t startOffset = std::max(aOffset, mStart);
+  uint32_t endOffset = std::min(aOffset + aLength, EndOffset());
+  for (uint32_t i = 0; i < endOffset - startOffset; i++) {
+    rect = rect.Union(mRects[startOffset - mStart + i]);
+  }
+  return rect;
+}
+
 } // namespace mozilla
--- a/widget/ContentCache.h
+++ b/widget/ContentCache.h
@@ -308,18 +308,32 @@ private:
     {
       CheckedInt<uint32_t> endOffset =
         CheckedInt<uint32_t>(aOffset) + aLength;
       if (NS_WARN_IF(!endOffset.isValid())) {
         return false;
       }
       return InRange(aOffset) && aOffset + aLength <= EndOffset();
     }
+    bool IsOverlappingWith(uint32_t aOffset, uint32_t aLength) const
+    {
+      if (!IsValid() || aOffset == UINT32_MAX) {
+        return false;
+      }
+      CheckedInt<uint32_t> endOffset =
+        CheckedInt<uint32_t>(aOffset) + aLength;
+      if (NS_WARN_IF(!endOffset.isValid())) {
+        return false;
+      }
+      return aOffset <= EndOffset() && endOffset.value() >= mStart;
+    }
     LayoutDeviceIntRect GetRect(uint32_t aOffset) const;
     LayoutDeviceIntRect GetUnionRect(uint32_t aOffset, uint32_t aLength) const;
+    LayoutDeviceIntRect GetUnionRectAsFarAsPossible(uint32_t aOffset,
+                                                    uint32_t aLength) const;
   } mTextRectArray;
 
   LayoutDeviceIntRect mEditorRect;
 
   // mIsComposing is valid only in chrome process.
   bool mIsComposing;
   // mRequestedToCommitOrCancelComposition is valid only in chrome process.
   bool mRequestedToCommitOrCancelComposition;