diff --git a/dom/base/Element.h b/dom/base/Element.h index d8f7bc70d..341769878 100644 --- a/dom/base/Element.h +++ b/dom/base/Element.h @@ -1617,6 +1617,16 @@ private: // Data members EventStates mState; + +public: + // Public helper to add or remove event states + void SetEventState(mozilla::EventStates aState, bool aAdd) { + if (aAdd) { + this->AddStates(aState); + } else { + this->RemoveStates(aState); + } + } }; class RemoveFromBindingManagerRunnable : public mozilla::Runnable diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index 64fbda751..d0e2ffb12 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -4095,3 +4095,35 @@ nsTranslationNodeList::GetLength(uint32_t* aRetVal) *aRetVal = mLength; return NS_OK; } + +NS_IMETHODIMP +nsDOMWindowUtils::AddElementEventState(nsIDOMElement* aElement, uint64_t aState) +{ + NS_ENSURE_ARG_POINTER(aElement); + nsCOMPtr content = do_QueryInterface(aElement); + if (!content) { + return NS_ERROR_INVALID_ARG; + } + mozilla::dom::Element* element = static_cast(content.get()); + if (!element) { + return NS_ERROR_INVALID_ARG; + } + element->SetEventState(mozilla::EventStates(aState), true); + return NS_OK; +} + +NS_IMETHODIMP +nsDOMWindowUtils::RemoveElementEventState(nsIDOMElement* aElement, uint64_t aState) +{ + NS_ENSURE_ARG_POINTER(aElement); + nsCOMPtr content = do_QueryInterface(aElement); + if (!content) { + return NS_ERROR_INVALID_ARG; + } + mozilla::dom::Element* element = static_cast(content.get()); + if (!element) { + return NS_ERROR_INVALID_ARG; + } + element->SetEventState(mozilla::EventStates(aState), false); + return NS_OK; +} diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index d266dd4db..922ce4462 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -2780,10 +2780,16 @@ 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"); } } @@ -8462,7 +8468,9 @@ HTMLInputElement::OnValueChanged(bool aNotify, bool 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 input, clearing state\n"); RemoveStates(NS_EVENT_STATE_AUTOFILL); + printf("🔍 AUTOFILL C++: Autofill state cleared from input\n"); } UpdateAllValidityStates(aNotify); diff --git a/dom/html/HTMLTextAreaElement.cpp b/dom/html/HTMLTextAreaElement.cpp index 01c437d09..390f68575 100644 --- a/dom/html/HTMLTextAreaElement.cpp +++ b/dom/html/HTMLTextAreaElement.cpp @@ -367,10 +367,16 @@ 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"); } 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"); } } @@ -1643,7 +1649,9 @@ HTMLTextAreaElement::OnValueChanged(bool aNotify, bool 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 diff --git a/dom/interfaces/base/nsIDOMWindowUtils.idl b/dom/interfaces/base/nsIDOMWindowUtils.idl index bbd06c0fe..c783339cd 100644 --- a/dom/interfaces/base/nsIDOMWindowUtils.idl +++ b/dom/interfaces/base/nsIDOMWindowUtils.idl @@ -1975,6 +1975,20 @@ interface nsIDOMWindowUtils : nsISupports { const long MOUSE_BUTTONS_5TH_BUTTON = 0x10; // Buttons are not specified, will be calculated from |aButton|. const long MOUSE_BUTTONS_NOT_SPECIFIED = -1; + + /** + * Add an EventState bit to an element (privileged only). + * @param element The element to modify. + * @param state The EventState bit (see EventStates.h, e.g. NS_EVENT_STATE_AUTOFILL). + */ + void addElementEventState(in nsIDOMElement element, in unsigned long long state); + + /** + * Remove an EventState bit from an element (privileged only). + * @param element The element to modify. + * @param state The EventState bit. + */ + void removeElementEventState(in nsIDOMElement element, in unsigned long long state); }; [scriptable, uuid(c694e359-7227-4392-a138-33c0cc1f15a6)] diff --git a/layout/style/nsCSSPseudoClassList.h b/layout/style/nsCSSPseudoClassList.h index fdebca2e6..8ca9c8f28 100644 --- a/layout/style/nsCSSPseudoClassList.h +++ b/layout/style/nsCSSPseudoClassList.h @@ -274,6 +274,9 @@ CSS_STATE_PSEUDO_CLASS(mozMeterSubSubOptimum, ":-moz-meter-sub-sub-optimum", 0, // Those values should be parsed but do nothing. CSS_STATE_PSEUDO_CLASS(mozPlaceholder, ":-moz-placeholder", 0, "", NS_EVENT_STATE_IGNORE) +// Internal-only pseudo-class for autofill highlight +CSS_STATE_PSEUDO_CLASS(mozAutofillHighlight, ":-moz-autofill-highlight", CSS_PSEUDO_CLASS_ENABLED_IN_UA_SHEETS, "", NS_EVENT_STATE_AUTOFILL) + #ifdef DEFINED_CSS_STATE_PSEUDO_CLASS #undef DEFINED_CSS_STATE_PSEUDO_CLASS #undef CSS_STATE_PSEUDO_CLASS diff --git a/layout/style/res/forms.css b/layout/style/res/forms.css index 0ae49d7e8..9f77e7fd6 100644 --- a/layout/style/res/forms.css +++ b/layout/style/res/forms.css @@ -1157,3 +1157,9 @@ input[type="date"], input[type="time"] { overflow: hidden !important; } + +/* Autofill highlight for internal-only pseudo-class */ +input:-moz-autofill-highlight, +textarea:-moz-autofill-highlight { + background-color: #ffff99 !important; +} diff --git a/toolkit/components/formautofill/FormAutofillContentService.js b/toolkit/components/formautofill/FormAutofillContentService.js index df95cde67..4756663c1 100644 --- a/toolkit/components/formautofill/FormAutofillContentService.js +++ b/toolkit/components/formautofill/FormAutofillContentService.js @@ -11,6 +11,8 @@ "use strict"; +console.log('🔍 AUTOFILL: FormAutofillContentService.js loaded'); + const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; Cu.import("resource://gre/modules/Services.jsm"); @@ -29,6 +31,19 @@ function FormHandler(aForm, aWindow) { this.window = aWindow; this.fieldDetails = []; + + // Add a reset event listener to clear autofill state + this.form.addEventListener("reset", () => { + console.log('Form reset detected, clearing autofill state'); + for (let element of this.form.elements) { + if (typeof element.setAutofilled === "function") { + element.setAutofilled(false); + console.log('setAutofilled(false) called on', element); + } + } + // Optionally, clear fieldDetails if you want to force re-collection + // this.fieldDetails = []; + }); } FormHandler.prototype = { @@ -130,7 +145,9 @@ FormHandler.prototype = { return "cancel"; } + console.log('🔍 AUTOFILL: About to call autofillFormFields with result:', result); this.autofillFormFields(result); + console.log('🔍 AUTOFILL: autofillFormFields completed'); return "success"; }), @@ -226,22 +243,38 @@ FormHandler.prototype = { * } */ autofillFormFields(aAutofillResult) { + console.log('🔍 AUTOFILL: autofillFormFields called with', aAutofillResult); + for (let field of aAutofillResult.fields) { + console.log('🔍 AUTOFILL: Processing field', field); + // Get the field details, if it was processed by the user interface. let fieldDetail = this.fieldDetails .find(f => f.section == field.section && f.addressType == field.addressType && f.contactType == field.contactType && f.fieldName == field.fieldName); + + console.log('🔍 AUTOFILL: Found fieldDetail?', !!fieldDetail, fieldDetail); + if (!fieldDetail) { + console.log('🔍 AUTOFILL: No fieldDetail found, skipping'); continue; } + console.log('🔍 AUTOFILL: Setting value on element', fieldDetail.element); fieldDetail.element.value = field.value; // Set the autofilled state on the element + console.log('🔍 AUTOFILL: Checking if setAutofilled exists on element'); + console.log('🔍 AUTOFILL: setAutofilled type:', typeof fieldDetail.element.setAutofilled); + if (typeof fieldDetail.element.setAutofilled === 'function') { + console.log('🔍 AUTOFILL: Calling setAutofilled(true) on element'); fieldDetail.element.setAutofilled(true); + console.log('🔍 AUTOFILL: setAutofilled(true) called successfully'); + } else { + console.log('🔍 AUTOFILL: setAutofilled is not a function on this element'); } } },