Bug 1395146 - part3: nsTextEditorState should cache empty value with mCachedValue r?smaug draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 30 Aug 2017 20:54:45 +0900
changeset 656379 dd5818ad304837f2299b213476cf1cb349c55ae0
parent 656378 41b7db5997451c45f5044887bd3ee582185ee6a9
child 729118 c5aa4042cb4983b73042586b346879636b2cfd62
push id77190
push usermasayuki@d-toybox.com
push dateThu, 31 Aug 2017 04:20:32 +0000
reviewerssmaug
bugs1395146
milestone57.0a1
Bug 1395146 - part3: nsTextEditorState should cache empty value with mCachedValue r?smaug nsTextEditorState::GetValue() uses mCachedValue only when the value isn't empty string. However, with SetIsVoid() and IsVoid() of nsAString, nsTextEditorState can cache empty value only with mCachedValue. MozReview-Commit-ID: AmQDquEn9M8
dom/html/nsTextEditorState.cpp
dom/html/nsTextEditorState.h
--- a/dom/html/nsTextEditorState.cpp
+++ b/dom/html/nsTextEditorState.cpp
@@ -1181,16 +1181,17 @@ nsTextEditorState::Construct(nsITextCont
     state->mEditorInitialized = false;
     state->mInitializing = false;
     state->mValueTransferInProgress = false;
     state->mSelectionCached = true;
     state->mSelectionRestoreEagerInit = false;
     state->mPlaceholderVisibility = false;
     state->mPreviewVisibility = false;
     state->mIsCommittingComposition = false;
+    state->ClearValueCache();
     // When adding more member variable initializations here, add the same
     // also to the constructor.
     return state;
   }
 
   return new nsTextEditorState(aOwningElement);
 }
 
@@ -1407,16 +1408,18 @@ nsTextEditorState::PrepareEditor(const n
   AutoHideSelectionChanges hideSelectionChanges(GetConstFrameSelection());
 
   // Don't attempt to initialize recursively!
   InitializationGuard guard(*this);
   if (guard.IsInitializingRecursively()) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
+  ClearValueCache();
+
   // Note that we don't check mTextEditor here, because we might already have
   // one around, in which case we don't create a new one, and we'll just tie
   // the required machinery to it.
 
   nsPresContext *presContext = mBoundFrame->PresContext();
   nsIPresShell *shell = presContext->GetPresShell();
 
   // Setup the editor flags
@@ -2427,17 +2430,17 @@ nsTextEditorState::GetValue(nsAString& a
   // been set to the editor yet.
   if (mIsCommittingComposition) {
     aValue = mValueBeingSet;
     return;
   }
 
   if (mTextEditor && mBoundFrame &&
       (mEditorInitialized || !IsSingleLineTextControl())) {
-    if (aIgnoreWrap && !mCachedValue.IsEmpty()) {
+    if (aIgnoreWrap && !mCachedValue.IsVoid()) {
       aValue = mCachedValue;
       return;
     }
 
     aValue.Truncate(); // initialize out param
 
     uint32_t flags = (nsIDocumentEncoder::OutputLFLineBreak |
                       nsIDocumentEncoder::OutputPreformatted |
@@ -2469,19 +2472,19 @@ nsTextEditorState::GetValue(nsAString& a
       AutoNoJSAPI nojsapi;
 
       mTextEditor->OutputToString(NS_LITERAL_STRING("text/plain"), flags,
                                   aValue);
     }
     // Only when the result doesn't include line breaks caused by hard-wrap,
     // mCacheValue should cache the value.
     if (!(flags & nsIDocumentEncoder::OutputWrap)) {
-      mCachedValue = aValue;
+      const_cast<nsTextEditorState*>(this)->SetValueCache(aValue);
     } else {
-      mCachedValue.Truncate();
+      const_cast<nsTextEditorState*>(this)->ClearValueCache();
     }
   } else {
     if (!mTextCtrlElement->ValueChanged() || !mValue) {
       mTextCtrlElement->GetDefaultValueFromContent(aValue);
     } else {
       aValue = *mValue;
     }
   }
@@ -2714,17 +2717,17 @@ nsTextEditorState::SetValue(const nsAStr
           if (!mBoundFrame) {
             return SetValue(newValue, aFlags & eSetValue_Notify);
           }
           return true;
         }
 
         // The new value never includes line breaks caused by hard-wrap.
         // So, mCachedValue can always cache the new value.
-        if (!mCachedValue.Assign(newValue, fallible)) {
+        if (!SetValueCache(newValue, fallible)) {
           return false;
         }
       }
     }
   } else {
     if (!mValue) {
       mValue.emplace();
     }
--- a/dom/html/nsTextEditorState.h
+++ b/dom/html/nsTextEditorState.h
@@ -250,17 +250,37 @@ public:
 
   /**
    * Get the maxlength attribute
    * @param aMaxLength the value of the max length attr
    * @returns false if attr not defined
    */
   int32_t GetMaxLength();
 
-  void ClearValueCache() { mCachedValue.Truncate(); }
+  void ClearValueCache()
+  {
+    mCachedValue.SetIsVoid(true);
+    MOZ_ASSERT(mCachedValue.IsEmpty());
+  }
+  void SetValueCache(const nsAString& aValue)
+  {
+    mCachedValue.Assign(aValue);
+    MOZ_ASSERT(!mCachedValue.IsVoid());
+  }
+  MOZ_MUST_USE bool
+  SetValueCache(const nsAString& aValue,
+                const mozilla::fallible_t& aFallible)
+  {
+    if (!mCachedValue.Assign(aValue, aFallible)) {
+      ClearValueCache();
+      return false;
+    }
+    MOZ_ASSERT(!mCachedValue.IsVoid());
+    return true;
+  }
 
   void HideSelectionIfBlurred();
 
   struct SelectionProperties {
     public:
       SelectionProperties() : mStart(0), mEnd(0),
         mDirection(nsITextControlFrame::eForward) {}
       bool IsDefault() const
@@ -455,21 +475,21 @@ private:
   nsCOMPtr<mozilla::dom::Element> mRootNode;
   nsCOMPtr<mozilla::dom::Element> mPlaceholderDiv;
   nsCOMPtr<mozilla::dom::Element> mPreviewDiv;
   nsTextControlFrame* mBoundFrame;
   RefPtr<nsTextInputListener> mTextListener;
   mozilla::Maybe<nsString> mValue;
   RefPtr<nsAnonDivObserver> mMutationObserver;
   // Cache of the |.value| of <input> or <textarea> element without hard-wrap.
-  // If this is empty string, it doesn't cache |.value|.
+  // If its IsVoid() returns true, it doesn't cache |.value|.
   // Otherwise, it's cached when setting specific value or getting value from
   // TextEditor.  Additionally, when contents in the anonymous <div> element
   // is modified, this is cleared.
-  mutable nsString mCachedValue;
+  nsString mCachedValue;
   // mValueBeingSet is available only while SetValue() is requesting to commit
   // composition.  I.e., this is valid only while mIsCommittingComposition is
   // true.  While active composition is being committed, GetValue() needs
   // the latest value which is set by SetValue().  So, this is cache for that.
   nsString mValueBeingSet;
   SelectionProperties mSelectionProperties;
   bool mEverInited; // Have we ever been initialized?
   bool mEditorInitialized;