Merge remote-tracking branch 'origin/tracking' into custom

This commit is contained in:
roytam1 2025-07-29 10:44:33 +08:00
commit 4c1a4a4402
31 changed files with 827 additions and 36 deletions

View file

@ -3381,7 +3381,9 @@ exports.CSS_PROPERTIES = {
"word-spacing",
"overflow-wrap",
"writing-mode",
"z-index"
"z-index",
"overflow-block",
"overflow-inline"
],
"supports": [
1,
@ -3411,7 +3413,6 @@ exports.CSS_PROPERTIES = {
"-moz-grid-line",
"-moz-groupbox",
"-moz-gtk-info-bar",
"clip",
"-moz-image-rect",
"-moz-inline-box",
"-moz-inline-grid",
@ -3502,6 +3503,7 @@ exports.CSS_PROPERTIES = {
"checkbox-container",
"checkbox-label",
"checkmenuitem",
"clip",
"clone",
"collapse",
"color",
@ -8568,6 +8570,42 @@ exports.CSS_PROPERTIES = {
"visible"
]
},
"overflow-block": {
"isInherited": false,
"subproperties": [
"overflow-block"
],
"supports": [],
"values": [
"auto",
"clip",
"hidden",
"inherit",
"initial",
"revert",
"scroll",
"unset",
"visible"
]
},
"overflow-inline": {
"isInherited": false,
"subproperties": [
"overflow-inline"
],
"supports": [],
"values": [
"auto",
"clip",
"hidden",
"inherit",
"initial",
"revert",
"scroll",
"unset",
"visible"
]
},
"overflow-wrap": {
"isInherited": true,
"subproperties": [

View file

@ -1603,6 +1603,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

View file

@ -4058,3 +4058,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<nsIContent> content = do_QueryInterface(aElement);
if (!content) {
return NS_ERROR_INVALID_ARG;
}
mozilla::dom::Element* element = static_cast<mozilla::dom::Element*>(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<nsIContent> content = do_QueryInterface(aElement);
if (!content) {
return NS_ERROR_INVALID_ARG;
}
mozilla::dom::Element* element = static_cast<mozilla::dom::Element*>(content.get());
if (!element) {
return NS_ERROR_INVALID_ARG;
}
element->SetEventState(mozilla::EventStates(aState), false);
return NS_OK;
}

View file

@ -293,6 +293,9 @@ private:
// Modal <dialog> element
#define NS_EVENT_STATE_MODAL_DIALOG NS_DEFINE_EVENT_STATE_MACRO(54)
// Autofilled input element (for :autofill pseudo-class)
#define NS_EVENT_STATE_AUTOFILL NS_DEFINE_EVENT_STATE_MACRO(55)
#define DIR_ATTR_STATES (NS_EVENT_STATE_HAS_DIR_ATTR | \
NS_EVENT_STATE_DIR_ATTR_LTR | \
NS_EVENT_STATE_DIR_ATTR_RTL | \

View file

@ -2832,6 +2832,20 @@ HTMLInputElement::SetUserInput(const nsAString& aValue)
return NS_OK;
}
void
HTMLInputElement::SetAutofilled(bool aAutofilled)
{
nsAutoString value;
GetValueInternal(value);
if (aAutofilled) {
AddStates(NS_EVENT_STATE_AUTOFILL);
mAutofilledValue = value;
} else {
RemoveStates(NS_EVENT_STATE_AUTOFILL);
mAutofilledValue.Truncate();
}
}
nsIEditor*
HTMLInputElement::GetEditor()
{
@ -3562,7 +3576,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)
@ -7070,6 +7095,13 @@ HTMLInputElement::IntrinsicState() const
state |= NS_EVENT_STATE_MOZ_SUBMITINVALID;
}
// Autofill highlight should persist as long as the value matches the autofilled value
nsAutoString value;
GetValueInternal(value);
if (!mAutofilledValue.IsEmpty() && value == mAutofilledValue) {
state |= NS_EVENT_STATE_AUTOFILL;
}
return state;
}
@ -8505,8 +8537,23 @@ HTMLInputElement::InitializeKeyboardEventListeners()
NS_IMETHODIMP_(void)
HTMLInputElement::OnValueChanged(bool aNotify, bool aWasInteractiveUserChange)
{
nsAutoString value;
GetValueInternal(value);
mLastValueChangeWasInteractive = aWasInteractiveUserChange;
// Only remove autofilled state if the value actually changed from autofilled value
if (aWasInteractiveUserChange && State().HasState(NS_EVENT_STATE_AUTOFILL)) {
if (!mAutofilledValue.IsEmpty() && mAutofilledValue != value) {
RemoveStates(NS_EVENT_STATE_AUTOFILL);
mAutofilledValue.Truncate();
}
} else if (aWasInteractiveUserChange && !State().HasState(NS_EVENT_STATE_AUTOFILL)) {
// If the value is changed back to the autofilled value, restore the state
if (!mAutofilledValue.IsEmpty() && mAutofilledValue == value) {
AddStates(NS_EVENT_STATE_AUTOFILL);
}
}
UpdateAllValidityStates(aNotify);
if (HasDirAuto()) {
@ -8537,6 +8584,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)
{

View file

@ -160,6 +160,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; }
@ -845,6 +847,15 @@ public:
void SetUserInput(const nsAString& aInput,
nsIPrincipal& aSubjectPrincipal);
/**
* Sets or clears the autofilled state of this input element.
* When setting, also stores the autofilled value for persistence.
* When clearing, clears the stored autofilled value.
*
* @param aAutofilled Whether the element should be marked as autofilled
*/
void SetAutofilled(bool aAutofilled);
// XPCOM GetPhonetic() is OK
/**
@ -860,6 +871,8 @@ public:
void UpdateEntries(const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories);
void SetAutofilledValue(const nsAString& aValue) { mAutofilledValue = aValue; }
protected:
virtual ~HTMLInputElement();
@ -1103,7 +1116,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.
@ -1629,6 +1642,11 @@ protected:
bool mNumberControlSpinnerSpinsUp : 1;
bool mPickerRunning : 1;
bool mSelectionCached : 1;
/**
* The value that was autofilled by the browser. Used to persist the autofill
* highlight as long as the value matches, regardless of focus/blur.
*/
nsString mAutofilledValue;
private:
static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
@ -1772,6 +1790,8 @@ private:
nsCOMPtr<nsIFilePicker> mFilePicker;
RefPtr<HTMLInputElement> mInput;
};
void EnsureAutofillState();
};
} // namespace dom

View file

@ -366,6 +366,46 @@ HTMLTextAreaElement::SetUserInput(const nsAString& aValue)
return SetValueInternal(aValue, nsTextEditorState::eSetValue_BySetUserInput);
}
void
HTMLTextAreaElement::SetAutofilled(bool aAutofilled)
{
if (aAutofilled) {
AddStates(NS_EVENT_STATE_AUTOFILL);
GetValueInternal(mAutofilledValue, true); // Store the autofilled value
} else {
RemoveStates(NS_EVENT_STATE_AUTOFILL);
mAutofilledValue.Truncate();
}
}
NS_IMETHODIMP_(void)
HTMLTextAreaElement::OnValueChanged(bool aNotify, bool aWasInteractiveUserChange)
{
nsAutoString value;
GetValueInternal(value, true);
// 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) {
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);
}
}
NS_IMETHODIMP
HTMLTextAreaElement::SetValueChanged(bool aValueChanged)
{
@ -556,6 +596,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)
{
@ -580,6 +633,9 @@ HTMLTextAreaElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
}
UpdateState(true);
// Defensive: re-apply autofill state if value is still autofilled value
EnsureAutofillState();
}
return NS_OK;
@ -1186,6 +1242,10 @@ HTMLTextAreaElement::IntrinsicState() const
{
EventStates state = nsGenericHTMLFormElementWithState::IntrinsicState();
if (!mAutofilledValue.IsEmpty()) {
state |= NS_EVENT_STATE_AUTOFILL;
}
if (HasAttr(kNameSpaceID_None, nsGkAtoms::required)) {
state |= NS_EVENT_STATE_REQUIRED;
} else {
@ -1628,23 +1688,6 @@ HTMLTextAreaElement::InitializeKeyboardEventListeners()
mState.InitializeKeyboardEventListeners();
}
NS_IMETHODIMP_(void)
HTMLTextAreaElement::OnValueChanged(bool aNotify, bool aWasInteractiveUserChange)
{
mLastValueChangeWasInteractive = aWasInteractiveUserChange;
// 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()
{
@ -1660,11 +1703,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

View file

@ -69,6 +69,17 @@ 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.
* This is used by the browser's autofill system to indicate when
* a value has been automatically filled (e.g., from saved form data).
*
* @param aAutofilled Whether the element should be marked as autofilled
*/
void SetAutofilled(bool aAutofilled);
// nsIFormControl
NS_IMETHOD_(uint32_t) GetType() const override { return NS_FORM_TEXTAREA; }
@ -293,6 +304,7 @@ public:
{
return mState.GetEditor();
}
nsTextEditorState* GetEditorState() const;
protected:
virtual ~HTMLTextAreaElement() {}
@ -324,6 +336,7 @@ protected:
void FireChangeEventIfNeeded();
nsString mFocusedValue;
nsString mAutofilledValue;
/** The state of the text editor (selection controller and the editor) **/
nsTextEditorState mState;
@ -397,6 +410,7 @@ protected:
private:
static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
nsRuleData* aData);
void EnsureAutofillState();
};
} // namespace dom

View file

@ -163,6 +163,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();
@ -348,6 +349,7 @@ private:
mutable bool mSelectionRestoreEagerInit; // Whether we're eager initing because of selection restore
bool mPlaceholderVisibility;
bool mIsCommittingComposition;
bool mSettingValue;
};
inline void

View file

@ -1944,6 +1944,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)]

View file

@ -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();
};

View file

@ -200,6 +200,13 @@ partial interface HTMLInputElement {
// for example will be dispatched when focusing out the element.
[Func="IsChromeOrXBL", NeedsSubjectPrincipal]
void setUserInput(DOMString input);
// Sets or clears the autofilled state of this input element.
// This is used by the browser's autofill system to indicate when
// a value has been automatically filled (e.g., from saved passwords
// or form data).
[ChromeOnly]
void setAutofilled(boolean autofilled);
};
partial interface HTMLInputElement {

View file

@ -97,4 +97,10 @@ partial interface HTMLTextAreaElement {
// element.
[ChromeOnly]
void setUserInput(DOMString input);
// Sets or clears the autofilled state of this textarea element.
// This is used by the browser's autofill system to indicate when
// a value has been automatically filled (e.g., from saved form data).
[ChromeOnly]
void setAutofilled(boolean autofilled);
};

View file

@ -348,6 +348,41 @@ NS_HSL2RGB(float h, float s, float l)
return NS_RGB(r, g, b);
}
// RGB to HSL conversion function
// The uint8_t RGB parameters are expected to be in the range 0-255
// Returns HSL values in the range: H[0-1], S[0-1], L[0-1]
void
NS_RGB2HSL(uint8_t aR, uint8_t aG, uint8_t aB, float* aH, float* aS, float* aL)
{
float r = aR / 255.0f;
float g = aG / 255.0f;
float b = aB / 255.0f;
float max = std::max(r, std::max(g, b));
float min = std::min(r, std::min(g, b));
float h, s, l = (max + min) / 2.0f;
if (max == min) {
h = s = 0.0f; // achromatic
} else {
float d = max - min;
s = l > 0.5f ? d / (2.0f - max - min) : d / (max + min);
if (max == r) {
h = (g - b) / d + (g < b ? 6.0f : 0.0f);
} else if (max == g) {
h = (b - r) / d + 2.0f;
} else {
h = (r - g) / d + 4.0f;
}
h /= 6.0f;
}
*aH = h;
*aS = s;
*aL = l;
}
const char*
NS_RGBToColorName(nscolor aColor)
{

View file

@ -113,6 +113,11 @@ const char * const * NS_AllColorNames(size_t *aSizeArray);
// the float parameters are all expected to be in the range 0-1
nscolor NS_HSL2RGB(float h, float s, float l);
// function to convert from RGB color space to HSL color space
// the uint8_t RGB parameters are expected to be in the range 0-255
// the float HSL parameters will be set to values in the range 0-1
void NS_RGB2HSL(uint8_t aR, uint8_t aG, uint8_t aB, float* aH, float* aS, float* aL);
// Return a color name for the given nscolor. If there is no color
// name for it, returns null. If there are multiple possible color
// names for the given color, the first one in nsColorNameList.h

View file

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

View file

@ -198,6 +198,7 @@ CSS_KEY(collapse, collapse)
CSS_KEY(color, color)
CSS_KEY(color-burn, color_burn)
CSS_KEY(color-dodge, color_dodge)
CSS_KEY(color-mix, color_mix)
CSS_KEY(common-ligatures, common_ligatures)
CSS_KEY(column, column)
CSS_KEY(column-reverse, column_reverse)
@ -313,6 +314,7 @@ CSS_KEY(horizontal, horizontal)
CSS_KEY(horizontal-tb, horizontal_tb)
CSS_KEY(hue, hue)
CSS_KEY(hue-rotate, hue_rotate)
CSS_KEY(hsl, hsl)
CSS_KEY(hz, hz)
CSS_KEY(icon, icon)
CSS_KEY(ignore, ignore)

View file

@ -7444,6 +7444,110 @@ CSSParserImpl::ParseColor(nsCSSValue& aValue)
}
break;
case eCSSToken_Function: {
// check for color-mix function
if (mToken.mIdent.LowerCaseEqualsLiteral("color-mix")) {
// parse color-mix function
if (!GetToken(true)) {
SkipUntil(')');
return CSSParseResult::Error;
}
if (mToken.mType != eCSSToken_Ident || !mToken.mIdent.LowerCaseEqualsLiteral("in")) {
SkipUntil(')');
return CSSParseResult::Error;
}
// Check for supported color spaces: srgb or hsl
if (!GetToken(true) || mToken.mType != eCSSToken_Ident) {
SkipUntil(')');
return CSSParseResult::Error;
}
mozilla::css::ColorMixColorSpace colorSpace;
if (mToken.mIdent.LowerCaseEqualsLiteral("srgb")) {
colorSpace = mozilla::css::ColorMixColorSpace::sRGB;
} else if (mToken.mIdent.LowerCaseEqualsLiteral("hsl")) {
colorSpace = mozilla::css::ColorMixColorSpace::HSL;
} else {
SkipUntil(')');
return CSSParseResult::Error;
}
if (!ExpectSymbol(',', true)) {
SkipUntil(')');
return CSSParseResult::Error;
}
nsCSSValue color1;
if (ParseColor(color1) != CSSParseResult::Ok) {
SkipUntil(')');
return CSSParseResult::Error;
}
// parse optional weight for first color
bool w1_specified = false;
float w1 = 0.5f; // Default to 50%
if (GetToken(true)) {
if (mToken.mType == eCSSToken_Percentage) {
w1 = mToken.mNumber; // percentage tokens are already normalized (0.0-1.0)
w1_specified = true;
// Reject invalid percentages (outside 0-100% range)
if (w1 < 0.0f || w1 > 1.0f) {
SkipUntil(')');
return CSSParseResult::Error;
}
} else {
UngetToken();
}
}
if (!ExpectSymbol(',', true)) {
SkipUntil(')');
return CSSParseResult::Error;
}
nsCSSValue color2;
if (ParseColor(color2) != CSSParseResult::Ok) {
SkipUntil(')');
return CSSParseResult::Error;
}
// parse optional weight for second color
bool w2_specified = false;
float w2 = 0.5f; // default to 50%
if (GetToken(true)) {
if (mToken.mType == eCSSToken_Percentage) {
w2 = mToken.mNumber; // percentage tokens are already normalized (0.0-1.0)
w2_specified = true;
// Reject invalid percentages (outside 0-100% range)
if (w2 < 0.0f || w2 > 1.0f) {
SkipUntil(')');
return CSSParseResult::Error;
}
} else {
UngetToken();
}
}
if (w1_specified && !w2_specified) {
// first specified, second should be complement
w2 = 1.0f - w1;
} else if (!w1_specified && w2_specified) {
// second specified, first should be complement
w1 = 1.0f - w2;
}
if (!ExpectSymbol(')', true)) {
SkipUntil(')');
return CSSParseResult::Error;
}
RefPtr<mozilla::css::ColorMixValue> colorMix = new mozilla::css::ColorMixValue(
colorSpace, color1, color2, w1, w2);
aValue.SetColorMixValue(colorMix.forget());
return CSSParseResult::Ok;
}
bool isRGB;
bool isHSL;
if ((isRGB = mToken.mIdent.LowerCaseEqualsLiteral("rgb")) ||
@ -8650,7 +8754,8 @@ CSSParserImpl::ParseVariant(nsCSSValue& aValue,
(tk->mIdent.LowerCaseEqualsLiteral("rgb") ||
tk->mIdent.LowerCaseEqualsLiteral("hsl") ||
tk->mIdent.LowerCaseEqualsLiteral("rgba") ||
tk->mIdent.LowerCaseEqualsLiteral("hsla"))))
tk->mIdent.LowerCaseEqualsLiteral("hsla") ||
tk->mIdent.LowerCaseEqualsLiteral("color-mix"))))
{
// Put token back so that parse color can get it
UngetToken();

View file

@ -194,6 +194,9 @@ CSS_STATE_PSEUDO_CLASS(mozFullScreen, ":-moz-full-screen", 0, "", NS_EVENT_STATE
CSS_STATE_PSEUDO_CLASS(modal, ":modal", 0, "", NS_EVENT_STATE_MODAL_DIALOG)
CSS_STATE_PSEUDO_CLASS(mozModalDialog, ":-moz-modal-dialog", 0, "", NS_EVENT_STATE_MODAL_DIALOG)
// Matches autofilled input elements
CSS_STATE_PSEUDO_CLASS(autofill, ":autofill", 0, "", NS_EVENT_STATE_AUTOFILL)
// Matches if the element is focused and should show a focus ring
CSS_STATE_PSEUDO_CLASS(mozFocusRing, ":-moz-focusring", 0, "", NS_EVENT_STATE_FOCUSRING)
@ -274,6 +277,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

View file

@ -16,6 +16,7 @@
#include "PLDHashTable.h"
#include "nsICSSPseudoComparator.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/css/ImportRule.h"
#include "mozilla/css/StyleRule.h"
#include "mozilla/css/GroupRule.h"
#include "nsIDocument.h"
@ -649,6 +650,12 @@ CascadeRuleEnumFunc(css::Rule* aRule, void* aData)
if (!layer->mData->mCounterStyleRules.AppendElement(counterStyleRule)) {
return false;
}
} else if (css::Rule::IMPORT_RULE == type &&
nsCSSRuleUtils::LoadImportedSheetsInOrderEnabled()) {
css::ImportRule* importRule = static_cast<css::ImportRule*>(aRule);
nsCSSRuleProcessor::CascadeSheet(
importRule->GetStyleSheet()->AsConcrete(),
layer);
}
return true;
}
@ -660,10 +667,12 @@ nsCSSRuleProcessor::CascadeSheet(CSSStyleSheet* aSheet, CascadeLayer* aLayer)
aSheet->UseForPresentation(aLayer->mPresContext,
aLayer->mCacheKey) &&
aSheet->mInner) {
CSSStyleSheet* child = aSheet->mInner->mFirstChild;
while (child) {
CascadeSheet(child, aLayer);
child = child->mNext;
if (!nsCSSRuleUtils::LoadImportedSheetsInOrderEnabled()) {
CSSStyleSheet* child = aSheet->mInner->mFirstChild;
while (child) {
CascadeSheet(child, aLayer);
child = child->mNext;
}
}
if (!aSheet->mInner->mOrderedRules.EnumerateForwards(CascadeRuleEnumFunc,

View file

@ -154,13 +154,13 @@ public:
bool IsInRuleProcessorCache() const { return mInRuleProcessorCache; }
bool IsUsedByMultipleStyleSets() const { return mStyleSetRefCnt > 1; }
static bool CascadeSheet(mozilla::CSSStyleSheet* aSheet,
CascadeLayer* aLayer);
protected:
virtual ~nsCSSRuleProcessor();
private:
static bool CascadeSheet(mozilla::CSSStyleSheet* aSheet,
CascadeLayer* aLayer);
RuleProcessorGroup* GetGroup(nsPresContext* aPresContext);
void RefreshGroup(nsPresContext* aPresContext);

View file

@ -19,6 +19,7 @@ using namespace mozilla::dom;
#define VISITED_PSEUDO_PREF "layout.css.visited_links_enabled"
static bool gSupportVisitedPseudo = true;
static bool gLoadImportedSheetsInOrder = true;
static nsTArray<nsCOMPtr<nsIAtom>>* sSystemMetrics = 0;
@ -31,6 +32,9 @@ nsCSSRuleUtils::Startup()
{
Preferences::AddBoolVarCache(
&gSupportVisitedPseudo, VISITED_PSEUDO_PREF, true);
Preferences::AddBoolVarCache(&gLoadImportedSheetsInOrder,
"layout.css.load-imported-sheets-in-order",
true);
}
static bool
@ -206,6 +210,12 @@ nsCSSRuleUtils::HasSystemMetric(nsIAtom* aMetric)
return sSystemMetrics->IndexOf(aMetric) != sSystemMetrics->NoIndex;
}
/* static */ bool
nsCSSRuleUtils::LoadImportedSheetsInOrderEnabled()
{
return gLoadImportedSheetsInOrder;
}
#ifdef XP_WIN
/* static */ uint8_t
nsCSSRuleUtils::GetWindowsThemeIdentifier()
@ -566,6 +576,14 @@ nsCSSRuleUtils::StateSelectorMatches(Element* aElement,
for (nsPseudoClassList* pseudoClass = aSelector->mPseudoClassList;
pseudoClass;
pseudoClass = pseudoClass->mNext) {
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
}
auto idx = static_cast<CSSPseudoClassTypeBase>(pseudoClass->mType);
EventStates statesToCheck = nsCSSPseudoClasses::sPseudoClassStates[idx];
if (!statesToCheck.IsEmpty() && !StateSelectorMatches(aElement,

View file

@ -27,6 +27,8 @@ struct nsCSSRuleUtils
static void FreeSystemMetrics();
static bool HasSystemMetric(nsIAtom* aMetric);
static bool LoadImportedSheetsInOrderEnabled();
#ifdef XP_WIN
// Cached theme identifier for the moz-windows-theme media query.
static uint8_t GetWindowsThemeIdentifier();

View file

@ -166,6 +166,10 @@ nsCSSValue::nsCSSValue(const nsCSSValue& aCopy)
mValue.mComplexColor = aCopy.mValue.mComplexColor;
mValue.mComplexColor->AddRef();
}
else if (eCSSUnit_ColorMix == mUnit) {
mValue.mColorMix = aCopy.mValue.mColorMix;
mValue.mColorMix->AddRef();
}
else if (eCSSUnit_Revert == mUnit) {
mValue.mCascadeOrigin = aCopy.mValue.mCascadeOrigin;
}
@ -282,6 +286,14 @@ bool nsCSSValue::operator==(const nsCSSValue& aOther) const
else if (eCSSUnit_ComplexColor == mUnit) {
return *mValue.mComplexColor == *aOther.mValue.mComplexColor;
}
else if (eCSSUnit_ColorMix == mUnit) {
return mValue.mColorMix == aOther.mValue.mColorMix ||
(mValue.mColorMix->mColorSpace == aOther.mValue.mColorMix->mColorSpace &&
mValue.mColorMix->mColor1 == aOther.mValue.mColorMix->mColor1 &&
mValue.mColorMix->mColor2 == aOther.mValue.mColorMix->mColor2 &&
mValue.mColorMix->mWeight1 == aOther.mValue.mColorMix->mWeight1 &&
mValue.mColorMix->mWeight2 == aOther.mValue.mColorMix->mWeight2);
}
else if (eCSSUnit_Revert == mUnit) {
return mValue.mCascadeOrigin == aOther.mValue.mCascadeOrigin;
}
@ -421,6 +433,8 @@ void nsCSSValue::DoReset()
mValue.mFloatColor->Release();
} else if (eCSSUnit_ComplexColor == mUnit) {
mValue.mComplexColor->Release();
} else if (eCSSUnit_ColorMix == mUnit) {
mValue.mColorMix->Release();
} else if (UnitHasArrayValue()) {
mValue.mArray->Release();
} else if (eCSSUnit_URL == mUnit) {
@ -545,6 +559,14 @@ nsCSSValue::SetComplexColorValue(already_AddRefed<ComplexColorValue> aValue)
mValue.mComplexColor = aValue.take();
}
void
nsCSSValue::SetColorMixValue(already_AddRefed<ColorMixValue> aValue)
{
Reset();
mUnit = eCSSUnit_ColorMix;
mValue.mColorMix = aValue.take();
}
void
nsCSSValue::SetCascadeOriginValue(mozilla::SheetType aValue, nsCSSUnit aUnit)
{
@ -1709,6 +1731,28 @@ nsCSSValue::AppendToString(nsCSSPropertyID aProperty, nsAString& aResult,
}
serializable.AppendToString(aProperty, aResult, aSerialization);
}
else if (eCSSUnit_ColorMix == unit) {
const ColorMixValue* colorMix = GetColorMixValue();
aResult.AppendLiteral("color-mix(in ");
// append color space
switch (colorMix->mColorSpace) {
case mozilla::css::ColorMixColorSpace::sRGB:
aResult.AppendLiteral("srgb");
break;
case mozilla::css::ColorMixColorSpace::HSL:
aResult.AppendLiteral("hsl");
break;
}
// append color1 and color2
aResult.AppendLiteral(", ");
colorMix->mColor1.AppendToString(aProperty, aResult, aSerialization);
aResult.AppendLiteral(", ");
colorMix->mColor2.AppendToString(aProperty, aResult, aSerialization);
aResult.Append(')');
}
else if (eCSSUnit_URL == unit || eCSSUnit_Image == unit) {
aResult.AppendLiteral("url(");
nsStyleUtil::AppendEscapedCSSString(
@ -2007,6 +2051,7 @@ nsCSSValue::AppendToString(nsCSSPropertyID aProperty, nsAString& aResult,
case eCSSUnit_HSLColor: break;
case eCSSUnit_HSLAColor: break;
case eCSSUnit_ComplexColor: break;
case eCSSUnit_ColorMix: break;
case eCSSUnit_Percent: aResult.Append(char16_t('%')); break;
case eCSSUnit_Number: break;
case eCSSUnit_Gradient: break;
@ -2221,6 +2266,11 @@ nsCSSValue::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
n += mValue.mComplexColor->SizeOfIncludingThis(aMallocSizeOf);
break;
// Color Mix
case eCSSUnit_ColorMix:
n += mValue.mColorMix->SizeOfIncludingThis(aMallocSizeOf);
break;
// Cascade Origin: nothing extra to measure.
case eCSSUnit_Revert:
break;
@ -3075,6 +3125,18 @@ css::ComplexColorValue::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
return n;
}
size_t
css::ColorMixValue::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
{
// Only measure it if it's unshared, to avoid double-counting.
size_t n = 0;
if (mRefCnt <= 1) {
n += aMallocSizeOf(this);
n += mColor1.SizeOfExcludingThis(aMallocSizeOf);
n += mColor2.SizeOfExcludingThis(aMallocSizeOf);
}
return n;
}
nsCSSValueGradientStop::nsCSSValueGradientStop()
: mLocation(eCSSUnit_None),
mColor(eCSSUnit_Null),

View file

@ -521,6 +521,7 @@ enum nsCSSUnit {
eCSSUnit_HSLColor = 89, // (nsCSSValueFloatColor*)
eCSSUnit_HSLAColor = 90, // (nsCSSValueFloatColor*)
eCSSUnit_ComplexColor = 91, // (ComplexColorValue*)
eCSSUnit_ColorMix = 92, // (ColorMixValue*)
eCSSUnit_Percent = 100, // (float) 1.0 == 100%) value is percentage of something
eCSSUnit_Number = 101, // (float) value is numeric (usually multiplier, different behavior than percent)
@ -606,6 +607,13 @@ struct nsCSSValueTriplet;
struct nsCSSValueTriplet_heap;
class nsCSSValueFloatColor;
namespace mozilla {
namespace css {
enum class ColorMixColorSpace;
struct ColorMixValue;
} // namespace css
} // namespace mozilla
class nsCSSValue {
public:
struct Array;
@ -804,6 +812,12 @@ public:
MOZ_ASSERT(mUnit == eCSSUnit_ComplexColor);
return mValue.mComplexColor->ToComplexColor();
}
mozilla::css::ColorMixValue* GetColorMixValue() const
{
MOZ_ASSERT(mUnit == eCSSUnit_ColorMix);
return mValue.mColorMix;
}
Array* GetArrayValue() const
{
@ -950,6 +964,8 @@ public:
void SetRGBAColorValue(const mozilla::css::RGBAColorData& aValue);
void SetComplexColorValue(
already_AddRefed<mozilla::css::ComplexColorValue> aValue);
void SetColorMixValue(
already_AddRefed<mozilla::css::ColorMixValue> aValue);
void SetCascadeOriginValue(mozilla::SheetType aValue, nsCSSUnit aUnit);
void SetArrayValue(nsCSSValue::Array* aArray, nsCSSUnit aUnit);
void SetURLValue(mozilla::css::URLValue* aURI);
@ -1063,6 +1079,7 @@ protected:
nsCSSValueFloatColor* MOZ_OWNING_REF mFloatColor;
mozilla::css::FontFamilyListRefCnt* MOZ_OWNING_REF mFontFamilyList;
mozilla::css::ComplexColorValue* MOZ_OWNING_REF mComplexColor;
mozilla::css::ColorMixValue* MOZ_OWNING_REF mColorMix;
mozilla::SheetType mCascadeOrigin;
} mValue;
};
@ -1972,5 +1989,41 @@ protected:
static const corner_type corners[4];
};
namespace mozilla {
namespace css {
enum class ColorMixColorSpace {
sRGB,
HSL
};
struct ColorMixValue final
{
ColorMixColorSpace mColorSpace;
nsCSSValue mColor1;
nsCSSValue mColor2;
float mWeight1;
float mWeight2;
ColorMixValue(ColorMixColorSpace aColorSpace, const nsCSSValue& aColor1, const nsCSSValue& aColor2)
: mColorSpace(aColorSpace), mColor1(aColor1), mColor2(aColor2), mWeight1(0.5f), mWeight2(0.5f) {}
ColorMixValue(ColorMixColorSpace aColorSpace, const nsCSSValue& aColor1, const nsCSSValue& aColor2,
float aWeight1, float aWeight2)
: mColorSpace(aColorSpace), mColor1(aColor1), mColor2(aColor2), mWeight1(aWeight1), mWeight2(aWeight2) {}
ColorMixValue(const ColorMixValue&) = delete;
NS_INLINE_DECL_REFCOUNTING(ColorMixValue)
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
private:
~ColorMixValue() {}
};
} // namespace css
} // namespace mozilla
#endif /* nsCSSValue_h___ */

View file

@ -1137,6 +1137,165 @@ static bool SetColor(const nsCSSValue& aValue, const nscolor aParentColor,
aResult = aParentColor;
result = true;
aConditions.SetUncacheable();
} else if (eCSSUnit_ColorMix == unit) {
const mozilla::css::ColorMixValue* colorMix = aValue.GetColorMixValue();
if (colorMix) {
nscolor color1, color2;
if (SetColor(colorMix->mColor1, aParentColor, aPresContext, aContext, color1, aConditions) &&
SetColor(colorMix->mColor2, aParentColor, aPresContext, aContext, color2, aConditions)) {
// interpolate each RGBA component with proper percentage handling
float w1 = colorMix->mWeight1;
float w2 = colorMix->mWeight2;
// edge case: if both weights are zero, return transparent black
if (w1 <= 0.0f && w2 <= 0.0f) {
aResult = NS_RGBA(0, 0, 0, 0);
result = true;
} else {
// normalize weights
float sum = w1 + w2;
if (sum <= 0.0f) {
// both weights zero - use equal weighting
w1 = w2 = 0.5f;
sum = 1.0f;
}
float norm1 = w1 / sum;
float norm2 = w2 / sum;
if (colorMix->mColorSpace == mozilla::css::ColorMixColorSpace::HSL) {
// HSL color space mixing
float h1, s1, l1, h2, s2, l2;
float a1 = NS_GET_A(color1) / 255.0f;
float a2 = NS_GET_A(color2) / 255.0f;
// Convert RGB colors to HSL
NS_RGB2HSL(NS_GET_R(color1), NS_GET_G(color1), NS_GET_B(color1), &h1, &s1, &l1);
NS_RGB2HSL(NS_GET_R(color2), NS_GET_G(color2), NS_GET_B(color2), &h2, &s2, &l2);
float h, s, l, a;
// check if both are opaque
if (a1 >= 1.0f && a2 >= 1.0f) {
if (s1 == 0.0f || s2 == 0.0f) {
h = (s1 == 0.0f) ? h2 : h1;
} else {
float hue_diff = h2 - h1;
if (hue_diff > 0.5f) {
h1 += 1.0f;
} else if (hue_diff < -0.5f) {
h2 += 1.0f;
}
h = h1 * norm1 + h2 * norm2;
if (h >= 1.0f) h -= 1.0f;
}
// interpolate saturation and lightness normally
s = s1 * norm1 + s2 * norm2;
l = l1 * norm1 + l2 * norm2;
a = 1.0f; // Result is opaque
} else {
// handle alpha premultiplication for HSL components when transparency is involved
float alpha1_weight = norm1 * a1;
float alpha2_weight = norm2 * a2;
float total_alpha_weight = alpha1_weight + alpha2_weight;
if (total_alpha_weight <= 0.0f) {
// both colors are fully transparent
h = s = l = 0.0f;
a = 0.0f;
} else {
// normalize alpha-weighted contributions
float norm_alpha1 = alpha1_weight / total_alpha_weight;
float norm_alpha2 = alpha2_weight / total_alpha_weight;
// handle hue interpolation (circular)
if (s1 == 0.0f || s2 == 0.0f) {
h = (s1 == 0.0f) ? h2 : h1;
} else {
float hue_diff = h2 - h1;
if (hue_diff > 0.5f) {
h1 += 1.0f;
} else if (hue_diff < -0.5f) {
h2 += 1.0f;
}
h = h1 * norm_alpha1 + h2 * norm_alpha2;
if (h >= 1.0f) h -= 1.0f;
}
// interpolate saturation and lightness with alpha weighting
s = s1 * norm_alpha1 + s2 * norm_alpha2;
l = l1 * norm_alpha1 + l2 * norm_alpha2;
// interpolate alpha normally (without premultiplication)
a = a1 * norm1 + a2 * norm2;
}
}
// Convert back to RGB
nscolor hslResult = NS_HSL2RGB(h, s, l);
uint8_t aInt = (uint8_t)mozilla::clamped(a * 255.0f + 0.5f, 0.0f, 255.0f);
aResult = NS_RGBA(NS_GET_R(hslResult), NS_GET_G(hslResult), NS_GET_B(hslResult), aInt);
result = true;
} else {
// sRGB color space mixing with proper alpha premultiplication
float r1 = NS_GET_R(color1);
float g1 = NS_GET_G(color1);
float b1 = NS_GET_B(color1);
float a1 = NS_GET_A(color1) / 255.0f;
float r2 = NS_GET_R(color2);
float g2 = NS_GET_G(color2);
float b2 = NS_GET_B(color2);
float a2 = NS_GET_A(color2) / 255.0f;
float r, g, b, a;
// Check if both colors are opaque - use simple interpolation
if (a1 >= 1.0f && a2 >= 1.0f) {
// Simple linear interpolation for opaque colors
r = r1 * norm1 + r2 * norm2;
g = g1 * norm1 + g2 * norm2;
b = b1 * norm1 + b2 * norm2;
a = 1.0f; // Result is opaque
} else {
// handle alpha premultiplication for RGB components when transparency is involved
float alpha1_weight = norm1 * a1;
float alpha2_weight = norm2 * a2;
float total_alpha_weight = alpha1_weight + alpha2_weight;
if (total_alpha_weight <= 0.0f) {
// both colors are fully transparent
r = g = b = a = 0.0f;
} else {
// normalize alpha-weighted contributions
float norm_alpha1 = alpha1_weight / total_alpha_weight;
float norm_alpha2 = alpha2_weight / total_alpha_weight;
// interpolate RGB components with alpha weighting
r = r1 * norm_alpha1 + r2 * norm_alpha2;
g = g1 * norm_alpha1 + g2 * norm_alpha2;
b = b1 * norm_alpha1 + b2 * norm_alpha2;
// interpolate alpha normally (without premultiplication)
a = a1 * norm1 + a2 * norm2;
}
}
// convert to integers with rounding
uint8_t rInt = (uint8_t)mozilla::clamped(r + 0.5f, 0.0f, 255.0f);
uint8_t gInt = (uint8_t)mozilla::clamped(g + 0.5f, 0.0f, 255.0f);
uint8_t bInt = (uint8_t)mozilla::clamped(b + 0.5f, 0.0f, 255.0f);
uint8_t aInt = (uint8_t)mozilla::clamped(a * 255.0f + 0.5f, 0.0f, 255.0f);
aResult = NS_RGBA(rInt, gInt, bInt, aInt);
result = true;
}
}
}
}
} else if (eCSSUnit_Enumerated == unit &&
aValue.GetIntValue() == NS_STYLE_COLOR_INHERIT_FROM_BODY) {
NS_ASSERTION(aPresContext->CompatibilityMode() == eCompatibility_NavQuirks,

View file

@ -106,6 +106,14 @@ input {
overflow-clip-box: content-box;
}
/* Autofill highlight - placed immediately after base input styles */
input:-moz-autofill-highlight,
select:-moz-autofill-highlight,
textarea:-moz-autofill-highlight {
background-color: #fffcd0 !important;
color: #090909;
}
input > .anonymous-div,
input::placeholder {
word-wrap: normal !important;
@ -1148,4 +1156,4 @@ input[type="number"] > div > div > div:hover {
input[type="date"],
input[type="time"] {
overflow: hidden !important;
}
}

View file

@ -2713,6 +2713,12 @@ pref("layout.css.resizeobserver.enabled", true);
// Is support for cascade layers enabled?
pref("layout.css.cascade-layers.enabled", true);
// Should rules in imported style sheets be added based on the order
// of appearance of their respective @import rules in the parent
// style sheet? Otherwise, they are added before rules preceding
// @import are processed, which is problematic for cascade layers.
pref("layout.css.load-imported-sheets-in-order", true);
// pref for which side vertical scrollbars should be on
// 0 = end-side in UI direction
// 1 = end-side in document/content direction

View file

@ -9,7 +9,7 @@
* See the nsIFormAutofillContentService documentation for details.
*/
"use strict";
"use strict"
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
@ -29,6 +29,15 @@ function FormHandler(aForm, aWindow) {
this.window = aWindow;
this.fieldDetails = [];
// Add a reset event listener to clear autofill state
this.form.addEventListener("reset", () => {
for (let element of this.form.elements) {
if (typeof element.setAutofilled === "function") {
element.setAutofilled(false);
}
}
});
}
FormHandler.prototype = {
@ -233,11 +242,14 @@ FormHandler.prototype = {
f.addressType == field.addressType &&
f.contactType == field.contactType &&
f.fieldName == field.fieldName);
if (!fieldDetail) {
continue;
}
fieldDetail.element.value = field.value;
if (typeof fieldDetail.element.setAutofilled === 'function') {
fieldDetail.element.setAutofilled(!!field.value);
}
}
},

View file

@ -1189,7 +1189,7 @@ var LoginManagerContent = {
// Fill the form
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 userNameDiffers = selectedLogin.username != usernameField.value;
@ -1202,6 +1202,10 @@ var LoginManagerContent = {
if (!disabledOrReadOnly && !userEnteredDifferentCase && userNameDiffers) {
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;
@ -1216,6 +1220,10 @@ var LoginManagerContent = {
};
log("Saving autoFilledLogin", autoFilledLogin.guid, "for", form.rootElement);
this.stateForDocument(doc).fillsByRootElement.set(form.rootElement, autoFilledLogin);
// Set autofilled state if value is present
if (typeof passwordField.setAutofilled === "function" && passwordField.value) {
passwordField.setAutofilled(true);
}
}
log("_fillForm succeeded");

View file

@ -40,6 +40,7 @@
#include "nsIFrame.h"
#include "nsIScriptSecurityManager.h"
#include "nsFocusManager.h"
#include "mozilla/dom/HTMLInputElement.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -541,9 +542,24 @@ nsFormFillController::SetTextValue(const nsAString & aTextValue)
{
nsCOMPtr<nsIDOMNSEditableElement> editable = do_QueryInterface(mFocusedInput);
if (editable) {
editable->BeginProgrammaticValueSet();
mSuppressOnInput = true;
editable->SetUserInput(aTextValue);
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);
nsAutoString value;
htmlInput->GetValue(value);
htmlInput->SetAutofilledValue(value);
}
}
}
}
return NS_OK;
}
@ -1331,9 +1347,6 @@ nsFormFillController::StopControllingInput()
nsresult rv;
nsCOMPtr <nsIFormAutoComplete> formAutoComplete =
do_GetService("@mozilla.org/satchel/form-autocomplete;1", &rv);
if (formAutoComplete) {
formAutoComplete->StopControllingInput(mFocusedInput);
}
mFocusedInputNode = nullptr;
mFocusedInput = nullptr;