Bug 1395146 - part2: Make nsTextEditorState cache the value of TextEditor with mCachedValue even when the editor is single line text control r?smaug
Currently, nsTextEditorState caches the value of TextEditor only when it's for
a multi-line text control, i.e., <textarea>. However, using it for single-line
text control improves the score of attachment 8848015. Although this might
increase the cost of creating and registering mutation observer, but it may not
make damage to performance of loading pages since editor for each element won't
be initialized until each element gets focus.
MozReview-Commit-ID: DnjEJ5AUh3M
--- a/dom/html/nsTextEditorState.cpp
+++ b/dom/html/nsTextEditorState.cpp
@@ -2279,21 +2279,18 @@ nsTextEditorState::CreateRootNode()
nodeInfo = doc->NodeInfoManager()->GetNodeInfo(nsGkAtoms::div, nullptr,
kNameSpaceID_XHTML,
nsIDOMNode::ELEMENT_NODE);
nsresult rv = NS_NewHTMLElement(getter_AddRefs(mRootNode), nodeInfo.forget(),
NOT_FROM_PARSER);
NS_ENSURE_SUCCESS(rv, rv);
- if (!IsSingleLineTextControl()) {
- mMutationObserver = new nsAnonDivObserver(this);
- mRootNode->AddMutationObserver(mMutationObserver);
- }
-
+ mMutationObserver = new nsAnonDivObserver(this);
+ mRootNode->AddMutationObserver(mMutationObserver);
return rv;
}
nsresult
nsTextEditorState::InitializeRootNode()
{
// Make our root node editable
mRootNode->SetFlags(NODE_IS_EDITABLE);
@@ -2430,18 +2427,17 @@ nsTextEditorState::GetValue(nsAString& a
// been set to the editor yet.
if (mIsCommittingComposition) {
aValue = mValueBeingSet;
return;
}
if (mTextEditor && mBoundFrame &&
(mEditorInitialized || !IsSingleLineTextControl())) {
- bool canCache = aIgnoreWrap && !IsSingleLineTextControl();
- if (canCache && !mCachedValue.IsEmpty()) {
+ if (aIgnoreWrap && !mCachedValue.IsEmpty()) {
aValue = mCachedValue;
return;
}
aValue.Truncate(); // initialize out param
uint32_t flags = (nsIDocumentEncoder::OutputLFLineBreak |
nsIDocumentEncoder::OutputPreformatted |
@@ -2470,17 +2466,19 @@ nsTextEditorState::GetValue(nsAString& a
// if plaintext editor didn't create <br> nodes all over), we wouldn't need
// this.
{ /* Scope for AutoNoJSAPI. */
AutoNoJSAPI nojsapi;
mTextEditor->OutputToString(NS_LITERAL_STRING("text/plain"), flags,
aValue);
}
- if (canCache) {
+ // Only when the result doesn't include line breaks caused by hard-wrap,
+ // mCacheValue should cache the value.
+ if (!(flags & nsIDocumentEncoder::OutputWrap)) {
mCachedValue = aValue;
} else {
mCachedValue.Truncate();
}
} else {
if (!mTextCtrlElement->ValueChanged() || !mValue) {
mTextCtrlElement->GetDefaultValueFromContent(aValue);
} else {
@@ -2714,20 +2712,20 @@ nsTextEditorState::SetValue(const nsAStr
// the existing selection -- see bug 574558), in which case we don't
// need to reset the value here.
if (!mBoundFrame) {
return SetValue(newValue, aFlags & eSetValue_Notify);
}
return true;
}
- if (!IsSingleLineTextControl()) {
- if (!mCachedValue.Assign(newValue, fallible)) {
- return false;
- }
+ // The new value never includes line breaks caused by hard-wrap.
+ // So, mCachedValue can always cache the new value.
+ if (!mCachedValue.Assign(newValue, fallible)) {
+ return false;
}
}
}
} else {
if (!mValue) {
mValue.emplace();
}
--- a/dom/html/nsTextEditorState.h
+++ b/dom/html/nsTextEditorState.h
@@ -454,17 +454,22 @@ private:
RefPtr<mozilla::TextEditor> mTextEditor;
nsCOMPtr<mozilla::dom::Element> mRootNode;
nsCOMPtr<mozilla::dom::Element> mPlaceholderDiv;
nsCOMPtr<mozilla::dom::Element> mPreviewDiv;
nsTextControlFrame* mBoundFrame;
RefPtr<nsTextInputListener> mTextListener;
mozilla::Maybe<nsString> mValue;
RefPtr<nsAnonDivObserver> mMutationObserver;
- mutable nsString mCachedValue; // Caches non-hard-wrapped value on a multiline control.
+ // Cache of the |.value| of <input> or <textarea> element without hard-wrap.
+ // If this is empty string, it doesn't cache |.value|.
+ // Otherwise, it's cached when setting specific value or getting value from
+ // TextEditor. Additionally, when contents in the anonymous <div> element
+ // is modified, this is cleared.
+ mutable nsString mCachedValue;
// mValueBeingSet is available only while SetValue() is requesting to commit
// composition. I.e., this is valid only while mIsCommittingComposition is
// true. While active composition is being committed, GetValue() needs
// the latest value which is set by SetValue(). So, this is cache for that.
nsString mValueBeingSet;
SelectionProperties mSelectionProperties;
bool mEverInited; // Have we ever been initialized?
bool mEditorInitialized;