mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-29 11:57:32 +09:00
Issue #2790 - Part 4: Working non persistent autofill highlight
This commit is contained in:
parent
4cdfb9e16b
commit
bf8cfcc980
12 changed files with 207 additions and 68 deletions
|
|
@ -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,7 +3573,18 @@ 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
|
||||||
HTMLInputElement::Focus(ErrorResult& aError)
|
HTMLInputElement::Focus(ErrorResult& aError)
|
||||||
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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; }
|
||||||
|
|
@ -1113,7 +1115,7 @@ protected:
|
||||||
bool MinOrMaxLengthApplies() const { return IsSingleLineTextControl(false, mType); }
|
bool MinOrMaxLengthApplies() const { return IsSingleLineTextControl(false, mType); }
|
||||||
|
|
||||||
void FreeData();
|
void FreeData();
|
||||||
nsTextEditorState *GetEditorState() const;
|
nsTextEditorState* GetEditorState() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages the internal data storage across type changes.
|
* Manages the internal data storage across type changes.
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -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];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1189,7 +1189,7 @@ var LoginManagerContent = {
|
||||||
// Fill the form
|
// Fill the form
|
||||||
|
|
||||||
if (usernameField) {
|
if (usernameField) {
|
||||||
// Don't modify the username field if it's disabled or readOnly so we preserve its case.
|
// Don't modify the username field if it's disabled or readOnly so we preserve its case.
|
||||||
let disabledOrReadOnly = usernameField.disabled || usernameField.readOnly;
|
let disabledOrReadOnly = usernameField.disabled || usernameField.readOnly;
|
||||||
|
|
||||||
let userNameDiffers = selectedLogin.username != usernameField.value;
|
let userNameDiffers = selectedLogin.username != usernameField.value;
|
||||||
|
|
@ -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");
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue