Bug 1352687, try to recycle HTMLInputElement's nsTextEditorState, r=baku
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Mon, 03 Apr 2017 20:40:48 +0300
changeset 351061 dbe1d8e2e07fb96a119b1119d81751e29114168c
parent 351060 b26588027af0358884915cb5a09b52a9e48ac815
child 351062 1825773142a096686e3be6cf5d94bc5b5930a175
push id31599
push usercbook@mozilla.com
push dateTue, 04 Apr 2017 10:35:26 +0000
treeherdermozilla-central@891981e67948 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1352687
milestone55.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 1352687, try to recycle HTMLInputElement's nsTextEditorState, r=baku
dom/base/nsContentUtils.cpp
dom/html/HTMLInputElement.cpp
dom/html/HTMLInputElement.h
dom/html/nsTextEditorState.cpp
dom/html/nsTextEditorState.h
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -39,16 +39,17 @@
 #include "mozilla/LoadInfo.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/CustomElementRegistry.h"
 #include "mozilla/dom/DocumentFragment.h"
 #include "mozilla/dom/DOMTypes.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/FileSystemSecurity.h"
 #include "mozilla/dom/FileBlobImpl.h"
+#include "mozilla/dom/HTMLInputElement.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/HTMLTemplateElement.h"
 #include "mozilla/dom/HTMLContentElement.h"
 #include "mozilla/dom/HTMLShadowElement.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ScriptSettings.h"
@@ -1985,16 +1986,18 @@ nsContentUtils::Shutdown()
   delete sOSText;
   sOSText = nullptr;
   delete sAltText;
   sAltText = nullptr;
   delete sModifierSeparator;
   sModifierSeparator = nullptr;
 
   NS_IF_RELEASE(sSameOriginChecker);
+
+  HTMLInputElement::Shutdown();
 }
 
 /**
  * Checks whether two nodes come from the same origin. aTrustedNode is
  * considered 'safe' in that a user can operate on it and that it isn't
  * a js-object that implements nsIDOMNode.
  * Never call this function with the first node provided by script, it
  * must always be known to be a 'real' node!
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -1043,16 +1043,38 @@ UploadLastDir::Observe(nsISupports* aSub
 
 #ifdef ACCESSIBILITY
 //Helper method
 static nsresult FireEventForAccessibility(nsIDOMHTMLInputElement* aTarget,
                                           nsPresContext* aPresContext,
                                           const nsAString& aEventType);
 #endif
 
+nsTextEditorState* HTMLInputElement::sCachedTextEditorState = nullptr;
+bool HTMLInputElement::sShutdown = false;
+
+/* static */ void
+HTMLInputElement::ReleaseTextEditorState(nsTextEditorState* aState)
+{
+  if (!sShutdown && !sCachedTextEditorState) {
+    aState->PrepareForReuse();
+    sCachedTextEditorState = aState;
+  } else {
+    delete aState;
+  }
+}
+
+/* static */ void
+HTMLInputElement::Shutdown()
+{
+  sShutdown = true;
+  delete sCachedTextEditorState;
+  sCachedTextEditorState = nullptr;
+}
+
 //
 // construction, destruction
 //
 
 HTMLInputElement::HTMLInputElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
                                    FromParser aFromParser, FromClone aFromClone)
   : nsGenericHTMLFormElementWithState(aNodeInfo, kInputDefaultType->value)
   , mAutocompleteAttrState(nsContentUtils::eAutocompleteAttrState_Unknown)
@@ -1074,17 +1096,18 @@ HTMLInputElement::HTMLInputElement(alrea
   , mHasRange(false)
   , mIsDraggingRange(false)
   , mNumberControlSpinnerIsSpinning(false)
   , mNumberControlSpinnerSpinsUp(false)
   , mPickerRunning(false)
   , mSelectionCached(true)
 {
   // We are in a type=text so we now we currenty need a nsTextEditorState.
-  mInputData.mState = new nsTextEditorState(this);
+  mInputData.mState =
+    nsTextEditorState::Construct(this, &sCachedTextEditorState);
 
   if (!gUploadLastDir)
     HTMLInputElement::InitUploadLastDir();
 
   // Set up our default state.  By default we're enabled (since we're
   // a control type that can be disabled but not actually disabled
   // right now), optional, and valid.  We are NOT readwrite by default
   // until someone calls UpdateEditableState on us, apparently!  Also
@@ -1107,17 +1130,17 @@ HTMLInputElement::~HTMLInputElement()
 void
 HTMLInputElement::FreeData()
 {
   if (!IsSingleLineTextControl(false)) {
     free(mInputData.mValue);
     mInputData.mValue = nullptr;
   } else {
     UnbindFromFrame(nullptr);
-    delete mInputData.mState;
+    ReleaseTextEditorState(mInputData.mState);
     mInputData.mState = nullptr;
   }
 }
 
 nsTextEditorState*
 HTMLInputElement::GetEditorState() const
 {
   if (!IsSingleLineTextControl(false)) {
@@ -5022,17 +5045,18 @@ HTMLInputElement::HandleTypeChange(uint8
   }
 
   // We already have a copy of the value, lets free it and changes the type.
   FreeData();
   mType = aNewType;
 
   if (IsSingleLineTextControl()) {
 
-    mInputData.mState = new nsTextEditorState(this);
+    mInputData.mState =
+      nsTextEditorState::Construct(this, &sCachedTextEditorState);
     if (!sp.IsDefault()) {
       mInputData.mState->SetSelectionProperties(sp);
     }
   }
 
   /**
    * The following code is trying to reproduce the algorithm described here:
    * http://www.whatwg.org/specs/web-apps/current-work/complete.html#input-type-change
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -853,16 +853,18 @@ public:
    *
    * then this function will return the number parsed as a Decimal, otherwise
    * it will return a Decimal for which Decimal::isFinite() will return false.
    */
   static Decimal StringToDecimal(const nsAString& aValue);
 
   void UpdateEntries(const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories);
 
+  static void Shutdown();
+
 protected:
   virtual ~HTMLInputElement();
 
   virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   // Pull IsSingleLineTextControl into our scope, otherwise it'd be hidden
   // by the nsITextControlElement version.
   using nsGenericHTMLFormElementWithState::IsSingleLineTextControl;
@@ -1766,14 +1768,19 @@ private:
     NS_DECL_ISUPPORTS
 
     NS_IMETHOD Done(int16_t aResult) override;
 
   private:
     nsCOMPtr<nsIFilePicker> mFilePicker;
     RefPtr<HTMLInputElement> mInput;
   };
+
+  static void ReleaseTextEditorState(nsTextEditorState* aState);
+
+  static nsTextEditorState* sCachedTextEditorState;
+  static bool sShutdown;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/dom/html/nsTextEditorState.cpp
+++ b/dom/html/nsTextEditorState.cpp
@@ -1063,20 +1063,48 @@ nsTextEditorState::nsTextEditorState(nsI
   , mEverInited(false)
   , mEditorInitialized(false)
   , mInitializing(false)
   , mValueTransferInProgress(false)
   , mSelectionCached(true)
   , mSelectionRestoreEagerInit(false)
   , mPlaceholderVisibility(false)
   , mIsCommittingComposition(false)
+  // When adding more member variable initializations here, add the same
+  // also to ::Construct.
 {
   MOZ_COUNT_CTOR(nsTextEditorState);
 }
 
+nsTextEditorState*
+nsTextEditorState::Construct(nsITextControlElement* aOwningElement,
+                             nsTextEditorState** aReusedState)
+{
+  if (*aReusedState) {
+    nsTextEditorState* state = *aReusedState;
+    *aReusedState = nullptr;
+    state->mTextCtrlElement = aOwningElement;
+    state->mBoundFrame = nullptr;
+    state->mSelectionProperties = SelectionProperties();
+    state->mEverInited = false;
+    state->mEditorInitialized = false;
+    state->mInitializing = false;
+    state->mValueTransferInProgress = false;
+    state->mSelectionCached = true;
+    state->mSelectionRestoreEagerInit = false;
+    state->mPlaceholderVisibility = false;
+    state->mIsCommittingComposition = false;
+    // When adding more member variable initializations here, add the same
+    // also to the constructor.
+    return state;
+  }
+
+  return new nsTextEditorState(aOwningElement);
+}
+
 nsTextEditorState::~nsTextEditorState()
 {
   MOZ_COUNT_DTOR(nsTextEditorState);
   Clear();
 }
 
 void
 nsTextEditorState::Clear()
--- a/dom/html/nsTextEditorState.h
+++ b/dom/html/nsTextEditorState.h
@@ -130,21 +130,34 @@ class HTMLInputElement;
  */
 
 class RestoreSelectionState;
 
 class nsTextEditorState : public mozilla::SupportsWeakPtr<nsTextEditorState> {
 public:
   MOZ_DECLARE_WEAKREFERENCE_TYPENAME(nsTextEditorState)
   explicit nsTextEditorState(nsITextControlElement* aOwningElement);
+  static nsTextEditorState*
+  Construct(nsITextControlElement* aOwningElement,
+            nsTextEditorState** aReusedState);
   ~nsTextEditorState();
 
   void Traverse(nsCycleCollectionTraversalCallback& cb);
   void Unlink();
 
+  void PrepareForReuse()
+  {
+    Unlink();
+    mValue.reset();
+    mCachedValue.Truncate();
+    mValueBeingSet.Truncate();
+    mTextCtrlElement = nullptr;
+    MOZ_ASSERT(!mMutationObserver);
+  }
+
   nsIEditor* GetEditor();
   nsISelectionController* GetSelectionController() const;
   nsFrameSelection* GetConstFrameSelection();
   nsresult BindToFrame(nsTextControlFrame* aFrame);
   void UnbindFromFrame(nsTextControlFrame* aFrame);
   nsresult PrepareEditor(const nsAString *aValue = nullptr);
   void InitializeKeyboardEventListeners();
 
@@ -396,17 +409,17 @@ private:
     nsTextEditorState& mState;
     bool mGuardSet;
   };
   friend class InitializationGuard;
   friend class PrepareEditorEvent;
 
   // The text control element owns this object, and ensures that this object
   // has a smaller lifetime.
-  nsITextControlElement* const MOZ_NON_OWNING_REF mTextCtrlElement;
+  nsITextControlElement* MOZ_NON_OWNING_REF mTextCtrlElement;
   // mSelCon is non-null while we have an mBoundFrame.
   RefPtr<nsTextInputSelectionImpl> mSelCon;
   RefPtr<RestoreSelectionState> mRestoringSelection;
   nsCOMPtr<nsIEditor> mEditor;
   nsCOMPtr<mozilla::dom::Element> mRootNode;
   nsCOMPtr<mozilla::dom::Element> mPlaceholderDiv;
   nsTextControlFrame* mBoundFrame;
   RefPtr<nsTextInputListener> mTextListener;