Bug 1395146 - part3: nsTextEditorState should cache empty value with mCachedValue r=smaug
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 30 Aug 2017 20:54:45 +0900
changeset 429840 929846a8860337b42dd664a9bbec5c88944b92b6
parent 429839 bd7325a8425e1c59245bf86b19fb077d40bd3a3e
child 429841 71ee70900245cc248028356a674e354a4f41f497
push id1567
push userjlorenzo@mozilla.com
push dateThu, 02 Nov 2017 12:36:05 +0000
treeherdermozilla-release@e512c14a0406 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1395146
milestone57.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 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;