Bug 1395146 - part3: nsTextEditorState should cache empty value with mCachedValue r?smaug
nsTextEditorState::GetValue() uses mCachedValue only when the value isn't empty
string. However, with SetIsVoid() and IsVoid() of nsAString, nsTextEditorState
can cache empty value only with mCachedValue.
MozReview-Commit-ID: AmQDquEn9M8
--- a/dom/html/nsTextEditorState.cpp
+++ b/dom/html/nsTextEditorState.cpp
@@ -1181,16 +1181,17 @@ nsTextEditorState::Construct(nsITextCont
state->mEditorInitialized = false;
state->mInitializing = false;
state->mValueTransferInProgress = false;
state->mSelectionCached = true;
state->mSelectionRestoreEagerInit = false;
state->mPlaceholderVisibility = false;
state->mPreviewVisibility = false;
state->mIsCommittingComposition = false;
+ state->ClearValueCache();
// When adding more member variable initializations here, add the same
// also to the constructor.
return state;
}
return new nsTextEditorState(aOwningElement);
}
@@ -1407,16 +1408,18 @@ nsTextEditorState::PrepareEditor(const n
AutoHideSelectionChanges hideSelectionChanges(GetConstFrameSelection());
// Don't attempt to initialize recursively!
InitializationGuard guard(*this);
if (guard.IsInitializingRecursively()) {
return NS_ERROR_NOT_INITIALIZED;
}
+ ClearValueCache();
+
// Note that we don't check mTextEditor here, because we might already have
// one around, in which case we don't create a new one, and we'll just tie
// the required machinery to it.
nsPresContext *presContext = mBoundFrame->PresContext();
nsIPresShell *shell = presContext->GetPresShell();
// Setup the editor flags
@@ -2427,17 +2430,17 @@ nsTextEditorState::GetValue(nsAString& a
// been set to the editor yet.
if (mIsCommittingComposition) {
aValue = mValueBeingSet;
return;
}
if (mTextEditor && mBoundFrame &&
(mEditorInitialized || !IsSingleLineTextControl())) {
- if (aIgnoreWrap && !mCachedValue.IsEmpty()) {
+ if (aIgnoreWrap && !mCachedValue.IsVoid()) {
aValue = mCachedValue;
return;
}
aValue.Truncate(); // initialize out param
uint32_t flags = (nsIDocumentEncoder::OutputLFLineBreak |
nsIDocumentEncoder::OutputPreformatted |
@@ -2469,19 +2472,19 @@ nsTextEditorState::GetValue(nsAString& a
AutoNoJSAPI nojsapi;
mTextEditor->OutputToString(NS_LITERAL_STRING("text/plain"), flags,
aValue);
}
// Only when the result doesn't include line breaks caused by hard-wrap,
// mCacheValue should cache the value.
if (!(flags & nsIDocumentEncoder::OutputWrap)) {
- mCachedValue = aValue;
+ const_cast<nsTextEditorState*>(this)->SetValueCache(aValue);
} else {
- mCachedValue.Truncate();
+ const_cast<nsTextEditorState*>(this)->ClearValueCache();
}
} else {
if (!mTextCtrlElement->ValueChanged() || !mValue) {
mTextCtrlElement->GetDefaultValueFromContent(aValue);
} else {
aValue = *mValue;
}
}
@@ -2714,17 +2717,17 @@ nsTextEditorState::SetValue(const nsAStr
if (!mBoundFrame) {
return SetValue(newValue, aFlags & eSetValue_Notify);
}
return true;
}
// The new value never includes line breaks caused by hard-wrap.
// So, mCachedValue can always cache the new value.
- if (!mCachedValue.Assign(newValue, fallible)) {
+ if (!SetValueCache(newValue, fallible)) {
return false;
}
}
}
} else {
if (!mValue) {
mValue.emplace();
}
--- a/dom/html/nsTextEditorState.h
+++ b/dom/html/nsTextEditorState.h
@@ -250,17 +250,37 @@ public:
/**
* Get the maxlength attribute
* @param aMaxLength the value of the max length attr
* @returns false if attr not defined
*/
int32_t GetMaxLength();
- void ClearValueCache() { mCachedValue.Truncate(); }
+ void ClearValueCache()
+ {
+ mCachedValue.SetIsVoid(true);
+ MOZ_ASSERT(mCachedValue.IsEmpty());
+ }
+ void SetValueCache(const nsAString& aValue)
+ {
+ mCachedValue.Assign(aValue);
+ MOZ_ASSERT(!mCachedValue.IsVoid());
+ }
+ MOZ_MUST_USE bool
+ SetValueCache(const nsAString& aValue,
+ const mozilla::fallible_t& aFallible)
+ {
+ if (!mCachedValue.Assign(aValue, aFallible)) {
+ ClearValueCache();
+ return false;
+ }
+ MOZ_ASSERT(!mCachedValue.IsVoid());
+ return true;
+ }
void HideSelectionIfBlurred();
struct SelectionProperties {
public:
SelectionProperties() : mStart(0), mEnd(0),
mDirection(nsITextControlFrame::eForward) {}
bool IsDefault() const
@@ -455,21 +475,21 @@ private:
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;
// Cache of the |.value| of <input> or <textarea> element without hard-wrap.
- // If this is empty string, it doesn't cache |.value|.
+ // If its IsVoid() returns true, 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;
+ 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;