Bug 1588745 - part 4: Make TextControlState reuse its instance by itself r=Ehsan

Currently, only `HTMLInputElement` reuses `TextControlState` instance since
`HTMLTextAreaElement` had the instance as a member rather than allocate it.

Now, all instances are allocated in the heap independently for guaranteeing
their lifetime.  So, the reuse mechanism should be managed by
`TextControlState` itself.

Depends on D51393

Differential Revision: https://phabricator.services.mozilla.com/D51394
This commit is contained in:
Masayuki Nakano
2019-11-01 20:51:48 +00:00
parent c14ab80c37
commit 55109d6fed
5 changed files with 58 additions and 50 deletions

View File

@@ -1802,7 +1802,7 @@ void nsContentUtils::Shutdown() {
NS_RELEASE(sUserInteractionObserver); NS_RELEASE(sUserInteractionObserver);
} }
HTMLInputElement::Shutdown(); TextControlState::Shutdown();
nsMappedAttributes::Shutdown(); nsMappedAttributes::Shutdown();
} }

View File

@@ -937,28 +937,6 @@ static nsresult FireEventForAccessibility(HTMLInputElement* aTarget,
EventMessage aEventMessage); EventMessage aEventMessage);
#endif #endif
TextControlState* HTMLInputElement::sCachedTextControlState = nullptr;
bool HTMLInputElement::sShutdown = false;
/* static */
void HTMLInputElement::ReleaseTextControlState(TextControlState* aState) {
if (!sShutdown && !sCachedTextControlState && !aState->IsBusy()) {
aState->PrepareForReuse();
sCachedTextControlState = aState;
} else {
aState->Destroy();
}
}
/* static */
void HTMLInputElement::Shutdown() {
sShutdown = true;
if (sCachedTextControlState) {
sCachedTextControlState->Destroy();
sCachedTextControlState = nullptr;
}
}
// //
// construction, destruction // construction, destruction
// //
@@ -1001,8 +979,7 @@ HTMLInputElement::HTMLInputElement(
"performance regression!"); "performance regression!");
// We are in a type=text so we now we currenty need a TextControlState. // We are in a type=text so we now we currenty need a TextControlState.
mInputData.mState = mInputData.mState = TextControlState::Construct(this);
TextControlState::Construct(this, &sCachedTextControlState);
void* memory = mInputTypeMem; void* memory = mInputTypeMem;
mInputType = InputType::Create(this, mType, memory); mInputType = InputType::Create(this, mType, memory);
@@ -1033,7 +1010,7 @@ void HTMLInputElement::FreeData() {
mInputData.mValue = nullptr; mInputData.mValue = nullptr;
} else { } else {
UnbindFromFrame(nullptr); UnbindFromFrame(nullptr);
ReleaseTextControlState(mInputData.mState); mInputData.mState->Destroy();
mInputData.mState = nullptr; mInputData.mState = nullptr;
} }
@@ -4515,8 +4492,7 @@ void HTMLInputElement::HandleTypeChange(uint8_t aNewType, bool aNotify) {
mInputType = InputType::Create(this, mType, memory); mInputType = InputType::Create(this, mType, memory);
if (IsSingleLineTextControl()) { if (IsSingleLineTextControl()) {
mInputData.mState = mInputData.mState = TextControlState::Construct(this);
TextControlState::Construct(this, &sCachedTextControlState);
if (!sp.IsDefault()) { if (!sp.IsDefault()) {
mInputData.mState->SetSelectionProperties(sp); mInputData.mState->SetSelectionProperties(sp);
} }

View File

@@ -846,8 +846,6 @@ class HTMLInputElement final : public nsGenericHTMLFormElementWithState,
void UpdateEntries( void UpdateEntries(
const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories); const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories);
static void Shutdown();
/** /**
* Returns if the required attribute applies for the current type. * Returns if the required attribute applies for the current type.
*/ */
@@ -1660,11 +1658,6 @@ class HTMLInputElement final : public nsGenericHTMLFormElementWithState,
nsCOMPtr<nsIFilePicker> mFilePicker; nsCOMPtr<nsIFilePicker> mFilePicker;
RefPtr<HTMLInputElement> mInput; RefPtr<HTMLInputElement> mInput;
}; };
static void ReleaseTextControlState(TextControlState* aState);
static TextControlState* sCachedTextControlState;
static bool sShutdown;
}; };
} // namespace dom } // namespace dom

View File

@@ -1127,7 +1127,7 @@ class MOZ_STACK_CLASS AutoTextControlHandlingState {
~AutoTextControlHandlingState() { ~AutoTextControlHandlingState() {
mTextControlState.mHandlingState = mParent; mTextControlState.mHandlingState = mParent;
if (!mParent && mTextControlStateDestroyed) { if (!mParent && mTextControlStateDestroyed) {
mTextControlState.Destroy(); mTextControlState.DeleteOrCacheForReuse();
} }
} }
@@ -1180,6 +1180,9 @@ class MOZ_STACK_CLASS AutoTextControlHandlingState {
* mozilla::TextControlState * mozilla::TextControlState
*****************************************************************************/ *****************************************************************************/
TextControlState* TextControlState::sReleasedInstance = nullptr;
bool TextControlState::sHasShutDown = false;
TextControlState::TextControlState(nsITextControlElement* aOwningElement) TextControlState::TextControlState(nsITextControlElement* aOwningElement)
: mTextCtrlElement(aOwningElement), : mTextCtrlElement(aOwningElement),
mBoundFrame(nullptr), mBoundFrame(nullptr),
@@ -1199,11 +1202,11 @@ TextControlState::TextControlState(nsITextControlElement* aOwningElement)
} }
TextControlState* TextControlState::Construct( TextControlState* TextControlState::Construct(
nsITextControlElement* aOwningElement, TextControlState** aReusedState) { nsITextControlElement* aOwningElement) {
if (aReusedState && *aReusedState) { if (sReleasedInstance) {
TextControlState* state = *aReusedState; MOZ_ASSERT(!sReleasedInstance->IsBusy());
MOZ_ASSERT(!state->IsBusy()); TextControlState* state = sReleasedInstance;
*aReusedState = nullptr; sReleasedInstance = nullptr;
state->mTextCtrlElement = aOwningElement; state->mTextCtrlElement = aOwningElement;
state->mBoundFrame = nullptr; state->mBoundFrame = nullptr;
state->mSelectionProperties = SelectionProperties(); state->mSelectionProperties = SelectionProperties();
@@ -1228,12 +1231,32 @@ TextControlState::~TextControlState() {
Clear(); Clear();
} }
void TextControlState::Shutdown() {
sHasShutDown = true;
if (sReleasedInstance) {
sReleasedInstance->DeleteOrCacheForReuse();
sReleasedInstance = nullptr;
}
}
void TextControlState::Destroy() { void TextControlState::Destroy() {
// If we're handling something, we should be deleted later. // If we're handling something, we should be deleted later.
if (mHandlingState) { if (mHandlingState) {
mHandlingState->OnDestroyTextControlState(); mHandlingState->OnDestroyTextControlState();
return; return;
} }
DeleteOrCacheForReuse();
}
void TextControlState::DeleteOrCacheForReuse() {
MOZ_ASSERT(!IsBusy());
// If we can cache this instance, we should do it instead of deleting it.
if (!sHasShutDown && !sReleasedInstance) {
sReleasedInstance = this;
sReleasedInstance->PrepareForReuse();
return;
}
delete this; delete this;
} }

View File

@@ -141,8 +141,9 @@ class TextControlState final : public SupportsWeakPtr<TextControlState> {
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(TextControlState) MOZ_DECLARE_WEAKREFERENCE_TYPENAME(TextControlState)
static TextControlState* Construct(nsITextControlElement* aOwningElement, static TextControlState* Construct(nsITextControlElement* aOwningElement);
TextControlState** aReusedState = nullptr);
static void Shutdown();
/** /**
* Destroy() deletes the instance immediately or later. * Destroy() deletes the instance immediately or later.
@@ -161,13 +162,6 @@ class TextControlState final : public SupportsWeakPtr<TextControlState> {
bool IsBusy() const { return !!mHandlingState || mValueTransferInProgress; } bool IsBusy() const { return !!mHandlingState || mValueTransferInProgress; }
void PrepareForReuse() {
MOZ_ASSERT(!IsBusy());
Unlink();
mValue.reset();
mTextCtrlElement = nullptr;
}
TextEditor* GetTextEditor(); TextEditor* GetTextEditor();
TextEditor* GetTextEditorWithoutCreation(); TextEditor* GetTextEditorWithoutCreation();
nsISelectionController* GetSelectionController() const; nsISelectionController* GetSelectionController() const;
@@ -386,6 +380,18 @@ class TextControlState final : public SupportsWeakPtr<TextControlState> {
explicit TextControlState(nsITextControlElement* aOwningElement); explicit TextControlState(nsITextControlElement* aOwningElement);
MOZ_CAN_RUN_SCRIPT_BOUNDARY ~TextControlState(); MOZ_CAN_RUN_SCRIPT_BOUNDARY ~TextControlState();
/**
* Delete the instance or cache to reuse it if possible.
*/
void DeleteOrCacheForReuse();
void PrepareForReuse() {
MOZ_ASSERT(!IsBusy());
Unlink();
mValue.reset();
mTextCtrlElement = nullptr;
}
void ValueWasChanged(bool aNotify); void ValueWasChanged(bool aNotify);
MOZ_CAN_RUN_SCRIPT void DestroyEditor(); MOZ_CAN_RUN_SCRIPT void DestroyEditor();
@@ -425,6 +431,16 @@ class TextControlState final : public SupportsWeakPtr<TextControlState> {
bool mPlaceholderVisibility; bool mPlaceholderVisibility;
bool mPreviewVisibility; bool mPreviewVisibility;
/**
* For avoiding allocation cost of the instance, we should reuse instances
* as far as possible.
* TODO: Maybe, we should cache more instances with array. Then, it must
* be faster to load pages which have a lot of `<input type="text">`
* elements.
*/
static TextControlState* sReleasedInstance;
static bool sHasShutDown;
friend class AutoTextControlHandlingState; friend class AutoTextControlHandlingState;
friend class PrepareEditorEvent; friend class PrepareEditorEvent;
friend class RestoreSelectionState; friend class RestoreSelectionState;