mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-20 00:20:37 +00:00
Bug 1833181 - Avoid attr lookups to check whether input value is empty. r=smaug
Differential Revision: https://phabricator.services.mozilla.com/D178078
This commit is contained in:
parent
e7716be371
commit
f37d9339a7
@ -171,7 +171,8 @@ bitflags! {
|
||||
Self::MODAL.bits |
|
||||
Self::INERT.bits |
|
||||
Self::TOPMOST_MODAL.bits |
|
||||
Self::REVEALED.bits;
|
||||
Self::REVEALED.bits |
|
||||
Self::VALUE_EMPTY.bits;
|
||||
|
||||
const INTRINSIC_STATES = !Self::EXTERNALLY_MANAGED_STATES.bits;
|
||||
}
|
||||
|
@ -1035,7 +1035,7 @@ HTMLInputElement::HTMLInputElement(already_AddRefed<dom::NodeInfo>&& aNodeInfo,
|
||||
// until someone calls UpdateEditableState on us, apparently! Also
|
||||
// by default we don't have to show validity UI and so forth.
|
||||
AddStatesSilently(ElementState::ENABLED | ElementState::OPTIONAL_ |
|
||||
ElementState::VALID);
|
||||
ElementState::VALID | ElementState::VALUE_EMPTY);
|
||||
UpdateApzAwareFlag();
|
||||
}
|
||||
|
||||
@ -1571,16 +1571,6 @@ void HTMLInputElement::GetNonFileValueInternal(nsAString& aValue) const {
|
||||
}
|
||||
}
|
||||
|
||||
bool HTMLInputElement::IsValueEmpty() const {
|
||||
if (GetValueMode() == VALUE_MODE_VALUE && IsSingleLineTextControl(false)) {
|
||||
return !mInputData.mState->HasNonEmptyValue();
|
||||
}
|
||||
|
||||
nsAutoString value;
|
||||
GetNonFileValueInternal(value);
|
||||
return value.IsEmpty();
|
||||
}
|
||||
|
||||
void HTMLInputElement::ClearFiles(bool aSetValueChanged) {
|
||||
nsTArray<OwningFileOrDirectory> data;
|
||||
SetFilesOrDirectories(data, aSetValueChanged);
|
||||
@ -2668,6 +2658,12 @@ nsresult HTMLInputElement::SetValueInternal(
|
||||
SetValueChanged(true);
|
||||
}
|
||||
|
||||
if (value.IsEmpty()) {
|
||||
AddStates(ElementState::VALUE_EMPTY);
|
||||
} else {
|
||||
RemoveStates(ElementState::VALUE_EMPTY);
|
||||
}
|
||||
|
||||
if (IsSingleLineTextControl(false)) {
|
||||
// Note that if aOptions includes
|
||||
// ValueSetterOption::BySetUserInputAPI, "input" event is automatically
|
||||
@ -2711,7 +2707,7 @@ nsresult HTMLInputElement::SetValueInternal(
|
||||
}
|
||||
}
|
||||
if (mDoneCreating) {
|
||||
OnValueChanged(ValueChangeKind::Internal);
|
||||
OnValueChanged(ValueChangeKind::Internal, value.IsEmpty(), &value);
|
||||
}
|
||||
// else DoneCreatingElement calls us again once mDoneCreating is true
|
||||
}
|
||||
@ -2727,7 +2723,7 @@ nsresult HTMLInputElement::SetValueInternal(
|
||||
|
||||
// This call might be useless in some situations because if the element is
|
||||
// a single line text control, TextControlState::SetValue will call
|
||||
// nsHTMLInputElement::OnValueChanged which is going to call UpdateState()
|
||||
// HTMLInputElement::OnValueChanged which is going to call UpdateState()
|
||||
// if the element is focused. This bug 665547.
|
||||
if (PlaceholderApplies() && HasAttr(nsGkAtoms::placeholder)) {
|
||||
UpdateState(true);
|
||||
@ -5729,11 +5725,15 @@ nsresult HTMLInputElement::SetDefaultValueAsValue() {
|
||||
return SetValueInternal(resetVal, ValueSetterOption::ByInternalAPI);
|
||||
}
|
||||
|
||||
void HTMLInputElement::SetDirectionFromValue(bool aNotify) {
|
||||
void HTMLInputElement::SetDirectionFromValue(bool aNotify,
|
||||
const nsAString* aKnownValue) {
|
||||
if (IsSingleLineTextControl(true)) {
|
||||
nsAutoString value;
|
||||
GetValue(value, CallerType::System);
|
||||
SetDirectionalityFromValue(this, value, aNotify);
|
||||
if (!aKnownValue) {
|
||||
GetValue(value, CallerType::System);
|
||||
aKnownValue = &value;
|
||||
}
|
||||
SetDirectionalityFromValue(this, *aKnownValue, aNotify);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5888,7 +5888,7 @@ HTMLInputElement::SubmitNamesValues(FormData* aFormData) {
|
||||
static nsTArray<FileContentData> SaveFileContentData(
|
||||
const nsTArray<OwningFileOrDirectory>& aArray) {
|
||||
nsTArray<FileContentData> res(aArray.Length());
|
||||
for (auto& it : aArray) {
|
||||
for (const auto& it : aArray) {
|
||||
if (it.IsFile()) {
|
||||
RefPtr<BlobImpl> impl = it.GetAsFile()->Impl();
|
||||
res.AppendElement(std::move(impl));
|
||||
@ -6089,11 +6089,9 @@ ElementState HTMLInputElement::IntrinsicState() const {
|
||||
}
|
||||
}
|
||||
|
||||
if (mType != FormControlType::InputFile && IsValueEmpty()) {
|
||||
state |= ElementState::VALUE_EMPTY;
|
||||
if (PlaceholderApplies() && HasAttr(nsGkAtoms::placeholder)) {
|
||||
state |= ElementState::PLACEHOLDER_SHOWN;
|
||||
}
|
||||
if (IsValueEmpty() && PlaceholderApplies() &&
|
||||
HasAttr(nsGkAtoms::placeholder)) {
|
||||
state |= ElementState::PLACEHOLDER_SHOWN;
|
||||
}
|
||||
|
||||
return state;
|
||||
@ -6102,7 +6100,7 @@ ElementState HTMLInputElement::IntrinsicState() const {
|
||||
static nsTArray<OwningFileOrDirectory> RestoreFileContentData(
|
||||
nsPIDOMWindowInner* aWindow, const nsTArray<FileContentData>& aData) {
|
||||
nsTArray<OwningFileOrDirectory> res(aData.Length());
|
||||
for (auto& it : aData) {
|
||||
for (const auto& it : aData) {
|
||||
if (it.type() == FileContentData::TBlobImpl) {
|
||||
if (!it.get_BlobImpl()) {
|
||||
// Serialization failed, skip this file.
|
||||
@ -6810,19 +6808,27 @@ void HTMLInputElement::InitializeKeyboardEventListeners() {
|
||||
}
|
||||
}
|
||||
|
||||
void HTMLInputElement::OnValueChanged(ValueChangeKind aKind) {
|
||||
void HTMLInputElement::OnValueChanged(ValueChangeKind aKind,
|
||||
bool aNewValueEmpty,
|
||||
const nsAString* aKnownNewValue) {
|
||||
MOZ_ASSERT_IF(aKnownNewValue, aKnownNewValue->IsEmpty() == aNewValueEmpty);
|
||||
if (aKind != ValueChangeKind::Internal) {
|
||||
mLastValueChangeWasInteractive = aKind == ValueChangeKind::UserInteraction;
|
||||
}
|
||||
|
||||
if (aNewValueEmpty) {
|
||||
AddStates(ElementState::VALUE_EMPTY);
|
||||
} else {
|
||||
RemoveStates(ElementState::VALUE_EMPTY);
|
||||
}
|
||||
|
||||
UpdateAllValidityStates(true);
|
||||
|
||||
if (HasDirAuto()) {
|
||||
SetDirectionFromValue(true);
|
||||
SetDirectionFromValue(true, aKnownNewValue);
|
||||
}
|
||||
|
||||
// :placeholder-shown and value-empty pseudo-class may change when the value
|
||||
// changes.
|
||||
// :placeholder-shown pseudo-class may change when the value changes.
|
||||
UpdateState(true);
|
||||
}
|
||||
|
||||
|
@ -242,7 +242,8 @@ class HTMLInputElement final : public TextControlElement,
|
||||
void EnablePreview() override;
|
||||
bool IsPreviewEnabled() override;
|
||||
void InitializeKeyboardEventListeners() override;
|
||||
void OnValueChanged(ValueChangeKind) override;
|
||||
void OnValueChanged(ValueChangeKind, bool aNewValueEmpty,
|
||||
const nsAString* aKnownNewValue) override;
|
||||
void GetValueFromSetRangeText(nsAString& aValue) override;
|
||||
MOZ_CAN_RUN_SCRIPT nsresult
|
||||
SetValueFromSetRangeText(const nsAString& aValue) override;
|
||||
@ -851,7 +852,9 @@ class HTMLInputElement final : public TextControlElement,
|
||||
*
|
||||
* @return whether the current value is the empty string.
|
||||
*/
|
||||
bool IsValueEmpty() const;
|
||||
bool IsValueEmpty() const {
|
||||
return State().HasState(ElementState::VALUE_EMPTY);
|
||||
}
|
||||
|
||||
// Parse a simple (hex) color.
|
||||
static mozilla::Maybe<nscolor> ParseSimpleColor(const nsAString& aColor);
|
||||
@ -1092,7 +1095,12 @@ class HTMLInputElement final : public TextControlElement,
|
||||
MOZ_CAN_RUN_SCRIPT
|
||||
nsresult SetDefaultValueAsValue();
|
||||
|
||||
void SetDirectionFromValue(bool aNotify);
|
||||
/**
|
||||
* Sets the direction from the input value. if aKnownValue is provided, it
|
||||
* saves a GetValue call.
|
||||
*/
|
||||
void SetDirectionFromValue(bool aNotify,
|
||||
const nsAString* aKnownValue = nullptr);
|
||||
|
||||
/**
|
||||
* Return if an element should have a specific validity UI
|
||||
|
@ -69,7 +69,7 @@ HTMLTextAreaElement::HTMLTextAreaElement(
|
||||
// until someone calls UpdateEditableState on us, apparently! Also
|
||||
// by default we don't have to show validity UI and so forth.
|
||||
AddStatesSilently(ElementState::ENABLED | ElementState::OPTIONAL_ |
|
||||
ElementState::VALID);
|
||||
ElementState::VALID | ElementState::VALUE_EMPTY);
|
||||
}
|
||||
|
||||
HTMLTextAreaElement::~HTMLTextAreaElement() {
|
||||
@ -784,7 +784,7 @@ ElementState HTMLTextAreaElement::IntrinsicState() const {
|
||||
}
|
||||
}
|
||||
|
||||
if (HasAttr(nsGkAtoms::placeholder) && IsValueEmpty()) {
|
||||
if (IsValueEmpty() && HasAttr(nsGkAtoms::placeholder)) {
|
||||
state |= ElementState::PLACEHOLDER_SHOWN;
|
||||
}
|
||||
|
||||
@ -948,13 +948,6 @@ bool HTMLTextAreaElement::IsMutable() const {
|
||||
return (!HasAttr(kNameSpaceID_None, nsGkAtoms::readonly) && !IsDisabled());
|
||||
}
|
||||
|
||||
bool HTMLTextAreaElement::IsValueEmpty() const {
|
||||
nsAutoString value;
|
||||
GetValueInternal(value, true);
|
||||
|
||||
return value.IsEmpty();
|
||||
}
|
||||
|
||||
void HTMLTextAreaElement::SetCustomValidity(const nsAString& aError) {
|
||||
ConstraintValidation::SetCustomValidity(aError);
|
||||
|
||||
@ -1001,7 +994,6 @@ bool HTMLTextAreaElement::IsValueMissing() const {
|
||||
if (!Required() || !IsMutable()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return IsValueEmpty();
|
||||
}
|
||||
|
||||
@ -1121,18 +1113,28 @@ void HTMLTextAreaElement::InitializeKeyboardEventListeners() {
|
||||
mState->InitializeKeyboardEventListeners();
|
||||
}
|
||||
|
||||
void HTMLTextAreaElement::OnValueChanged(ValueChangeKind aKind) {
|
||||
void HTMLTextAreaElement::OnValueChanged(ValueChangeKind aKind,
|
||||
bool aNewValueEmpty,
|
||||
const nsAString*) {
|
||||
if (aKind != ValueChangeKind::Internal) {
|
||||
mLastValueChangeWasInteractive = aKind == ValueChangeKind::UserInteraction;
|
||||
}
|
||||
|
||||
const bool emptyBefore = IsValueEmpty();
|
||||
if (aNewValueEmpty) {
|
||||
AddStates(ElementState::VALUE_EMPTY);
|
||||
} else {
|
||||
RemoveStates(ElementState::VALUE_EMPTY);
|
||||
}
|
||||
|
||||
// Update the validity state
|
||||
bool validBefore = IsValid();
|
||||
const bool validBefore = IsValid();
|
||||
UpdateTooLongValidityState();
|
||||
UpdateTooShortValidityState();
|
||||
UpdateValueMissingValidityState();
|
||||
|
||||
if (validBefore != IsValid() || HasAttr(nsGkAtoms::placeholder)) {
|
||||
if (validBefore != IsValid() ||
|
||||
(emptyBefore != IsValueEmpty() && HasAttr(nsGkAtoms::placeholder))) {
|
||||
UpdateState(true);
|
||||
}
|
||||
}
|
||||
|
@ -97,7 +97,8 @@ class HTMLTextAreaElement final : public TextControlElement,
|
||||
void EnablePreview() override;
|
||||
bool IsPreviewEnabled() override;
|
||||
void InitializeKeyboardEventListeners() override;
|
||||
void OnValueChanged(ValueChangeKind) override;
|
||||
void OnValueChanged(ValueChangeKind, bool aNewValueEmpty,
|
||||
const nsAString* aKnownNewValue) override;
|
||||
void GetValueFromSetRangeText(nsAString& aValue) override;
|
||||
MOZ_CAN_RUN_SCRIPT nsresult
|
||||
SetValueFromSetRangeText(const nsAString& aValue) override;
|
||||
@ -377,7 +378,9 @@ class HTMLTextAreaElement final : public TextControlElement,
|
||||
*
|
||||
* @return whether the current value is the empty string.
|
||||
*/
|
||||
bool IsValueEmpty() const;
|
||||
bool IsValueEmpty() const {
|
||||
return State().HasState(ElementState::VALUE_EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper to get the current selection range. Will throw on the ErrorResult
|
||||
|
@ -182,8 +182,16 @@ class TextControlElement : public nsGenericHTMLFormControlElementWithState {
|
||||
|
||||
/**
|
||||
* Callback called whenever the value is changed.
|
||||
*
|
||||
* aKnownNewValue can be used to avoid value lookups if present (might be
|
||||
* null, if the caller doesn't know the specific value that got set).
|
||||
*/
|
||||
virtual void OnValueChanged(ValueChangeKind) = 0;
|
||||
virtual void OnValueChanged(ValueChangeKind, bool aNewValueEmpty,
|
||||
const nsAString* aKnownNewValue) = 0;
|
||||
|
||||
void OnValueChanged(ValueChangeKind aKind, const nsAString& aNewValue) {
|
||||
return OnValueChanged(aKind, aNewValue.IsEmpty(), &aNewValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helpers for value manipulation from SetRangeText.
|
||||
|
@ -1032,14 +1032,14 @@ nsresult TextInputListener::OnEditActionHandled(TextEditor& aTextEditor) {
|
||||
}
|
||||
|
||||
if (weakFrame.IsAlive()) {
|
||||
HandleValueChanged();
|
||||
HandleValueChanged(aTextEditor);
|
||||
}
|
||||
}
|
||||
|
||||
return mTextControlState ? mTextControlState->OnEditActionHandled() : NS_OK;
|
||||
}
|
||||
|
||||
void TextInputListener::HandleValueChanged() {
|
||||
void TextInputListener::HandleValueChanged(TextEditor& aTextEditor) {
|
||||
// Make sure we know we were changed (do NOT set this to false if there are
|
||||
// no undo items; JS could change the value and we'd still need to save it)
|
||||
if (mSetValueChanged) {
|
||||
@ -1050,7 +1050,8 @@ void TextInputListener::HandleValueChanged() {
|
||||
// NOTE(emilio): execCommand might get here even though it might not be a
|
||||
// "proper" user-interactive change. Might be worth reconsidering which
|
||||
// ValueChangeKind are we passing down.
|
||||
mTxtCtrlElement->OnValueChanged(ValueChangeKind::UserInteraction);
|
||||
mTxtCtrlElement->OnValueChanged(ValueChangeKind::UserInteraction,
|
||||
aTextEditor.IsEmpty(), nullptr);
|
||||
if (mTextControlState) {
|
||||
mTextControlState->ClearLastInteractiveValue();
|
||||
}
|
||||
@ -2711,7 +2712,8 @@ bool TextControlState::SetValue(const nsAString& aValue,
|
||||
// If we were handling SetValue() before, don't update the DOM state twice,
|
||||
// just let the outer call do so.
|
||||
if (!wasHandlingSetValue) {
|
||||
handlingSetValue.GetTextControlElement()->OnValueChanged(changeKind);
|
||||
handlingSetValue.GetTextControlElement()->OnValueChanged(
|
||||
changeKind, handlingSetValue.GetSettingValue());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -2973,7 +2975,8 @@ bool TextControlState::SetValueWithoutTextEditor(
|
||||
// Update validity state before dispatching "input" event for its
|
||||
// listeners like `EditorBase::NotifyEditorObservers()`.
|
||||
aHandlingSetValue.GetTextControlElement()->OnValueChanged(
|
||||
ValueChangeKind::UserInteraction);
|
||||
ValueChangeKind::UserInteraction,
|
||||
aHandlingSetValue.GetSettingValue());
|
||||
|
||||
ClearLastInteractiveValue();
|
||||
|
||||
@ -3000,20 +3003,6 @@ bool TextControlState::SetValueWithoutTextEditor(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextControlState::HasNonEmptyValue() const {
|
||||
// If the frame for editor is alive, we can compute it with mTextEditor.
|
||||
// Otherwise, we need to check cached value via GetValue().
|
||||
if (mTextEditor && mBoundFrame && mEditorInitialized &&
|
||||
!(mHandlingState &&
|
||||
mHandlingState->IsHandling(TextControlAction::CommitComposition))) {
|
||||
return !mTextEditor->IsEmpty();
|
||||
}
|
||||
|
||||
nsAutoString value;
|
||||
GetValue(value, true);
|
||||
return !value.IsEmpty();
|
||||
}
|
||||
|
||||
void TextControlState::InitializeKeyboardEventListeners() {
|
||||
// register key listeners
|
||||
EventListenerManager* manager =
|
||||
|
@ -294,7 +294,6 @@ class TextControlState final : public SupportsWeakPtr {
|
||||
* nsContentUtils::PlatformToDOMLineBreaks().
|
||||
*/
|
||||
bool ValueEquals(const nsAString& aValue) const;
|
||||
bool HasNonEmptyValue() const;
|
||||
// The following methods are for textarea element to use whether default
|
||||
// value or not.
|
||||
// XXX We might have to add assertion when it is into editable,
|
||||
|
@ -40,13 +40,12 @@ class TextInputListener final : public nsIDOMEventListener,
|
||||
* aFrame is an optional pointer to our frame, if not passed the method will
|
||||
* use mFrame to compute it lazily.
|
||||
*/
|
||||
void HandleValueChanged();
|
||||
void HandleValueChanged(TextEditor&);
|
||||
|
||||
/**
|
||||
* OnEditActionHandled() is called when the editor handles each edit action.
|
||||
*/
|
||||
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult
|
||||
OnEditActionHandled(TextEditor& aTextEditor);
|
||||
[[nodiscard]] MOZ_CAN_RUN_SCRIPT nsresult OnEditActionHandled(TextEditor&);
|
||||
|
||||
/**
|
||||
* OnSelectionChange() is called when selection is changed in the editor.
|
||||
|
Loading…
x
Reference in New Issue
Block a user