Bug 1477898 - Make AutoRestoreEditorState not call EditorBase::SetFlags() since the virtual call cost appears in profile r?m_kato draft
authorMasayuki Nakano <masayuki@d-toybox.com>
Mon, 23 Jul 2018 22:04:54 +0900
changeset 821866 8b2b03cc82254755c48fddbba83c37ca6d9db0f8
parent 821865 901062bcdd9134e0867d593a9a08e0e894d49ae6
child 821900 2569a77233168ef94a1bfb38379391f871a5a8b2
child 821902 ce4de1619223be055704cca68d425fe02f74e1d1
push id117203
push usermasayuki@d-toybox.com
push dateTue, 24 Jul 2018 04:52:22 +0000
reviewersm_kato
bugs1477898
milestone63.0a1
Bug 1477898 - Make AutoRestoreEditorState not call EditorBase::SetFlags() since the virtual call cost appears in profile r?m_kato Unfortunately, EditorBase::SetFlags() is virtual call even though AutoRestoreEditorState always works only with TextEditor (the method is overridden only by HTMLEditor). And AutoRestoreEditorState is a hot path when <input>.value or <textarea>.value is set a lot. Fortunately, EditorBase::Flags() can be an inline method. So, we can make both constructor and destructor of the class check if it'll change flags actually. Additionally, this patch fixes nsTextEditorState::PrepareEditor() too. MozReview-Commit-ID: 7S4hLRRrbfB
dom/html/nsTextEditorState.cpp
--- a/dom/html/nsTextEditorState.cpp
+++ b/dom/html/nsTextEditorState.cpp
@@ -45,16 +45,25 @@
 #include "nsNumberControlFrame.h"
 #include "nsFrameSelection.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/Telemetry.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
+inline nsresult
+SetEditorFlagsIfNecessary(EditorBase& aEditorBase, uint32_t aFlags)
+{
+  if (aEditorBase.Flags() == aFlags) {
+    return NS_OK;
+  }
+  return aEditorBase.SetFlags(aFlags);
+}
+
 class MOZ_STACK_CLASS ValueSetter
 {
 public:
   explicit ValueSetter(TextEditor* aTextEditor)
     : mTextEditor(aTextEditor)
     // To protect against a reentrant call to SetValue, we check whether
     // another SetValue is already happening for this editor.  If it is,
     // we must wait until we unwind to re-enable oninput events.
@@ -134,29 +143,34 @@ public:
                                   MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : mTextEditor(aTextEditor)
     , mSavedFlags(mTextEditor->Flags())
     , mSavedMaxLength(mTextEditor->MaxTextLength())
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     MOZ_ASSERT(mTextEditor);
 
+    // EditorBase::SetFlags() is a virtual method.  Even though it does nothing
+    // if new flags and current flags are same, the calling cost causes
+    // appearing the method in profile.  So, this class should check if it's
+    // necessary to call.
     uint32_t flags = mSavedFlags;
     flags &= ~(nsIPlaintextEditor::eEditorDisabledMask);
     flags &= ~(nsIPlaintextEditor::eEditorReadonlyMask);
     flags |= nsIPlaintextEditor::eEditorDontEchoPassword;
-    mTextEditor->SetFlags(flags);
-
+    if (mSavedFlags != flags) {
+      mTextEditor->SetFlags(flags);
+    }
     mTextEditor->SetMaxTextLength(-1);
   }
 
   ~AutoRestoreEditorState()
   {
      mTextEditor->SetMaxTextLength(mSavedMaxLength);
-     mTextEditor->SetFlags(mSavedFlags);
+     SetEditorFlagsIfNecessary(*mTextEditor, mSavedFlags);
   }
 
 private:
   TextEditor* mTextEditor;
   uint32_t mSavedFlags;
   int32_t mSavedMaxLength;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
@@ -1477,45 +1491,49 @@ nsTextEditorState::PrepareEditor(const n
     if (element->HasAttr(kNameSpaceID_None, nsGkAtoms::disabled))
       editorFlags |= nsIPlaintextEditor::eEditorDisabledMask;
 
     // Disable the selection if necessary.
     if (newTextEditor->IsDisabled()) {
       mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_OFF);
     }
 
-    newTextEditor->SetFlags(editorFlags);
+    SetEditorFlagsIfNecessary(*newTextEditor, editorFlags);
   }
 
   if (shouldInitializeEditor) {
     // Hold on to the newly created editor
     preDestroyer.Swap(mTextEditor);
   }
 
   // If we have a default value, insert it under the div we created
   // above, but be sure to use the editor so that '*' characters get
   // displayed for password fields, etc. SetValue() will call the
   // editor for us.
 
   if (!defaultValue.IsEmpty()) {
-    rv = newTextEditor->SetFlags(editorFlags);
-    NS_ENSURE_SUCCESS(rv, rv);
+    rv = SetEditorFlagsIfNecessary(*newTextEditor, editorFlags);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
 
     // Now call SetValue() which will make the necessary editor calls to set
     // the default value.  Make sure to turn off undo before setting the default
     // value, and turn it back on afterwards. This will make sure we can't undo
     // past the default value.
     // So, we use eSetValue_Internal flag only that it will turn off undo.
 
     bool success = SetValue(defaultValue, eSetValue_Internal);
     NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
 
     // Now restore the original editor flags.
-    rv = newTextEditor->SetFlags(editorFlags);
-    NS_ENSURE_SUCCESS(rv, rv);
+    rv = SetEditorFlagsIfNecessary(*newTextEditor, editorFlags);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
   }
 
   if (IsPasswordTextControl()) {
     // Disable undo for <input type="password">.  Note that we want to do this
     // at the very end of InitEditor(), so the calls to EnableUndoRedo() when
     // setting the default value don't screw us up.  Since changing the
     // control type does a reframe, we don't have to worry about dynamic type
     // changes here.