Bug 1395146 - part2: Make nsTextEditorState cache the value of TextEditor with mCachedValue even when the editor is single line text control r?smaug draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Wed, 30 Aug 2017 19:43:00 +0900
changeset 656378 41b7db5997451c45f5044887bd3ee582185ee6a9
parent 656377 4ee423eab3194831235bf1c973e279e164898723
child 656379 dd5818ad304837f2299b213476cf1cb349c55ae0
push id77190
push usermasayuki@d-toybox.com
push dateThu, 31 Aug 2017 04:20:32 +0000
reviewerssmaug
bugs1395146, 8848015
milestone57.0a1
Bug 1395146 - part2: Make nsTextEditorState cache the value of TextEditor with mCachedValue even when the editor is single line text control r?smaug Currently, nsTextEditorState caches the value of TextEditor only when it's for a multi-line text control, i.e., <textarea>. However, using it for single-line text control improves the score of attachment 8848015. Although this might increase the cost of creating and registering mutation observer, but it may not make damage to performance of loading pages since editor for each element won't be initialized until each element gets focus. MozReview-Commit-ID: DnjEJ5AUh3M
dom/html/nsTextEditorState.cpp
dom/html/nsTextEditorState.h
--- a/dom/html/nsTextEditorState.cpp
+++ b/dom/html/nsTextEditorState.cpp
@@ -2279,21 +2279,18 @@ nsTextEditorState::CreateRootNode()
   nodeInfo = doc->NodeInfoManager()->GetNodeInfo(nsGkAtoms::div, nullptr,
                                                  kNameSpaceID_XHTML,
                                                  nsIDOMNode::ELEMENT_NODE);
 
   nsresult rv = NS_NewHTMLElement(getter_AddRefs(mRootNode), nodeInfo.forget(),
                                   NOT_FROM_PARSER);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (!IsSingleLineTextControl()) {
-    mMutationObserver = new nsAnonDivObserver(this);
-    mRootNode->AddMutationObserver(mMutationObserver);
-  }
-
+  mMutationObserver = new nsAnonDivObserver(this);
+  mRootNode->AddMutationObserver(mMutationObserver);
   return rv;
 }
 
 nsresult
 nsTextEditorState::InitializeRootNode()
 {
   // Make our root node editable
   mRootNode->SetFlags(NODE_IS_EDITABLE);
@@ -2430,18 +2427,17 @@ nsTextEditorState::GetValue(nsAString& a
   // been set to the editor yet.
   if (mIsCommittingComposition) {
     aValue = mValueBeingSet;
     return;
   }
 
   if (mTextEditor && mBoundFrame &&
       (mEditorInitialized || !IsSingleLineTextControl())) {
-    bool canCache = aIgnoreWrap && !IsSingleLineTextControl();
-    if (canCache && !mCachedValue.IsEmpty()) {
+    if (aIgnoreWrap && !mCachedValue.IsEmpty()) {
       aValue = mCachedValue;
       return;
     }
 
     aValue.Truncate(); // initialize out param
 
     uint32_t flags = (nsIDocumentEncoder::OutputLFLineBreak |
                       nsIDocumentEncoder::OutputPreformatted |
@@ -2470,17 +2466,19 @@ nsTextEditorState::GetValue(nsAString& a
     // if plaintext editor didn't create <br> nodes all over), we wouldn't need
     // this.
     { /* Scope for AutoNoJSAPI. */
       AutoNoJSAPI nojsapi;
 
       mTextEditor->OutputToString(NS_LITERAL_STRING("text/plain"), flags,
                                   aValue);
     }
-    if (canCache) {
+    // Only when the result doesn't include line breaks caused by hard-wrap,
+    // mCacheValue should cache the value.
+    if (!(flags & nsIDocumentEncoder::OutputWrap)) {
       mCachedValue = aValue;
     } else {
       mCachedValue.Truncate();
     }
   } else {
     if (!mTextCtrlElement->ValueChanged() || !mValue) {
       mTextCtrlElement->GetDefaultValueFromContent(aValue);
     } else {
@@ -2714,20 +2712,20 @@ nsTextEditorState::SetValue(const nsAStr
           // the existing selection -- see bug 574558), in which case we don't
           // need to reset the value here.
           if (!mBoundFrame) {
             return SetValue(newValue, aFlags & eSetValue_Notify);
           }
           return true;
         }
 
-        if (!IsSingleLineTextControl()) {
-          if (!mCachedValue.Assign(newValue, fallible)) {
-            return false;
-          }
+        // The new value never includes line breaks caused by hard-wrap.
+        // So, mCachedValue can always cache the new value.
+        if (!mCachedValue.Assign(newValue, fallible)) {
+          return false;
         }
       }
     }
   } else {
     if (!mValue) {
       mValue.emplace();
     }
 
--- a/dom/html/nsTextEditorState.h
+++ b/dom/html/nsTextEditorState.h
@@ -454,17 +454,22 @@ private:
   RefPtr<mozilla::TextEditor> mTextEditor;
   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;
-  mutable nsString mCachedValue; // Caches non-hard-wrapped value on a multiline control.
+  // Cache of the |.value| of <input> or <textarea> element without hard-wrap.
+  // If this is empty string, 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;
   // 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;