Issue #2790 - Part 4: Working non persistent autofill highlight

This commit is contained in:
MeladJM 2025-07-25 11:12:29 +08:00 • committed by roytam1
commit bf8cfcc980
12 changed files with 207 additions and 68 deletions

View file

@ -2835,16 +2835,11 @@ HTMLInputElement::SetUserInput(const nsAString& aValue)
void void
HTMLInputElement::SetAutofilled(bool aAutofilled) HTMLInputElement::SetAutofilled(bool aAutofilled)
{ {
printf("🔍 AUTOFILL C++: SetAutofilled called with aAutofilled=%s\n", aAutofilled ? "true" : "false");
if (aAutofilled) { if (aAutofilled) {
printf("🔍 AUTOFILL C++: Adding NS_EVENT_STATE_AUTOFILL state\n");
AddStates(NS_EVENT_STATE_AUTOFILL); AddStates(NS_EVENT_STATE_AUTOFILL);
printf("🔍 AUTOFILL C++: State added successfully\n");
} else { } else {
printf("🔍 AUTOFILL C++: Removing NS_EVENT_STATE_AUTOFILL state\n");
RemoveStates(NS_EVENT_STATE_AUTOFILL); RemoveStates(NS_EVENT_STATE_AUTOFILL);
printf("🔍 AUTOFILL C++: State removed successfully\n");
} }
} }
@ -3578,6 +3573,17 @@ HTMLInputElement::Blur(ErrorResult& aError)
} }
nsGenericHTMLElement::Blur(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 void
@ -8521,13 +8527,16 @@ HTMLInputElement::InitializeKeyboardEventListeners()
NS_IMETHODIMP_(void) NS_IMETHODIMP_(void)
HTMLInputElement::OnValueChanged(bool aNotify, bool aWasInteractiveUserChange) HTMLInputElement::OnValueChanged(bool aNotify, bool aWasInteractiveUserChange)
{ {
nsAutoString value;
GetValueInternal(value);
mLastValueChangeWasInteractive = aWasInteractiveUserChange; 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)) { if (aWasInteractiveUserChange && State().HasState(NS_EVENT_STATE_AUTOFILL)) {
printf("🔍 AUTOFILL C++: User changed autofilled input, clearing state\n"); if (mAutofilledValue != value) {
RemoveStates(NS_EVENT_STATE_AUTOFILL); RemoveStates(NS_EVENT_STATE_AUTOFILL);
printf("🔍 AUTOFILL C++: Autofill state cleared from input\n"); mAutofilledValue.Truncate();
}
} }
UpdateAllValidityStates(aNotify); UpdateAllValidityStates(aNotify);
@ -8560,6 +8569,24 @@ HTMLInputElement::HasCachedSelection()
return isCached; 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 void
HTMLInputElement::FieldSetDisabledChanged(bool aNotify) HTMLInputElement::FieldSetDisabledChanged(bool aNotify)
{ {

View file

@ -160,6 +160,8 @@ public:
} }
NS_IMETHOD SetUserInput(const nsAString& aInput) override; NS_IMETHOD SetUserInput(const nsAString& aInput) override;
NS_IMETHOD BeginProgrammaticValueSet() override;
NS_IMETHOD EndProgrammaticValueSet() override;
// Overriden nsIFormControl methods // Overriden nsIFormControl methods
NS_IMETHOD_(uint32_t) GetType() const override { return mType; } NS_IMETHOD_(uint32_t) GetType() const override { return mType; }
@ -1639,6 +1641,7 @@ protected:
bool mNumberControlSpinnerSpinsUp : 1; bool mNumberControlSpinnerSpinsUp : 1;
bool mPickerRunning : 1; bool mPickerRunning : 1;
bool mSelectionCached : 1; bool mSelectionCached : 1;
nsString mAutofilledValue;
private: private:
static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes, static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,

View file

@ -369,16 +369,46 @@ HTMLTextAreaElement::SetUserInput(const nsAString& aValue)
void void
HTMLTextAreaElement::SetAutofilled(bool aAutofilled) HTMLTextAreaElement::SetAutofilled(bool aAutofilled)
{ {
printf("🔍 AUTOFILL C++: HTMLTextAreaElement::SetAutofilled called with aAutofilled=%s\n", aAutofilled ? "true" : "false");
if (aAutofilled) { if (aAutofilled) {
printf("🔍 AUTOFILL C++: Adding NS_EVENT_STATE_AUTOFILL state to textarea\n");
AddStates(NS_EVENT_STATE_AUTOFILL); AddStates(NS_EVENT_STATE_AUTOFILL);
printf("🔍 AUTOFILL C++: State added successfully to textarea\n"); GetValueInternal(mAutofilledValue, true); // Store the autofilled value
} else { } else {
printf("🔍 AUTOFILL C++: Removing NS_EVENT_STATE_AUTOFILL state from textarea\n");
RemoveStates(NS_EVENT_STATE_AUTOFILL); 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);
} }
} }
@ -572,6 +602,19 @@ HTMLTextAreaElement::FireChangeEventIfNeeded()
false); 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 nsresult
HTMLTextAreaElement::PostHandleEvent(EventChainPostVisitor& aVisitor) HTMLTextAreaElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
{ {
@ -596,6 +639,9 @@ HTMLTextAreaElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
} }
UpdateState(true); UpdateState(true);
// Defensive: re-apply autofill state if value is still autofilled value
EnsureAutofillState();
} }
return NS_OK; return NS_OK;
@ -1202,6 +1248,11 @@ HTMLTextAreaElement::IntrinsicState() const
{ {
EventStates state = nsGenericHTMLFormElementWithState::IntrinsicState(); EventStates state = nsGenericHTMLFormElementWithState::IntrinsicState();
// PATCH: Persist autofill state if autofilled
if (!mAutofilledValue.IsEmpty()) {
state |= NS_EVENT_STATE_AUTOFILL;
}
if (HasAttr(kNameSpaceID_None, nsGkAtoms::required)) { if (HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
state |= NS_EVENT_STATE_REQUIRED; state |= NS_EVENT_STATE_REQUIRED;
} else { } else {
@ -1244,6 +1295,8 @@ HTMLTextAreaElement::IntrinsicState() const
state |= NS_EVENT_STATE_PLACEHOLDERSHOWN; state |= NS_EVENT_STATE_PLACEHOLDERSHOWN;
} }
state |= NS_EVENT_STATE_AUTOFILL;
return state; return state;
} }
@ -1644,30 +1697,6 @@ HTMLTextAreaElement::InitializeKeyboardEventListeners()
mState.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) NS_IMETHODIMP_(bool)
HTMLTextAreaElement::HasCachedSelection() HTMLTextAreaElement::HasCachedSelection()
{ {
@ -1683,11 +1712,33 @@ HTMLTextAreaElement::FieldSetDisabledChanged(bool aNotify)
nsGenericHTMLFormElementWithState::FieldSetDisabledChanged(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* JSObject*
HTMLTextAreaElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) HTMLTextAreaElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{ {
return HTMLTextAreaElementBinding::Wrap(aCx, this, aGivenProto); return HTMLTextAreaElementBinding::Wrap(aCx, this, aGivenProto);
} }
nsTextEditorState* HTMLTextAreaElement::GetEditorState() const {
return const_cast<nsTextEditorState*>(&mState);
}
} // namespace dom } // namespace dom
} // namespace mozilla } // namespace mozilla

View file

@ -69,6 +69,8 @@ public:
return nsGenericHTMLElement::GetEditor(aEditor); return nsGenericHTMLElement::GetEditor(aEditor);
} }
NS_IMETHOD SetUserInput(const nsAString& aInput) override; 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. * Sets or clears the autofilled state of this textarea element.
@ -302,6 +304,7 @@ public:
{ {
return mState.GetEditor(); return mState.GetEditor();
} }
nsTextEditorState* GetEditorState() const;
protected: protected:
virtual ~HTMLTextAreaElement() {} virtual ~HTMLTextAreaElement() {}
@ -333,6 +336,7 @@ protected:
void FireChangeEventIfNeeded(); void FireChangeEventIfNeeded();
nsString mFocusedValue; nsString mFocusedValue;
nsString mAutofilledValue;
/** The state of the text editor (selection controller and the editor) **/ /** The state of the text editor (selection controller and the editor) **/
nsTextEditorState mState; nsTextEditorState mState;
@ -406,6 +410,7 @@ protected:
private: private:
static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes, static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
nsRuleData* aData); nsRuleData* aData);
void EnsureAutofillState();
}; };
} // namespace dom } // namespace dom

View file

@ -163,6 +163,7 @@ public:
// or reconsider fixing bug 597525 to remove these. // or reconsider fixing bug 597525 to remove these.
void EmptyValue() { if (mValue) mValue->Truncate(); } void EmptyValue() { if (mValue) mValue->Truncate(); }
bool IsEmpty() const { return mValue ? mValue->IsEmpty() : true; } bool IsEmpty() const { return mValue ? mValue->IsEmpty() : true; }
void SettingValue(bool aValue) { mSettingValue = aValue; }
nsresult CreatePlaceholderNode(); nsresult CreatePlaceholderNode();
@ -348,6 +349,7 @@ private:
mutable bool mSelectionRestoreEagerInit; // Whether we're eager initing because of selection restore mutable bool mSelectionRestoreEagerInit; // Whether we're eager initing because of selection restore
bool mPlaceholderVisibility; bool mPlaceholderVisibility;
bool mIsCommittingComposition; bool mIsCommittingComposition;
bool mSettingValue;
}; };
inline void inline void

View file

@ -25,4 +25,10 @@ interface nsIDOMNSEditableElement : nsISupports
// 'change' event for example will be dispatched when focusing out the // 'change' event for example will be dispatched when focusing out the
// element. // element.
[noscript] void setUserInput(in DOMString input); [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();
}; };

View file

@ -1270,6 +1270,14 @@ ComputeSelectorStateDependence(nsCSSSelector& aSelector)
continue; continue;
} }
// --- BEGIN PATCH: Explicit autofill state dependence ---
if (pseudoClass->mType == CSSPseudoClassType::autofill ||
pseudoClass->mType == CSSPseudoClassType::mozAutofillHighlight) {
states |= NS_EVENT_STATE_AUTOFILL;
continue;
}
// --- END PATCH ---
auto idx = static_cast<CSSPseudoClassTypeBase>(pseudoClass->mType); auto idx = static_cast<CSSPseudoClassTypeBase>(pseudoClass->mType);
states |= nsCSSPseudoClasses::sPseudoClassStateDependences[idx]; states |= nsCSSPseudoClasses::sPseudoClassStateDependences[idx];
} }

View file

@ -576,6 +576,16 @@ nsCSSRuleUtils::StateSelectorMatches(Element* aElement,
for (nsPseudoClassList* pseudoClass = aSelector->mPseudoClassList; for (nsPseudoClassList* pseudoClass = aSelector->mPseudoClassList;
pseudoClass; pseudoClass;
pseudoClass = pseudoClass->mNext) { pseudoClass = pseudoClass->mNext) {
// --- Autofill explicit matching ---
if (pseudoClass->mType == CSSPseudoClassType::autofill ||
pseudoClass->mType == CSSPseudoClassType::mozAutofillHighlight) {
// Match if the element has the autofill state, regardless of focus
if (!aElement->State().HasState(NS_EVENT_STATE_AUTOFILL)) {
return false;
}
continue; // This pseudo-class matches
}
// --- End autofill explicit matching ---
auto idx = static_cast<CSSPseudoClassTypeBase>(pseudoClass->mType); auto idx = static_cast<CSSPseudoClassTypeBase>(pseudoClass->mType);
EventStates statesToCheck = nsCSSPseudoClasses::sPseudoClassStates[idx]; EventStates statesToCheck = nsCSSPseudoClasses::sPseudoClassStates[idx];
if (!statesToCheck.IsEmpty() && !StateSelectorMatches(aElement, if (!statesToCheck.IsEmpty() && !StateSelectorMatches(aElement,

View file

@ -1152,6 +1152,11 @@ input[type="time"] {
/* Autofill highlight for internal-only pseudo-class */ /* Autofill highlight for internal-only pseudo-class */
input:-moz-autofill-highlight, input:-moz-autofill-highlight,
select:-moz-autofill-highlight,
textarea:-moz-autofill-highlight { textarea:-moz-autofill-highlight {
background-color: #ffff99 !important; background-color: #ffff99 !important;
} }
.custom-autofill-highlight {
background: yellow !important;
}

View file

@ -9,9 +9,7 @@
* See the nsIFormAutofillContentService documentation for details. * See the nsIFormAutofillContentService documentation for details.
*/ */
"use strict"; "use strict"
console.log('🔍 AUTOFILL: FormAutofillContentService.js loaded');
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
@ -34,15 +32,11 @@ function FormHandler(aForm, aWindow) {
// Add a reset event listener to clear autofill state // Add a reset event listener to clear autofill state
this.form.addEventListener("reset", () => { this.form.addEventListener("reset", () => {
console.log('Form reset detected, clearing autofill state');
for (let element of this.form.elements) { for (let element of this.form.elements) {
if (typeof element.setAutofilled === "function") { if (typeof element.setAutofilled === "function") {
element.setAutofilled(false); element.setAutofilled(false);
console.log('setAutofilled(false) called on', element);
} }
} }
// Optionally, clear fieldDetails if you want to force re-collection
// this.fieldDetails = [];
}); });
} }
@ -145,9 +139,7 @@ FormHandler.prototype = {
return "cancel"; return "cancel";
} }
console.log('🔍 AUTOFILL: About to call autofillFormFields with result:', result);
this.autofillFormFields(result); this.autofillFormFields(result);
console.log('🔍 AUTOFILL: autofillFormFields completed');
return "success"; return "success";
}), }),
@ -243,10 +235,8 @@ FormHandler.prototype = {
* } * }
*/ */
autofillFormFields: function (aAutofillResult) { autofillFormFields: function (aAutofillResult) {
console.log('🔍 AUTOFILL: autofillFormFields called with', aAutofillResult);
for (let field of aAutofillResult.fields) { for (let field of aAutofillResult.fields) {
console.log('🔍 AUTOFILL: Processing field', field);
// Get the field details, if it was processed by the user interface. // Get the field details, if it was processed by the user interface.
let fieldDetail = this.fieldDetails let fieldDetail = this.fieldDetails
@ -255,26 +245,36 @@ FormHandler.prototype = {
f.contactType == field.contactType && f.contactType == field.contactType &&
f.fieldName == field.fieldName); f.fieldName == field.fieldName);
console.log('🔍 AUTOFILL: Found fieldDetail?', !!fieldDetail, fieldDetail);
if (!fieldDetail) { if (!fieldDetail) {
console.log('🔍 AUTOFILL: No fieldDetail found, skipping');
continue; continue;
} }
console.log('🔍 AUTOFILL: Setting value on element', fieldDetail.element);
fieldDetail.element.value = field.value; fieldDetail.element.value = field.value;
// Set the autofilled state on the element // Add event listeners for debugging
console.log('🔍 AUTOFILL: Checking if setAutofilled exists on element'); // try {
console.log('🔍 AUTOFILL: setAutofilled type:', typeof fieldDetail.element.setAutofilled); // fieldDetail.element.addEventListener('focus', () => console.log('[JS] input focused'));
// fieldDetail.element.addEventListener('blur', () => console.log('[JS] input blurred'));
// fieldDetail.element.addEventListener('input', () => console.log('[JS] input event, value:', fieldDetail.element.value));
// } catch (e) {
// console.log('[JS] Could not add event listeners:', e);
// }
// if (typeof fieldDetail.element.setAutofilled === 'function') {
// if (field.value) {
// console.log('AUTOFILL: Calling setAutofilled(true) on element');
// fieldDetail.element.setAutofilled(true);
// console.log('AUTOFILL: setAutofilled(true) called successfully');
// } else {
// console.log('AUTOFILL: Calling setAutofilled(false) on element (empty value)');
// fieldDetail.element.setAutofilled(false);
// }
// } else {
// console.log('AUTOFILL: setAutofilled is not a function on this element');
// }
// Highlight: Set autofilled state for all autofilled fields
if (typeof fieldDetail.element.setAutofilled === 'function') { if (typeof fieldDetail.element.setAutofilled === 'function') {
console.log('🔍 AUTOFILL: Calling setAutofilled(true) on element'); fieldDetail.element.setAutofilled(!!field.value);
fieldDetail.element.setAutofilled(true);
console.log('🔍 AUTOFILL: setAutofilled(true) called successfully');
} else {
console.log('🔍 AUTOFILL: setAutofilled is not a function on this element');
} }
} }
}, },

View file

@ -1202,6 +1202,10 @@ var LoginManagerContent = {
if (!disabledOrReadOnly && !userEnteredDifferentCase && userNameDiffers) { if (!disabledOrReadOnly && !userEnteredDifferentCase && userNameDiffers) {
usernameField.setUserInput(selectedLogin.username); usernameField.setUserInput(selectedLogin.username);
} }
//Set autofilled state if value is present
if (typeof usernameField.setAutofilled === "function" && usernameField.value) {
usernameField.setAutofilled(true);
}
} }
let doc = form.ownerDocument; let doc = form.ownerDocument;
@ -1216,6 +1220,10 @@ var LoginManagerContent = {
}; };
log("Saving autoFilledLogin", autoFilledLogin.guid, "for", form.rootElement); log("Saving autoFilledLogin", autoFilledLogin.guid, "for", form.rootElement);
this.stateForDocument(doc).fillsByRootElement.set(form.rootElement, autoFilledLogin); this.stateForDocument(doc).fillsByRootElement.set(form.rootElement, autoFilledLogin);
// Patch: Set autofilled state if value is present
if (typeof passwordField.setAutofilled === "function" && passwordField.value) {
passwordField.setAutofilled(true);
}
} }
log("_fillForm succeeded"); log("_fillForm succeeded");

View file

@ -40,6 +40,7 @@
#include "nsIFrame.h" #include "nsIFrame.h"
#include "nsIScriptSecurityManager.h" #include "nsIScriptSecurityManager.h"
#include "nsFocusManager.h" #include "nsFocusManager.h"
#include "mozilla/dom/HTMLInputElement.h"
using namespace mozilla; using namespace mozilla;
using namespace mozilla::dom; using namespace mozilla::dom;
@ -541,9 +542,21 @@ nsFormFillController::SetTextValue(const nsAString & aTextValue)
{ {
nsCOMPtr<nsIDOMNSEditableElement> editable = do_QueryInterface(mFocusedInput); nsCOMPtr<nsIDOMNSEditableElement> editable = do_QueryInterface(mFocusedInput);
if (editable) { if (editable) {
editable->BeginProgrammaticValueSet();
mSuppressOnInput = true; mSuppressOnInput = true;
editable->SetUserInput(aTextValue); editable->SetUserInput(aTextValue);
mSuppressOnInput = false; mSuppressOnInput = false;
editable->EndProgrammaticValueSet();
if (mFocusedInput) {
nsCOMPtr<nsIContent> content = do_QueryInterface(mFocusedInput);
if (content) {
mozilla::dom::HTMLInputElement* htmlInput = mozilla::dom::HTMLInputElement::FromContentOrNull(content);
if (htmlInput) {
htmlInput->SetAutofilled(true);
}
}
}
} }
return NS_OK; return NS_OK;
} }
@ -1332,7 +1345,8 @@ nsFormFillController::StopControllingInput()
nsCOMPtr <nsIFormAutoComplete> formAutoComplete = nsCOMPtr <nsIFormAutoComplete> formAutoComplete =
do_GetService("@mozilla.org/satchel/form-autocomplete;1", &rv); do_GetService("@mozilla.org/satchel/form-autocomplete;1", &rv);
if (formAutoComplete) { if (formAutoComplete) {
formAutoComplete->StopControllingInput(mFocusedInput); // PATCH: Do NOT call StopControllingInput here, so autofill state is NOT cleared on blur/focus.
// formAutoComplete->StopControllingInput(mFocusedInput);
} }
mFocusedInputNode = nullptr; mFocusedInputNode = nullptr;