Bug 1166436 part.19 ContentCache::mSelection should have invalid state and it should be checked r=m_kato
authorMasayuki Nakano <masayuki@d-toybox.com>
Fri, 05 Jun 2015 18:28:21 +0900
changeset 278142 cbaeacbb97009880ef2b44c546fdd0f31125e912
parent 278141 6fe725dba239a5eeb30805e26a9362dd6d938a4b
child 278143 41bf995f486fa2c46b0a9ec0f95fc47f33997793
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-beta@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersm_kato
bugs1166436
milestone41.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1166436 part.19 ContentCache::mSelection should have invalid state and it should be checked r=m_kato
widget/ContentCache.cpp
widget/ContentCache.h
widget/TextEvents.h
--- a/widget/ContentCache.cpp
+++ b/widget/ContentCache.cpp
@@ -190,16 +190,25 @@ ContentCache::HandleQueryContentEvent(Wi
   aEvent.mReply.mFocusedWidget = aWidget;
 
   switch (aEvent.message) {
     case NS_QUERY_SELECTED_TEXT:
       MOZ_LOG(sContentCacheLog, LogLevel::Info,
         ("ContentCache: 0x%p (mIsChrome=%s) HandleQueryContentEvent("
          "aEvent={ message=NS_QUERY_SELECTED_TEXT }, aWidget=0x%p)",
          this, GetBoolName(mIsChrome), aWidget));
+      if (NS_WARN_IF(!IsSelectionValid())) {
+        // If content cache hasn't been initialized properly, make the query
+        // failed.
+        MOZ_LOG(sContentCacheLog, LogLevel::Error,
+          ("ContentCache: 0x%p (mIsChrome=%s) HandleQueryContentEvent(), "
+           "FAILED because mSelection is not valid",
+           this, GetBoolName(mIsChrome)));
+        return true;
+      }
       aEvent.mReply.mOffset = mSelection.StartOffset();
       if (mSelection.Collapsed()) {
         aEvent.mReply.mString.Truncate(0);
       } else {
         if (NS_WARN_IF(mSelection.EndOffset() > mText.Length())) {
           MOZ_LOG(sContentCacheLog, LogLevel::Error,
             ("ContentCache: 0x%p (mIsChrome=%s) HandleQueryContentEvent(), "
              "FAILED because mSelection.EndOffset()=%u is larger than "
@@ -253,16 +262,25 @@ ContentCache::HandleQueryContentEvent(Wi
     }
     case NS_QUERY_TEXT_RECT:
       MOZ_LOG(sContentCacheLog, LogLevel::Info,
         ("ContentCache: 0x%p (mIsChrome=%s) HandleQueryContentEvent("
          "aEvent={ message=NS_QUERY_TEXT_RECT, mInput={ mOffset=%u, "
          "mLength=%u } }, aWidget=0x%p), mText.Length()=%u",
          this, GetBoolName(mIsChrome), aEvent.mInput.mOffset,
          aEvent.mInput.mLength, aWidget, mText.Length()));
+      if (NS_WARN_IF(!IsSelectionValid())) {
+        // If content cache hasn't been initialized properly, make the query
+        // failed.
+        MOZ_LOG(sContentCacheLog, LogLevel::Error,
+          ("ContentCache: 0x%p (mIsChrome=%s) HandleQueryContentEvent(), "
+           "FAILED because mSelection is not valid",
+           this, GetBoolName(mIsChrome)));
+        return true;
+      }
       if (aEvent.mInput.mLength) {
         if (NS_WARN_IF(!GetUnionTextRects(aEvent.mInput.mOffset,
                                           aEvent.mInput.mLength,
                                           aEvent.mReply.mRect))) {
           // XXX We don't have cache for this request.
           MOZ_LOG(sContentCacheLog, LogLevel::Error,
             ("ContentCache: 0x%p (mIsChrome=%s) HandleQueryContentEvent(), "
              "FAILED to get union rect",
@@ -302,16 +320,25 @@ ContentCache::HandleQueryContentEvent(Wi
       break;
     case NS_QUERY_CARET_RECT:
       MOZ_LOG(sContentCacheLog, LogLevel::Info,
         ("ContentCache: 0x%p (mIsChrome=%s) HandleQueryContentEvent("
          "aEvent={ message=NS_QUERY_CARET_RECT, mInput={ mOffset=%u } }, "
          "aWidget=0x%p), mText.Length()=%u",
          this, GetBoolName(mIsChrome), aEvent.mInput.mOffset, aWidget,
          mText.Length()));
+      if (NS_WARN_IF(!IsSelectionValid())) {
+        // If content cache hasn't been initialized properly, make the query
+        // failed.
+        MOZ_LOG(sContentCacheLog, LogLevel::Error,
+          ("ContentCache: 0x%p (mIsChrome=%s) HandleQueryContentEvent(), "
+           "FAILED because mSelection is not valid",
+           this, GetBoolName(mIsChrome)));
+        return true;
+      }
       if (NS_WARN_IF(!GetCaretRect(aEvent.mInput.mOffset,
                                    aEvent.mReply.mRect))) {
         MOZ_LOG(sContentCacheLog, LogLevel::Error,
           ("ContentCache: 0x%p (mIsChrome=%s) HandleQueryContentEvent(), "
            "FAILED to get caret rect",
            this, GetBoolName(mIsChrome)));
         return false;
       }
@@ -415,16 +442,20 @@ ContentCache::CacheCaret(nsIWidget* aWid
 
   // CacheCaret() must be called in content process.
   if (NS_WARN_IF(mIsChrome)) {
     return false;
   }
 
   mCaret.Clear();
 
+  if (NS_WARN_IF(!mSelection.IsValid())) {
+    return false;
+  }
+
   // XXX Should be mSelection.mFocus?
   mCaret.mOffset = mSelection.StartOffset();
 
   nsEventStatus status = nsEventStatus_eIgnore;
   WidgetQueryContentEvent caretRect(true, NS_QUERY_CARET_RECT, aWidget);
   caretRect.InitForQueryCaretRect(mCaret.mOffset);
   aWidget->DispatchEvent(&caretRect, status);
   if (NS_WARN_IF(!caretRect.mSucceeded)) {
@@ -560,16 +591,20 @@ ContentCache::CacheTextRects(nsIWidget* 
   if (NS_WARN_IF(mIsChrome)) {
     return false;
   }
 
   mTextRectArray.Clear();
   mSelection.mAnchorCharRect.SetEmpty();
   mSelection.mFocusCharRect.SetEmpty();
 
+  if (NS_WARN_IF(!mSelection.IsValid())) {
+    return false;
+  }
+
   // Retrieve text rects in composition string if there is.
   nsRefPtr<TextComposition> textComposition =
     IMEStateManager::GetTextCompositionFor(aWidget);
   if (textComposition) {
     // Note that TextComposition::String() may not be modified here because
     // it's modified after all edit action listeners are performed but this
     // is called while some of them are performed.
     uint32_t length = textComposition->LastData().Length();
--- a/widget/ContentCache.h
+++ b/widget/ContentCache.h
@@ -6,16 +6,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_ContentCache_h
 #define mozilla_ContentCache_h
 
 #include <stdint.h>
 
 #include "mozilla/Assertions.h"
+#include "mozilla/CheckedInt.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/WritingModes.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "Units.h"
 
 class nsIWidget;
 
@@ -165,39 +166,70 @@ private:
 
     WritingMode mWritingMode;
 
     // Character rects at next character of mAnchor and mFocus.
     LayoutDeviceIntRect mAnchorCharRect;
     LayoutDeviceIntRect mFocusCharRect;
 
     Selection()
-      : mAnchor(0)
-      , mFocus(0)
+      : mAnchor(UINT32_MAX)
+      , mFocus(UINT32_MAX)
     {
     }
 
     void Clear()
     {
-      mAnchor = mFocus = 0;
+      mAnchor = mFocus = UINT32_MAX;
       mWritingMode = WritingMode();
       mAnchorCharRect.SetEmpty();
       mFocusCharRect.SetEmpty();
     }
 
-    bool Collapsed() const { return mFocus == mAnchor; }
-    bool Reversed() const { return mFocus < mAnchor; }
-    uint32_t StartOffset() const { return Reversed() ? mFocus : mAnchor; }
-    uint32_t EndOffset() const { return Reversed() ? mAnchor : mFocus; }
+    bool IsValid() const
+    {
+      return mAnchor != UINT32_MAX && mFocus != UINT32_MAX;
+    }
+    bool Collapsed() const
+    {
+      NS_ASSERTION(IsValid(),
+                   "The caller should check if the selection is valid");
+      return mFocus == mAnchor;
+    }
+    bool Reversed() const
+    {
+      NS_ASSERTION(IsValid(),
+                   "The caller should check if the selection is valid");
+      return mFocus < mAnchor;
+    }
+    uint32_t StartOffset() const
+    {
+      NS_ASSERTION(IsValid(),
+                   "The caller should check if the selection is valid");
+      return Reversed() ? mFocus : mAnchor;
+    }
+    uint32_t EndOffset() const
+    {
+      NS_ASSERTION(IsValid(),
+                   "The caller should check if the selection is valid");
+      return Reversed() ? mAnchor : mFocus;
+    }
     uint32_t Length() const
     {
+      NS_ASSERTION(IsValid(),
+                   "The caller should check if the selection is valid");
       return Reversed() ? mAnchor - mFocus : mFocus - mAnchor;
     }
   } mSelection;
 
+  bool IsSelectionValid() const
+  {
+    return mSelection.IsValid() && mSelection.EndOffset() <= mText.Length();
+  }
+
   struct Caret final
   {
     uint32_t mOffset;
     LayoutDeviceIntRect mRect;
 
     Caret()
       : mOffset(UINT32_MAX)
     {
@@ -208,17 +240,18 @@ private:
       mOffset = UINT32_MAX;
       mRect.SetEmpty();
     }
 
     bool IsValid() const { return mOffset != UINT32_MAX; }
 
     uint32_t Offset() const
     {
-      NS_WARN_IF(mOffset == UINT32_MAX);
+      NS_ASSERTION(IsValid(),
+                   "The caller should check if the caret is valid");
       return mOffset;
     }
   } mCaret;
 
   struct TextRectArray final
   {
     uint32_t mStart;
     RectArray mRects;
@@ -229,38 +262,50 @@ private:
     }
 
     void Clear()
     {
       mStart = UINT32_MAX;
       mRects.Clear();
     }
 
+    bool IsValid() const
+    {
+      if (mStart == UINT32_MAX) {
+        return false;
+      }
+      CheckedInt<uint32_t> endOffset =
+        CheckedInt<uint32_t>(mStart) + mRects.Length();
+      return endOffset.isValid();
+    }
     uint32_t StartOffset() const
     {
-      NS_WARN_IF(mStart == UINT32_MAX);
+      NS_ASSERTION(IsValid(),
+                   "The caller should check if the caret is valid");
       return mStart;
     }
     uint32_t EndOffset() const
     {
-      if (NS_WARN_IF(mStart == UINT32_MAX) ||
-          NS_WARN_IF(static_cast<uint64_t>(mStart) + mRects.Length() >
-                       UINT32_MAX)) {
+      NS_ASSERTION(IsValid(),
+                   "The caller should check if the caret is valid");
+      if (!IsValid()) {
         return UINT32_MAX;
       }
       return mStart + mRects.Length();
     }
     bool InRange(uint32_t aOffset) const
     {
-      return mStart != UINT32_MAX &&
+      return IsValid() &&
              StartOffset() <= aOffset && aOffset < EndOffset();
     }
     bool InRange(uint32_t aOffset, uint32_t aLength) const
     {
-      if (NS_WARN_IF(static_cast<uint64_t>(aOffset) + aLength > UINT32_MAX)) {
+      CheckedInt<uint32_t> endOffset =
+        CheckedInt<uint32_t>(aOffset) + aLength;
+      if (NS_WARN_IF(!endOffset.isValid())) {
         return false;
       }
       return InRange(aOffset) && aOffset + aLength <= EndOffset();
     }
     LayoutDeviceIntRect GetRect(uint32_t aOffset) const;
     LayoutDeviceIntRect GetUnionRect(uint32_t aOffset, uint32_t aLength) const;
   } mTextRectArray;
 
--- a/widget/TextEvents.h
+++ b/widget/TextEvents.h
@@ -5,16 +5,17 @@
 
 #ifndef mozilla_TextEvents_h__
 #define mozilla_TextEvents_h__
 
 #include <stdint.h>
 
 #include "mozilla/Assertions.h"
 #include "mozilla/BasicEvents.h"
+#include "mozilla/CheckedInt.h"
 #include "mozilla/EventForwards.h" // for KeyNameIndex, temporarily
 #include "mozilla/TextRange.h"
 #include "mozilla/FontRange.h"
 #include "nsCOMPtr.h"
 #include "nsIDOMKeyEvent.h"
 #include "nsITransferable.h"
 #include "nsRect.h"
 #include "nsStringGlue.h"
@@ -538,20 +539,19 @@ public:
   bool mSucceeded;
   bool mWasAsync;
   bool mUseNativeLineBreak;
   bool mWithFontRanges;
   struct
   {
     uint32_t EndOffset() const
     {
-      if (NS_WARN_IF(static_cast<uint64_t>(mOffset) + mLength > UINT32_MAX)) {
-        return UINT32_MAX;
-      }
-      return mOffset + mLength;
+      CheckedInt<uint32_t> endOffset =
+        CheckedInt<uint32_t>(mOffset) + mLength;
+      return NS_WARN_IF(!endOffset.isValid()) ? UINT32_MAX : endOffset.value();
     }
 
     uint32_t mOffset;
     uint32_t mLength;
   } mInput;
 
   struct Reply
   {