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
--- 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.