Bug 1687682 - Make autofill use a semi-transparent background-image rather than filter. r=mstange,tgiles

With the non-native theme we don't need filter for this to affect
"native" inputs, we can just implement the logic in nsNativeBasicTheme
instead.

A bit unfortunate that we need that special-case, but it seems better
than using filter, which can break websites due to it creating an
stacking context.

I _think_ there are tests that I need to adjust for this change, but if
not I'll write some.

Keep the current behavior behind a pref just in case.

Differential Revision: https://phabricator.services.mozilla.com/D125232
This commit is contained in:
Emilio Cobos Álvarez 2021-09-12 11:16:07 +00:00
Родитель 046786d1c8
Коммит d41c93a17a
11 изменённых файлов: 87 добавлений и 62 удалений

Просмотреть файл

@ -93,13 +93,10 @@ function _getAdaptedProfile(profile) {
return adaptedProfile;
}
// We could not get ManuallyManagedState of element now, so directly check if
// filter and text color style are applied.
async function checkFieldHighlighted(elem, expectedValue) {
let isHighlightApplied;
await SimpleTest.promiseWaitForCondition(function checkHighlight() {
const computedStyle = window.getComputedStyle(elem);
isHighlightApplied = computedStyle.getPropertyValue("filter") !== "none";
isHighlightApplied = elem.matches(":autofill");
return isHighlightApplied === expectedValue;
}, `Checking #${elem.id} highlight style`);

Просмотреть файл

@ -999,6 +999,26 @@ input:autofill {
filter: grayscale(21%) brightness(88%) contrast(161%) invert(10%) sepia(40%) saturate(206%);
}
@supports -moz-bool-pref("layout.css.autofill.background") {
/* We find a few pages where using `filter` causes issues because it
* changes the z-order (see bug 1687682, bug 1727950).
*
* The idea behind using background-image instead of plain background-color,
* is that it's less likely to be overridden by the page.
*
* The color is chosen so that you get the same final color on a white
* background as the filter above (#fffcc8), but with some alpha so as to
* prevent fully illegible text.
*
* NOTE(emilio): Keep the color in sync with kAutofillColor in
* nsNativeBasicTheme!
*/
input:autofill {
filter: none;
background-image: linear-gradient(rgba(255, 249, 145, 0.5), rgba(255, 249, 145, 0.5));
}
}
input:-moz-autofill-preview {
color: GrayText;
}

Просмотреть файл

@ -6742,6 +6742,12 @@
mirror: always
rust: true
# Whether to use background-image to style autofill controls.
- name: layout.css.autofill.background
type: bool
value: true
mirror: always
# Whether the `:-moz-submit-invalid` pseudo-class is exposed to content.
- name: layout.css.moz-submit-invalid.enabled
type: RelaxedAtomicBool

Просмотреть файл

@ -140,9 +140,8 @@ add_task(async function fill_generated_password_empty_field() {
function checkInitialFieldValue(inputSelector) {
const input = content.document.querySelector(inputSelector);
is(input.value.length, 0, "Password field is empty");
is(
content.getComputedStyle(input).filter,
"none",
ok(
!input.matches(":autofill"),
"Password field should not be highlighted"
);
}
@ -165,9 +164,8 @@ add_task(async function fill_generated_password_empty_field() {
LTU.generation.LENGTH,
"Password field was filled with generated password"
);
isnot(
content.getComputedStyle(input).filter,
"none",
ok(
input.matches(":autofill"),
"Password field should be highlighted"
);
LTU.loginField.checkPasswordMasked(input, false, "after fill");
@ -210,9 +208,8 @@ add_task(async function fill_generated_password_nonempty_field() {
[[passwordInputSelector]],
function checkInitialFieldValue(inputSelector) {
const input = content.document.querySelector(inputSelector);
is(
content.getComputedStyle(input).filter,
"none",
ok(
!input.matches(":autofill"),
"Password field should not be highlighted"
);
}
@ -235,9 +232,8 @@ add_task(async function fill_generated_password_nonempty_field() {
LTU.generation.LENGTH,
"Password field was filled with generated password"
);
isnot(
content.getComputedStyle(input).filter,
"none",
ok(
input.matches(":autofill"),
"Password field should be highlighted"
);
LTU.loginField.checkPasswordMasked(input, false, "after fill");
@ -303,9 +299,8 @@ add_task(async function fill_generated_password_with_matching_logins() {
LTU.generation.LENGTH,
"Password field was filled with generated password"
);
isnot(
content.getComputedStyle(input).filter,
"none",
ok(
input.matches(":autofill"),
"Password field should be highlighted"
);
LTU.loginField.checkPasswordMasked(input, false, "after fill");
@ -396,9 +391,8 @@ add_task(async function test_edited_generated_password_in_new_tab() {
function checkInitialFieldValue(inputSelector) {
const input = content.document.querySelector(inputSelector);
is(input.value.length, 0, "Password field is empty");
is(
content.getComputedStyle(input).filter,
"none",
ok(
!input.matches(":autofill"),
"Password field should not be highlighted"
);
}
@ -421,9 +415,8 @@ add_task(async function test_edited_generated_password_in_new_tab() {
LTU.generation.LENGTH,
"Password field was filled with generated password"
);
isnot(
content.getComputedStyle(input).filter,
"none",
ok(
input.matches(":autofill"),
"Password field should be highlighted"
);
LTU.loginField.checkPasswordMasked(input, false, "after fill");

Просмотреть файл

@ -49,11 +49,11 @@ add_task(async function test_field_highlight_on_autocomplete() {
await synthesizeKey("KEY_Enter");
await ContentTaskUtils.waitForCondition(() => {
return document.defaultView.getComputedStyle(username).getPropertyValue("filter") !== "none";
return username.matches(":autofill")
}, "Highlight was successfully applied to the username field on username autocomplete");
isnot(document.defaultView.getComputedStyle(password).getPropertyValue("filter"), "none",
"Highlight was successfully applied to the password field on username autocomplete");
ok(password.matches(":autofill"),
"Highlight was successfully applied to the password field on username autocomplete");
// Clear existing highlight on login fields. We check by pressing the tab key after backspace
// (by shifting focus to the next element) because the tab key was known to cause a bug where the
@ -61,12 +61,12 @@ add_task(async function test_field_highlight_on_autocomplete() {
username.focus();
synthesizeKey("KEY_Backspace");
synthesizeKey("KEY_Tab");
is(document.defaultView.getComputedStyle(username).getPropertyValue("filter"), "none",
ok(!username.matches(":autofill"),
"Highlight was successfully removed on the username field");
synthesizeKey("KEY_Backspace");
synthesizeKey("KEY_Tab");
is(document.defaultView.getComputedStyle(password).getPropertyValue("filter"), "none",
ok(!password.matches(":autofill"),
"Highlight was successfully removed on the password field");
// Clear login fields.
@ -82,7 +82,7 @@ add_task(async function test_field_highlight_on_autocomplete() {
synthesizeKey("KEY_Enter");
await ContentTaskUtils.waitForCondition(() => {
return document.defaultView.getComputedStyle(password).getPropertyValue("filter") !== "none";
return password.matches(":autofill");
}, "Highlight was successfully applied to the password field on password autocomplete");
// Clear existing highlight on the password field. We check by pressing the tab key after backspace
@ -91,7 +91,7 @@ add_task(async function test_field_highlight_on_autocomplete() {
synthesizeKey("KEY_Backspace");
synthesizeKey("KEY_Tab");
is(document.defaultView.getComputedStyle(password).getPropertyValue("filter"), "none",
ok(!password.matches(":autofill"),
"Highlight was successfully removed on the password field");
// Clear login fields.

Просмотреть файл

@ -47,7 +47,7 @@ add_task(async function test_username_field_in_username_only_form_highlight_on_a
await synthesizeKey("KEY_Enter");
await ContentTaskUtils.waitForCondition(() => {
return document.defaultView.getComputedStyle(username).getPropertyValue("filter") !== "none";
return username.matches(":autofill");
}, "Highlight was successfully applied to the username field on username autocomplete");
// Clear existing highlight on login fields. We check by pressing the tab key after backspace
@ -56,7 +56,7 @@ add_task(async function test_username_field_in_username_only_form_highlight_on_a
username.focus();
synthesizeKey("KEY_Backspace");
synthesizeKey("KEY_Tab");
is(document.defaultView.getComputedStyle(username).getPropertyValue("filter"), "none",
ok(!username.matches(":autofill"),
"Highlight was successfully removed on the username field");
// Clear login fields.

Просмотреть файл

@ -167,13 +167,13 @@ async function testConfirmPasswordFieldFilledWithGeneratedPassword({
// check field filling & highlights
if (expectedFilled.includes(input.name)) {
await ContentTaskUtils.waitForCondition(() => {
return document.defaultView.getComputedStyle(input).getPropertyValue("filter") !== "none";
return input.matches(":autofill");
}, `Highlight was successfully applied to the (${input.name}) field`);
is(input.value, generatedPW, `Field (${input.name}) has the generated password value`);
} else {
await ContentTaskUtils.waitForCondition(() => {
return document.defaultView.getComputedStyle(input).getPropertyValue("filter") == "none";
return !input.matches(":autofill");
}, `Highlight was not applied to field (${input.name})`);
let expectedValue = (input.name in expectedNonDefaultValues) ? expectedNonDefaultValues[input.name] : input.defaultValue;
@ -222,7 +222,7 @@ async function testConfirmPasswordFieldFilledWithGeneratedPassword({
synthesizeKey("KEY_Backspace");
await TestUtils.waitForTick();
await ContentTaskUtils.waitForCondition(() => {
return document.defaultView.getComputedStyle(pword2).getPropertyValue("filter") == "none";
return !pword2.matches(":autofill");
}, "Highlight was successfully cleared from the confirm-password field");
// if it got originally masked (i.e. was a password field) verify the focused confirm field now masks

Просмотреть файл

@ -35,10 +35,10 @@ add_task(async function test_field_highlight_on_autofill() {
let doc = this.content.document;
let username = doc.getElementById("uname");
let password = doc.getElementById("pword");
isnot(doc.defaultView.getComputedStyle(username).getPropertyValue("filter"), "none",
"Highlight was successfully applied to the username field on page load autofill");
isnot(doc.defaultView.getComputedStyle(password).getPropertyValue("filter"), "none",
"Highlight was successfully applied to the password field on page load autofill");
ok(username.matches(":autofill"),
"Highlight was successfully applied to the username field on page load autofill");
ok(password.matches(":autofill"),
"Highlight was successfully applied to the password field on page load autofill");
// Test that initiating a change on the input value will remove the highlight. We check by pressing
// the tab key after backspace(by shifting focus to the next element) because the tab key is known to
@ -47,13 +47,11 @@ add_task(async function test_field_highlight_on_autofill() {
await EventUtils.synthesizeKey("KEY_Backspace", {}, this.content);
await EventUtils.synthesizeKey("KEY_Tab", {}, this.content);
let computedStyle = doc.defaultView.getComputedStyle(username);
is(computedStyle.getPropertyValue("filter"), "none", "Highlight was successfully removed on change in value of username input element");
ok(!username.matches(":autofill"), "Highlight was successfully removed on change in value of username input element");
await EventUtils.synthesizeKey("KEY_Backspace", {}, this.content);
await EventUtils.synthesizeKey("KEY_Tab", {}, this.content);
computedStyle = doc.defaultView.getComputedStyle(password);
is(computedStyle.getPropertyValue("filter"), "none", "Highlight was successfully removed on change in value of password input element");
ok(!password.matches(":autofill"), "Highlight was successfully removed on change in value of password input element");
});
});
</script>

Просмотреть файл

@ -35,9 +35,9 @@ add_task(async function test_field_highlight_on_autofill() {
let doc = this.content.document;
let username = doc.getElementById("uname");
let password = doc.getElementById("pword");
is(doc.defaultView.getComputedStyle(username).getPropertyValue("filter"), "none",
ok(!username.matches(":autofill"),
"Highlight was not applied to the username field on page load autofill");
isnot(doc.defaultView.getComputedStyle(password).getPropertyValue("filter"), "none",
ok(password.matches(":autofill"),
"Highlight was successfully applied to the password field on page load autofill");
// Test that initiating a change on the input value will remove the highlight. We check by pressing
@ -47,13 +47,11 @@ add_task(async function test_field_highlight_on_autofill() {
await EventUtils.synthesizeKey("U", {}, this.content);
await EventUtils.synthesizeKey("KEY_Tab", {}, this.content);
let computedStyle = doc.defaultView.getComputedStyle(username);
is(computedStyle.getPropertyValue("filter"), "none", "Highlight is still not present on username element");
ok(!username.matches(":autofill"), "Highlight is still not present on username element");
await EventUtils.synthesizeKey("KEY_Backspace", {}, this.content);
await EventUtils.synthesizeKey("KEY_Tab", {}, this.content);
computedStyle = doc.defaultView.getComputedStyle(password);
is(computedStyle.getPropertyValue("filter"), "none", "Highlight was successfully removed on change in value of password input element");
ok(!password.matches(":autofill"), "Highlight was successfully removed on change in value of password input element");
});
});
</script>

Просмотреть файл

@ -33,7 +33,7 @@ add_task(async function test_field_highlight_on_autofill() {
let EventUtils = ContentTaskUtils.getEventUtils(this.content);
let doc = this.content.document;
let username = doc.getElementById("uname");
isnot(doc.defaultView.getComputedStyle(username).getPropertyValue("filter"), "none",
ok(username.matches(":autofill"),
"Highlight was successfully applied to the username field on page load autofill");
// Test that initiating a change on the input value will remove the highlight. We check by pressing
@ -43,8 +43,7 @@ add_task(async function test_field_highlight_on_autofill() {
await EventUtils.synthesizeKey("KEY_Backspace", {}, this.content);
await EventUtils.synthesizeKey("KEY_Tab", {}, this.content);
let computedStyle = doc.defaultView.getComputedStyle(username);
is(computedStyle.getPropertyValue("filter"), "none", "Highlight was successfully removed on change in value of username input element");
ok(!username.matches(":autofill"), "Highlight was successfully removed on change in value of username input element");
});
});
</script>

Просмотреть файл

@ -317,11 +317,15 @@ static bool IsScrollbarWidthThin(nsIFrame* aFrame) {
return scrollbarWidth == StyleScrollbarWidth::Thin;
}
static sRGBColor SystemColor(StyleSystemColor aColor) {
static nscolor SystemNsColor(StyleSystemColor aColor) {
// TODO(emilio): We could not hardcode light appearance here with a bit of
// work, but doesn't matter for now.
return sRGBColor::FromABGR(LookAndFeel::Color(
aColor, LookAndFeel::ColorScheme::Light, LookAndFeel::UseStandins::No));
return LookAndFeel::Color(aColor, LookAndFeel::ColorScheme::Light,
LookAndFeel::UseStandins::No);
}
static sRGBColor SystemColor(StyleSystemColor aColor) {
return sRGBColor::FromABGR(SystemNsColor(aColor));
}
template <typename Compute>
@ -526,19 +530,29 @@ std::pair<sRGBColor, sRGBColor> nsNativeBasicTheme::ComputeButtonColors(
return std::make_pair(backgroundColor, borderColor);
}
// NOTE: This should be kept in sync with forms.css, see the comment in the
// input:autofill rule.
constexpr nscolor kAutofillColor = NS_RGBA(255, 249, 145, 128);
std::pair<sRGBColor, sRGBColor> nsNativeBasicTheme::ComputeTextfieldColors(
const EventStates& aState, UseSystemColors aUseSystemColors) {
const sRGBColor backgroundColor = [&] {
nscolor backgroundColor = [&] {
if (bool(aUseSystemColors)) {
return SystemColor(StyleSystemColor::TextBackground);
return SystemNsColor(StyleSystemColor::Field);
}
if (aState.HasState(NS_EVENT_STATE_DISABLED)) {
return sColorWhiteAlpha50;
return NS_RGBA(0xff, 0xff, 0xff, 128);
}
return sColorWhite;
return NS_RGB(0xff, 0xff, 0xff);
}();
if (aState.HasState(NS_EVENT_STATE_AUTOFILL) &&
StaticPrefs::layout_css_autofill_background()) {
backgroundColor = NS_ComposeColors(backgroundColor, kAutofillColor);
}
const sRGBColor borderColor = ComputeBorderColor(aState, aUseSystemColors);
return std::make_pair(backgroundColor, borderColor);
return std::make_pair(sRGBColor::FromABGR(backgroundColor), borderColor);
}
std::pair<sRGBColor, sRGBColor> nsNativeBasicTheme::ComputeRangeProgressColors(