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:
@@ -1802,7 +1802,7 @@ void nsContentUtils::Shutdown() {
|
|||||||
NS_RELEASE(sUserInteractionObserver);
|
NS_RELEASE(sUserInteractionObserver);
|
||||||
}
|
}
|
||||||
|
|
||||||
HTMLInputElement::Shutdown();
|
TextControlState::Shutdown();
|
||||||
nsMappedAttributes::Shutdown();
|
nsMappedAttributes::Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user