Bug 1597679 - part 3: Create methods to compare given string with values of TextControlState, nsTextControlFrame, HTMLInputElement and HTMLTextAreaElement r=smaug

For avoiding unnecessary copy of string buffer only for comparing setting
value and current value, especially with `nsAutoString`, this patch
creates `*Equals()` methods for every class.

And also this avoids to call `nsContentUtils::PlatformToDOMLineBreaks()` in
most paths.

Differential Revision: https://phabricator.services.mozilla.com/D54331
This commit is contained in:
Masayuki Nakano
2019-11-25 06:35:17 +00:00
parent 16fa2f8998
commit f93c719628
8 changed files with 125 additions and 40 deletions

View File

@@ -6756,6 +6756,11 @@ void HTMLInputElement::GetTextEditorValue(nsAString& aValue,
} }
} }
bool HTMLInputElement::TextEditorValueEquals(const nsAString& aValue) const {
TextControlState* state = GetEditorState();
return state ? state->ValueEquals(aValue) : aValue.IsEmpty();
}
void HTMLInputElement::InitializeKeyboardEventListeners() { void HTMLInputElement::InitializeKeyboardEventListeners() {
TextControlState* state = GetEditorState(); TextControlState* state = GetEditorState();
if (state) { if (state) {

View File

@@ -250,6 +250,13 @@ class HTMLInputElement final : public TextControlElement,
const nsAString& aValue) override; const nsAString& aValue) override;
virtual bool HasCachedSelection() override; virtual bool HasCachedSelection() override;
/**
* TextEditorValueEquals() is designed for internal use so that aValue
* shouldn't include \r character. It should be handled before calling this
* with nsContentUtils::PlatformToDOMLineBreaks().
*/
bool TextEditorValueEquals(const nsAString& aValue) const;
// Methods for nsFormFillController so it can do selection operations on input // Methods for nsFormFillController so it can do selection operations on input
// types the HTML spec doesn't support them on, like "email". // types the HTML spec doesn't support them on, like "email".
uint32_t GetSelectionStartIgnoringType(ErrorResult& aRv); uint32_t GetSelectionStartIgnoringType(ErrorResult& aRv);

View File

@@ -201,13 +201,8 @@ void HTMLTextAreaElement::GetType(nsAString& aType) {
} }
void HTMLTextAreaElement::GetValue(nsAString& aValue) { void HTMLTextAreaElement::GetValue(nsAString& aValue) {
nsAutoString value; GetValueInternal(aValue, true);
GetValueInternal(value, true); MOZ_ASSERT(aValue.FindChar(static_cast<char16_t>('\r')) == -1);
// Normalize CRLF and CR to LF
nsContentUtils::PlatformToDOMLineBreaks(value);
aValue = value;
} }
void HTMLTextAreaElement::GetValueInternal(nsAString& aValue, void HTMLTextAreaElement::GetValueInternal(nsAString& aValue,
@@ -216,6 +211,11 @@ void HTMLTextAreaElement::GetValueInternal(nsAString& aValue,
mState->GetValue(aValue, aIgnoreWrap); mState->GetValue(aValue, aIgnoreWrap);
} }
bool HTMLTextAreaElement::ValueEquals(const nsAString& aValue) const {
MOZ_ASSERT(mState);
return mState->ValueEquals(aValue);
}
TextEditor* HTMLTextAreaElement::GetTextEditor() { TextEditor* HTMLTextAreaElement::GetTextEditor() {
MOZ_ASSERT(mState); MOZ_ASSERT(mState);
return mState->GetTextEditor(); return mState->GetTextEditor();

View File

@@ -237,6 +237,12 @@ class HTMLTextAreaElement final : public TextControlElement,
void GetDefaultValue(nsAString& aDefaultValue, ErrorResult& aError); void GetDefaultValue(nsAString& aDefaultValue, ErrorResult& aError);
void SetDefaultValue(const nsAString& aDefaultValue, ErrorResult& aError); void SetDefaultValue(const nsAString& aDefaultValue, ErrorResult& aError);
void GetValue(nsAString& aValue); void GetValue(nsAString& aValue);
/**
* ValueEquals() is designed for internal use so that aValue shouldn't
* include \r character. It should be handled before calling this with
* nsContentUtils::PlatformToDOMLineBreaks().
*/
bool ValueEquals(const nsAString& aValue) const;
MOZ_CAN_RUN_SCRIPT_BOUNDARY MOZ_CAN_RUN_SCRIPT_BOUNDARY
void SetValue(const nsAString& aValue, ErrorResult& aError); void SetValue(const nsAString& aValue, ErrorResult& aError);

View File

@@ -2491,6 +2491,7 @@ void TextControlState::GetValue(nsAString& aValue, bool aIgnoreWrap) const {
if (mHandlingState && if (mHandlingState &&
mHandlingState->IsHandling(TextControlAction::CommitComposition)) { mHandlingState->IsHandling(TextControlAction::CommitComposition)) {
aValue = mHandlingState->GetSettingValue(); aValue = mHandlingState->GetSettingValue();
MOZ_ASSERT(aValue.FindChar(static_cast<char16_t>('\r')) == -1);
return; return;
} }
@@ -2498,6 +2499,7 @@ void TextControlState::GetValue(nsAString& aValue, bool aIgnoreWrap) const {
(mEditorInitialized || !IsSingleLineTextControl())) { (mEditorInitialized || !IsSingleLineTextControl())) {
if (aIgnoreWrap && !mBoundFrame->CachedValue().IsVoid()) { if (aIgnoreWrap && !mBoundFrame->CachedValue().IsVoid()) {
aValue = mBoundFrame->CachedValue(); aValue = mBoundFrame->CachedValue();
MOZ_ASSERT(aValue.FindChar(static_cast<char16_t>('\r')) == -1);
return; return;
} }
@@ -2532,6 +2534,7 @@ void TextControlState::GetValue(nsAString& aValue, bool aIgnoreWrap) const {
AutoNoJSAPI nojsapi; AutoNoJSAPI nojsapi;
DebugOnly<nsresult> rv = mTextEditor->ComputeTextValue(flags, aValue); DebugOnly<nsresult> rv = mTextEditor->ComputeTextValue(flags, aValue);
MOZ_ASSERT(aValue.FindChar(static_cast<char16_t>('\r')) == -1);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to get value"); NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to get value");
} }
// Only when the result doesn't include line breaks caused by hard-wrap, // Only when the result doesn't include line breaks caused by hard-wrap,
@@ -2543,13 +2546,27 @@ void TextControlState::GetValue(nsAString& aValue, bool aIgnoreWrap) const {
} }
} else { } else {
if (!mTextCtrlElement->ValueChanged() || !mValue) { if (!mTextCtrlElement->ValueChanged() || !mValue) {
mTextCtrlElement->GetDefaultValueFromContent(aValue); // Use nsString to avoid copying string buffer at setting aValue.
nsString value;
mTextCtrlElement->GetDefaultValueFromContent(value);
// TODO: We should make default value not include \r.
nsContentUtils::PlatformToDOMLineBreaks(value);
aValue = value;
} else { } else {
aValue = *mValue; aValue = *mValue;
MOZ_ASSERT(aValue.FindChar(static_cast<char16_t>('\r')) == -1);
} }
} }
} }
bool TextControlState::ValueEquals(const nsAString& aValue) const {
// We can avoid copying string buffer in many cases. Therefore, we should
// use nsString rather than nsAutoString here.
nsString value;
GetValue(value, true);
return aValue.Equals(value);
}
#ifdef DEBUG #ifdef DEBUG
// @param aFlags TextControlState::SetValueFlags // @param aFlags TextControlState::SetValueFlags
bool AreFlagsNotDemandingContradictingMovements(uint32_t aFlags) { bool AreFlagsNotDemandingContradictingMovements(uint32_t aFlags) {
@@ -2596,17 +2613,12 @@ bool TextControlState::SetValue(const nsAString& aValue,
} else { } else {
// If setting value won't change current value, we shouldn't commit // If setting value won't change current value, we shouldn't commit
// composition for compatibility with the other browsers. // composition for compatibility with the other browsers.
nsAutoString currentValue; MOZ_ASSERT(!aOldValue || mBoundFrame->TextEquals(*aOldValue));
if (aOldValue) { bool isSameAsCurrentValue =
#ifdef DEBUG aOldValue
mBoundFrame->GetText(currentValue); ? aOldValue->Equals(handlingSetValue.GetSettingValue())
MOZ_ASSERT(currentValue.Equals(*aOldValue)); : mBoundFrame->TextEquals(handlingSetValue.GetSettingValue());
#endif if (isSameAsCurrentValue) {
currentValue.Assign(*aOldValue);
} else {
mBoundFrame->GetText(currentValue);
}
if (handlingSetValue.GetSettingValue() == currentValue) {
// Note that in this case, we shouldn't fire any events with setting // Note that in this case, we shouldn't fire any events with setting
// value because event handlers may try to set value recursively but // value because event handlers may try to set value recursively but
// we cannot commit composition at that time due to unsafe to run // we cannot commit composition at that time due to unsafe to run
@@ -2691,19 +2703,16 @@ bool TextControlState::SetValueWithTextEditor(
} }
#endif #endif
nsAutoString currentValue; MOZ_ASSERT(!aHandlingSetValue.GetOldValue() ||
if (aHandlingSetValue.GetOldValue()) { mBoundFrame->TextEquals(*aHandlingSetValue.GetOldValue()));
#ifdef DEBUG bool isSameAsCurrentValue =
mBoundFrame->GetText(currentValue); aHandlingSetValue.GetOldValue()
MOZ_ASSERT(currentValue.Equals(*aHandlingSetValue.GetOldValue())); ? aHandlingSetValue.GetOldValue()->Equals(
#endif aHandlingSetValue.GetSettingValue())
currentValue.Assign(*aHandlingSetValue.GetOldValue()); : mBoundFrame->TextEquals(aHandlingSetValue.GetSettingValue());
} else {
mBoundFrame->GetText(currentValue);
}
// this is necessary to avoid infinite recursion // this is necessary to avoid infinite recursion
if (currentValue == aHandlingSetValue.GetSettingValue()) { if (isSameAsCurrentValue) {
return true; return true;
} }
@@ -2763,6 +2772,13 @@ bool TextControlState::SetValueWithTextEditor(
// } // }
// However, this path won't be used in web content anymore. // However, this path won't be used in web content anymore.
nsCOMPtr<nsISelectionController> kungFuDeathGrip = mSelCon.get(); nsCOMPtr<nsISelectionController> kungFuDeathGrip = mSelCon.get();
// Use nsString to avoid copying string buffer in most cases.
nsString currentValue;
if (aHandlingSetValue.GetOldValue()) {
currentValue.Assign(*aHandlingSetValue.GetOldValue());
} else {
mBoundFrame->GetText(currentValue);
}
uint32_t currentLength = currentValue.Length(); uint32_t currentLength = currentValue.Length();
uint32_t newlength = aHandlingSetValue.GetSettingValue().Length(); uint32_t newlength = aHandlingSetValue.GetSettingValue().Length();
if (!currentLength || if (!currentLength ||

View File

@@ -208,6 +208,15 @@ class TextControlState final : public SupportsWeakPtr<TextControlState> {
// it. // it.
eSetValue_MoveCursorToBeginSetSelectionDirectionForward = 1 << 6, eSetValue_MoveCursorToBeginSetSelectionDirectionForward = 1 << 6,
}; };
/**
* SetValue() sets the value to aValue with replacing \r\n and \r with \n.
*
* @param aValue The new value. Can contain \r.
* @param aOldValue Optional. If you have already know current value,
* set this to it. However, this must not contain \r
* for the performance.
* @param aFlags See SetValueFlags.
*/
MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE bool SetValue(const nsAString& aValue, MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE bool SetValue(const nsAString& aValue,
const nsAString* aOldValue, const nsAString* aOldValue,
uint32_t aFlags); uint32_t aFlags);
@@ -215,7 +224,17 @@ class TextControlState final : public SupportsWeakPtr<TextControlState> {
uint32_t aFlags) { uint32_t aFlags) {
return SetValue(aValue, nullptr, aFlags); return SetValue(aValue, nullptr, aFlags);
} }
/**
* GetValue() returns current value either with or without TextEditor.
* The result never includes \r.
*/
void GetValue(nsAString& aValue, bool aIgnoreWrap) const; void GetValue(nsAString& aValue, bool aIgnoreWrap) const;
/**
* ValueEquals() is designed for internal use so that aValue shouldn't
* include \r character. It should be handled before calling this with
* nsContentUtils::PlatformToDOMLineBreaks().
*/
bool ValueEquals(const nsAString& aValue) const;
bool HasNonEmptyValue(); bool HasNonEmptyValue();
// The following methods are for textarea element to use whether default // The following methods are for textarea element to use whether default
// value or not. // value or not.

View File

@@ -1124,18 +1124,43 @@ nsresult nsTextControlFrame::AttributeChanged(int32_t aNameSpaceID,
} }
void nsTextControlFrame::GetText(nsString& aText) { void nsTextControlFrame::GetText(nsString& aText) {
TextControlElement* textControlElement = if (HTMLInputElement* inputElement = HTMLInputElement::FromNode(mContent)) {
TextControlElement::FromNode(GetContent()); if (IsSingleLineTextControl()) {
MOZ_ASSERT(textControlElement); // There will be no line breaks so we can ignore the wrap property.
if (IsSingleLineTextControl()) { inputElement->GetTextEditorValue(aText, true);
// There will be no line breaks so we can ignore the wrap property. return;
textControlElement->GetTextEditorValue(aText, true);
} else {
HTMLTextAreaElement* textArea = HTMLTextAreaElement::FromNode(mContent);
if (textArea) {
textArea->GetValue(aText);
} }
aText.Truncate();
return;
} }
MOZ_ASSERT(!IsSingleLineTextControl());
if (HTMLTextAreaElement* textAreaElement =
HTMLTextAreaElement::FromNode(mContent)) {
textAreaElement->GetValue(aText);
return;
}
MOZ_ASSERT(aText.IsEmpty());
aText.Truncate();
}
bool nsTextControlFrame::TextEquals(const nsAString& aText) const {
if (HTMLInputElement* inputElement = HTMLInputElement::FromNode(mContent)) {
if (IsSingleLineTextControl()) {
// There will be no line breaks so we can ignore the wrap property.
return inputElement->TextEditorValueEquals(aText);
}
return aText.IsEmpty();
}
MOZ_ASSERT(!IsSingleLineTextControl());
if (HTMLTextAreaElement* textAreaElement =
HTMLTextAreaElement::FromNode(mContent)) {
return textAreaElement->ValueEquals(aText);
}
return aText.IsEmpty();
} }
/// END NSIFRAME OVERLOADS /// END NSIFRAME OVERLOADS

View File

@@ -163,6 +163,13 @@ class nsTextControlFrame final : public nsContainerFrame,
void GetText(nsString& aText); void GetText(nsString& aText);
/**
* TextEquals() is designed for internal use so that aValue shouldn't
* include \r character. It should be handled before calling this with
* nsContentUtils::PlatformToDOMLineBreaks().
*/
bool TextEquals(const nsAString& aText) const;
virtual nsresult PeekOffset(nsPeekOffsetStruct* aPos) override; virtual nsresult PeekOffset(nsPeekOffsetStruct* aPos) override;
NS_DECL_QUERYFRAME NS_DECL_QUERYFRAME