ported from UXP: Issue #2790 - Part 4: Working non persistent autofill highlight (bf8cfcc9)

This commit is contained in:
2025-07-29 15:15:26 +08:00
parent eff68ee912
commit 2fae301fd1
12 changed files with 207 additions and 68 deletions
+38 -11
View File
@@ -2780,16 +2780,11 @@ HTMLInputElement::SetUserInput(const nsAString& aValue)
void
HTMLInputElement::SetAutofilled(bool aAutofilled)
{
printf("🔍 AUTOFILL C++: SetAutofilled called with aAutofilled=%s\n", aAutofilled ? "true" : "false");
if (aAutofilled) {
printf("🔍 AUTOFILL C++: Adding NS_EVENT_STATE_AUTOFILL state\n");
AddStates(NS_EVENT_STATE_AUTOFILL);
printf("🔍 AUTOFILL C++: State added successfully\n");
} else {
printf("🔍 AUTOFILL C++: Removing NS_EVENT_STATE_AUTOFILL state\n");
RemoveStates(NS_EVENT_STATE_AUTOFILL);
printf("🔍 AUTOFILL C++: State removed successfully\n");
}
}
@@ -3525,7 +3520,18 @@ HTMLInputElement::Blur(ErrorResult& aError)
}
nsGenericHTMLElement::Blur(aError);
}
if (State().HasState(NS_EVENT_STATE_AUTOFILL)) {
// Force a complete restyle to ensure autofill pseudo-classes are processed
if (nsIDocument* doc = GetComposedDoc()) {
if (nsIPresShell* shell = doc->GetShell()) {
if (nsIFrame* frame = GetPrimaryFrame()) {
shell->FrameNeedsReflow(frame, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
}
}
}
}
}
void
HTMLInputElement::Focus(ErrorResult& aError)
@@ -8464,13 +8470,16 @@ HTMLInputElement::InitializeKeyboardEventListeners()
NS_IMETHODIMP_(void)
HTMLInputElement::OnValueChanged(bool aNotify, bool aWasInteractiveUserChange)
{
nsAutoString value;
GetValueInternal(value, CallerType::System);
mLastValueChangeWasInteractive = aWasInteractiveUserChange;
// Clear autofilled state if this was an interactive user change
// Only remove autofilled state if the value actually changed from autofilled value
if (aWasInteractiveUserChange && State().HasState(NS_EVENT_STATE_AUTOFILL)) {
printf("🔍 AUTOFILL C++: User changed autofilled input, clearing state\n");
RemoveStates(NS_EVENT_STATE_AUTOFILL);
printf("🔍 AUTOFILL C++: Autofill state cleared from input\n");
if (mAutofilledValue != value) {
RemoveStates(NS_EVENT_STATE_AUTOFILL);
mAutofilledValue.Truncate();
}
}
UpdateAllValidityStates(aNotify);
@@ -8503,6 +8512,24 @@ HTMLInputElement::HasCachedSelection()
return isCached;
}
NS_IMETHODIMP
HTMLInputElement::BeginProgrammaticValueSet() {
nsTextEditorState* state = GetEditorState();
if (state) {
state->SettingValue(true);
}
return NS_OK;
}
NS_IMETHODIMP
HTMLInputElement::EndProgrammaticValueSet() {
nsTextEditorState* state = GetEditorState();
if (state) {
state->SettingValue(false);
}
return NS_OK;
}
void
HTMLInputElement::FieldSetDisabledChanged(bool aNotify)
{
+4 -1
View File
@@ -162,6 +162,8 @@ public:
}
NS_IMETHOD SetUserInput(const nsAString& aInput) override;
NS_IMETHOD BeginProgrammaticValueSet() override;
NS_IMETHOD EndProgrammaticValueSet() override;
// Overriden nsIFormControl methods
NS_IMETHOD_(uint32_t) GetType() const override { return mType; }
@@ -1133,7 +1135,7 @@ protected:
bool MinOrMaxLengthApplies() const { return IsSingleLineTextControl(false, mType); }
void FreeData();
nsTextEditorState *GetEditorState() const;
nsTextEditorState* GetEditorState() const;
/**
* Manages the internal data storage across type changes.
@@ -1659,6 +1661,7 @@ protected:
bool mNumberControlSpinnerSpinsUp : 1;
bool mPickerRunning : 1;
bool mSelectionCached : 1;
nsString mAutofilledValue;
private:
static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
+81 -30
View File
@@ -367,16 +367,46 @@ HTMLTextAreaElement::SetUserInput(const nsAString& aValue)
void
HTMLTextAreaElement::SetAutofilled(bool aAutofilled)
{
printf("🔍 AUTOFILL C++: HTMLTextAreaElement::SetAutofilled called with aAutofilled=%s\n", aAutofilled ? "true" : "false");
if (aAutofilled) {
printf("🔍 AUTOFILL C++: Adding NS_EVENT_STATE_AUTOFILL state to textarea\n");
AddStates(NS_EVENT_STATE_AUTOFILL);
printf("🔍 AUTOFILL C++: State added successfully to textarea\n");
GetValueInternal(mAutofilledValue, true); // Store the autofilled value
} else {
printf("🔍 AUTOFILL C++: Removing NS_EVENT_STATE_AUTOFILL state from textarea\n");
RemoveStates(NS_EVENT_STATE_AUTOFILL);
printf("🔍 AUTOFILL C++: State removed successfully from textarea\n");
mAutofilledValue.Truncate();
}
}
NS_IMETHODIMP_(void)
HTMLTextAreaElement::OnValueChanged(bool aNotify, bool aWasInteractiveUserChange)
{
nsAutoString value;
GetValueInternal(value, true);
// printf("[TextArea] OnValueChanged: aWasInteractiveUserChange=%d, value='%s', autofilled='%s', autofill state=%d\n",
// aWasInteractiveUserChange,
// NS_ConvertUTF16toUTF8(value).get(),
// NS_ConvertUTF16toUTF8(mAutofilledValue).get(),
// State().HasState(NS_EVENT_STATE_AUTOFILL));
// Only remove autofilled state if the value actually changed from autofilled value
if (State().HasState(NS_EVENT_STATE_AUTOFILL) || !mAutofilledValue.IsEmpty()) {
if (aWasInteractiveUserChange && mAutofilledValue != value) {
RemoveStates(NS_EVENT_STATE_AUTOFILL);
mAutofilledValue.Truncate();
} else if (aWasInteractiveUserChange && mAutofilledValue == value) {
// Defensive: re-add the autofill state if it was removed by something else
AddStates(NS_EVENT_STATE_AUTOFILL);
}
}
// Update the validity state
bool validBefore = IsValid();
UpdateTooLongValidityState();
UpdateTooShortValidityState();
UpdateValueMissingValidityState();
if (validBefore != IsValid() ||
HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder)) {
UpdateState(aNotify);
}
}
@@ -570,6 +600,19 @@ HTMLTextAreaElement::FireChangeEventIfNeeded()
false);
}
void
HTMLTextAreaElement::EnsureAutofillState()
{
nsAutoString value;
GetValueInternal(value, true);
if (!mAutofilledValue.IsEmpty() && mAutofilledValue == value) {
if (!State().HasState(NS_EVENT_STATE_AUTOFILL)) {
AddStates(NS_EVENT_STATE_AUTOFILL);
UpdateState(true); // Force style system to re-evaluate
}
}
}
nsresult
HTMLTextAreaElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
{
@@ -594,6 +637,9 @@ HTMLTextAreaElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
}
UpdateState(true);
// Defensive: re-apply autofill state if value is still autofilled value
EnsureAutofillState();
}
return NS_OK;
@@ -1200,6 +1246,11 @@ HTMLTextAreaElement::IntrinsicState() const
{
EventStates state = nsGenericHTMLFormElementWithState::IntrinsicState();
// PATCH: Persist autofill state if autofilled
if (!mAutofilledValue.IsEmpty()) {
state |= NS_EVENT_STATE_AUTOFILL;
}
if (HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
state |= NS_EVENT_STATE_REQUIRED;
} else {
@@ -1242,6 +1293,8 @@ HTMLTextAreaElement::IntrinsicState() const
state |= NS_EVENT_STATE_PLACEHOLDERSHOWN;
}
state |= NS_EVENT_STATE_AUTOFILL;
return state;
}
@@ -1642,30 +1695,6 @@ HTMLTextAreaElement::InitializeKeyboardEventListeners()
mState.InitializeKeyboardEventListeners();
}
NS_IMETHODIMP_(void)
HTMLTextAreaElement::OnValueChanged(bool aNotify, bool aWasInteractiveUserChange)
{
mLastValueChangeWasInteractive = aWasInteractiveUserChange;
// Clear autofilled state if this was an interactive user change
if (aWasInteractiveUserChange && State().HasState(NS_EVENT_STATE_AUTOFILL)) {
printf("🔍 AUTOFILL C++: User changed autofilled textarea, clearing state\n");
RemoveStates(NS_EVENT_STATE_AUTOFILL);
printf("🔍 AUTOFILL C++: Autofill state cleared from textarea\n");
}
// Update the validity state
bool validBefore = IsValid();
UpdateTooLongValidityState();
UpdateTooShortValidityState();
UpdateValueMissingValidityState();
if (validBefore != IsValid() ||
HasAttr(kNameSpaceID_None, nsGkAtoms::placeholder)) {
UpdateState(aNotify);
}
}
NS_IMETHODIMP_(bool)
HTMLTextAreaElement::HasCachedSelection()
{
@@ -1681,11 +1710,33 @@ HTMLTextAreaElement::FieldSetDisabledChanged(bool aNotify)
nsGenericHTMLFormElementWithState::FieldSetDisabledChanged(aNotify);
}
NS_IMETHODIMP
HTMLTextAreaElement::BeginProgrammaticValueSet() {
nsTextEditorState* state = GetEditorState();
if (state) {
state->SettingValue(true);
}
return NS_OK;
}
NS_IMETHODIMP
HTMLTextAreaElement::EndProgrammaticValueSet() {
nsTextEditorState* state = GetEditorState();
if (state) {
state->SettingValue(false);
}
return NS_OK;
}
JSObject*
HTMLTextAreaElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return HTMLTextAreaElementBinding::Wrap(aCx, this, aGivenProto);
}
nsTextEditorState* HTMLTextAreaElement::GetEditorState() const {
return const_cast<nsTextEditorState*>(&mState);
}
} // namespace dom
} // namespace mozilla
+5
View File
@@ -70,6 +70,8 @@ public:
return nsGenericHTMLElement::GetEditor(aEditor);
}
NS_IMETHOD SetUserInput(const nsAString& aInput) override;
NS_IMETHOD BeginProgrammaticValueSet() override;
NS_IMETHOD EndProgrammaticValueSet() override;
/**
* Sets or clears the autofilled state of this textarea element.
@@ -303,6 +305,7 @@ public:
{
return mState.GetEditor();
}
nsTextEditorState* GetEditorState() const;
protected:
virtual ~HTMLTextAreaElement() {}
@@ -334,6 +337,7 @@ protected:
void FireChangeEventIfNeeded();
nsString mFocusedValue;
nsString mAutofilledValue;
/** The state of the text editor (selection controller and the editor) **/
nsTextEditorState mState;
@@ -407,6 +411,7 @@ protected:
private:
static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
nsRuleData* aData);
void EnsureAutofillState();
};
} // namespace dom
+2
View File
@@ -164,6 +164,7 @@ public:
// or reconsider fixing bug 597525 to remove these.
void EmptyValue() { if (mValue) mValue->Truncate(); }
bool IsEmpty() const { return mValue ? mValue->IsEmpty() : true; }
void SettingValue(bool aValue) { mSettingValue = aValue; }
nsresult CreatePlaceholderNode();
@@ -349,6 +350,7 @@ private:
mutable bool mSelectionRestoreEagerInit; // Whether we're eager initing because of selection restore
bool mPlaceholderVisibility;
bool mIsCommittingComposition;
bool mSettingValue;
};
inline void
@@ -25,4 +25,10 @@ interface nsIDOMNSEditableElement : nsISupports
// 'change' event for example will be dispatched when focusing out the
// element.
[noscript] void setUserInput(in DOMString input);
/**
* Call this before and after programmatically setting the value to prevent
* OnValueChanged from treating it as a user edit.
*/
void beginProgrammaticValueSet();
void endProgrammaticValueSet();
};