Bug 1736914 - Implement prefers-reduced-transparency media query r=geckoview-reviewers,emilio,jonalmeida,ohall

Implemented the prefers-reduced-transparency media query for all
platforms.

Windows and Mac have specific settings which are used, others (Android
and Linux/GTK) have it enabled if prefers-reduced-motion is also enabled
as there is no dedicated setting to check.

Locked behind new pref `layout.css.prefers-reduced-transparency.enabled`,
off by default always for now.

Also added new WPT tests (none previously).

Demo video: https://goose.icu/firefox_prt.mp4
Test page: https://goose.icu/prefers-reduced-transparency

Differential Revision: https://phabricator.services.mozilla.com/D172424
This commit is contained in:
CanadaHonk 2023-03-13 21:30:17 +00:00
parent 23b25a9b51
commit 3bbca01da2
17 changed files with 155 additions and 5 deletions

View file

@ -630,6 +630,8 @@ void Gecko_MediaFeatures_GetDeviceSize(const mozilla::dom::Document*,
float Gecko_MediaFeatures_GetResolution(const mozilla::dom::Document*);
bool Gecko_MediaFeatures_PrefersReducedMotion(const mozilla::dom::Document*);
bool Gecko_MediaFeatures_PrefersReducedTransparency(
const mozilla::dom::Document*);
mozilla::StylePrefersContrast Gecko_MediaFeatures_PrefersContrast(
const mozilla::dom::Document*);
mozilla::StylePrefersColorScheme Gecko_MediaFeatures_PrefersColorScheme(

View file

@ -283,6 +283,14 @@ bool Gecko_MediaFeatures_PrefersReducedMotion(const Document* aDocument) {
return LookAndFeel::GetInt(LookAndFeel::IntID::PrefersReducedMotion, 0) == 1;
}
bool Gecko_MediaFeatures_PrefersReducedTransparency(const Document* aDocument) {
if (aDocument->ShouldResistFingerprinting()) {
return false;
}
return LookAndFeel::GetInt(LookAndFeel::IntID::PrefersReducedTransparency,
0) == 1;
}
StylePrefersColorScheme Gecko_MediaFeatures_PrefersColorScheme(
const Document* aDocument, bool aUseContent) {
auto scheme = aUseContent ? LookAndFeel::PreferredColorSchemeForContent()

View file

@ -3481,7 +3481,7 @@
mirror: never
#endif
# Use to control to dump CheckedUnsafePtr creation stack and assignment stacks.
# Use to control to dump CheckedUnsafePtr creation stack and assignment stacks.
- name: dom.checkedUnsafePtr.dumpStacks.enabled
type: RelaxedAtomicBool
value: false
@ -8770,6 +8770,13 @@
mirror: always
rust: true
# Dictates whether or not the prefers-reduced-transparency media query is enabled.
- name: layout.css.prefers-reduced-transparency.enabled
type: RelaxedAtomicBool
value: false
mirror: always
rust: true
# Is support for forced-color-adjust properties enabled?
- name: layout.css.forced-color-adjust.enabled
type: RelaxedAtomicBool

View file

@ -188,6 +188,13 @@ enum PrefersReducedMotion {
Reduce,
}
#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
#[repr(u8)]
enum PrefersReducedTransparency {
NoPreference,
Reduce,
}
/// Values for the prefers-color-scheme media feature.
#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)]
#[repr(u8)]
@ -227,6 +234,24 @@ fn eval_prefers_reduced_motion(
}
}
/// https://drafts.csswg.org/mediaqueries-5/#prefers-reduced-transparency
fn eval_prefers_reduced_transparency(
context: &Context,
query_value: Option<PrefersReducedTransparency>,
) -> bool {
let prefers_reduced =
unsafe { bindings::Gecko_MediaFeatures_PrefersReducedTransparency(context.device().document()) };
let query_value = match query_value {
Some(v) => v,
None => return prefers_reduced,
};
match query_value {
PrefersReducedTransparency::NoPreference => !prefers_reduced,
PrefersReducedTransparency::Reduce => prefers_reduced,
}
}
/// Possible values for prefers-contrast media query.
/// https://drafts.csswg.org/mediaqueries-5/#prefers-contrast
#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)]
@ -618,7 +643,7 @@ macro_rules! bool_pref_feature {
/// to support new types in these entries and (2) ensuring that either
/// nsPresContext::MediaFeatureValuesChanged is called when the value that
/// would be returned by the evaluator function could change.
pub static MEDIA_FEATURES: [QueryFeatureDescription; 63] = [
pub static MEDIA_FEATURES: [QueryFeatureDescription; 64] = [
feature!(
atom!("width"),
AllowsRanges::Yes,
@ -742,6 +767,12 @@ pub static MEDIA_FEATURES: [QueryFeatureDescription; 63] = [
keyword_evaluator!(eval_prefers_reduced_motion, PrefersReducedMotion),
FeatureFlags::empty(),
),
feature!(
atom!("prefers-reduced-transparency"),
AllowsRanges::No,
keyword_evaluator!(eval_prefers_reduced_transparency, PrefersReducedTransparency),
FeatureFlags::empty(),
),
feature!(
atom!("prefers-contrast"),
AllowsRanges::No,

View file

@ -322,6 +322,13 @@ fn disabled_by_pref(feature: &Atom, context: &ParserContext) -> bool {
return !context.in_ua_or_chrome_sheet() &&
!static_prefs::pref!("layout.css.prefers-contrast.enabled");
}
// prefers-reduced-transparency is always enabled in the ua and chrome. On
// the web it is hidden behind a preference (see Bug 1822176).
if *feature == atom!("prefers-reduced-transparency") {
return !context.in_ua_or_chrome_sheet() &&
!static_prefs::pref!("layout.css.prefers-reduced-transparency.enabled");
}
}
false
}

View file

@ -0,0 +1,2 @@
[prefers-reduced-transparency.html]
prefs: [layout.css.prefers-reduced-transparency.enabled:true]

View file

@ -0,0 +1,30 @@
<!DOCTYPE html>
<link rel="help" href="https://drafts.csswg.org/mediaqueries-5/#prefers-reduced-transparency" />
<script type="text/javascript" src="/resources/testharness.js"></script>
<script type="text/javascript" src="/resources/testharnessreport.js"></script>
<script type="text/javascript" src="resources/matchmedia-utils.js"></script>
<script>
query_should_be_known("(prefers-reduced-transparency)");
query_should_be_known("(prefers-reduced-transparency: no-preference)");
query_should_be_known("(prefers-reduced-transparency: reduce)");
query_should_be_unknown("(prefers-reduced-transparency: 0)");
query_should_be_unknown("(prefers-reduced-transparency: none)");
query_should_be_unknown("(prefers-reduced-transparency: 10px)");
query_should_be_unknown("(prefers-reduced-transparency: no-preference reduce)");
query_should_be_unknown("(prefers-reduced-transparency: reduced)");
query_should_be_unknown("(prefers-reduced-transparency: no-preference/reduce)");
test(() => {
// https://drafts.csswg.org/mediaqueries-5/#boolean-context
let booleanContext = window.matchMedia("(prefers-reduced-transparency)");
let noPreference = window.matchMedia("(prefers-reduced-transparency: no-preference)");
assert_equals(booleanContext.matches, !noPreference.matches);
}, "Check that no-preference evaluates to false in the boolean context");
test(() => {
let invalid = window.matchMedia("(prefers-reduced-transparency: 10px)");
assert_equals(invalid.matches, false);
}, "Check that invalid evaluates to false");
</script>

View file

@ -292,8 +292,16 @@ class LookAndFeel {
* 0: no-preference
* 1: reduce
*/
PrefersReducedMotion,
/**
* Corresponding to prefers-reduced-transparency.
* https://drafts.csswg.org/mediaqueries-5/#prefers-reduced-transparency
* 0: no-preference
* 1: reduce
*/
PrefersReducedTransparency,
/**
* Corresponding to PointerCapabilities in ServoTypes.h
* 0: None

View file

@ -346,6 +346,10 @@ nsresult nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) {
aResult = java::GeckoSystemStateListener::PrefersReducedMotion();
break;
case IntID::PrefersReducedTransparency:
aResult = 0;
break;
case IntID::PrimaryPointerCapabilities:
aResult = java::GeckoAppShell::GetAllPointerCapabilities();

View file

@ -485,6 +485,9 @@ nsresult nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) {
case IntID::PrefersReducedMotion:
aResult = NSWorkspace.sharedWorkspace.accessibilityDisplayShouldReduceMotion;
break;
case IntID::PrefersReducedTransparency:
aResult = NSWorkspace.sharedWorkspace.accessibilityDisplayShouldReduceTransparency;
break;
case IntID::UseAccessibilityTheme:
aResult = NSWorkspace.sharedWorkspace.accessibilityDisplayShouldIncreaseContrast;
break;

View file

@ -945,11 +945,13 @@ nsresult nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) {
case IntID::GTKCSDCloseButtonPosition:
aResult = mCSDCloseButtonPosition;
break;
case IntID::UseAccessibilityTheme: {
case IntID::UseAccessibilityTheme:
// If high contrast is enabled, enable prefers-reduced-transparency media
// query as well as there is no dedicated option.
case IntID::PrefersReducedTransparency:
EnsureInit();
aResult = mSystemTheme.mHighContrast;
break;
}
case IntID::TitlebarRadius: {
EnsureInit();
aResult = EffectiveTheme().mTitlebarRadius;

View file

@ -165,6 +165,7 @@ nsresult HeadlessLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) {
aResult = 0;
break;
case IntID::PrefersReducedMotion:
case IntID::PrefersReducedTransparency:
aResult = 0;
break;
case IntID::PrimaryPointerCapabilities:

View file

@ -181,6 +181,7 @@ static const char sIntPrefs[][45] = {
"ui.GtkCSDReversedPlacement",
"ui.systemUsesDarkTheme",
"ui.prefersReducedMotion",
"ui.prefersReducedTransparency",
"ui.primaryPointerCapabilities",
"ui.allPointerCapabilities",
"ui.systemScrollbarSize",

View file

@ -400,6 +400,20 @@ static IInspectable* GetUISettings() {
uiSettings3->add_ColorValuesChanged(callback.Get(), &unusedToken)));
}
ComPtr<IUISettings4> uiSettings4;
if (SUCCEEDED(uiSettingsAsInspectable.As(&uiSettings4))) {
EventRegistrationToken unusedToken;
auto callback =
Callback<ITypedEventHandler<UISettings*, IInspectable*>>([](auto...) {
// Transparent effects changes change media queries only.
LookAndFeel::NotifyChangedAllWindows(
widget::ThemeChangeKind::MediaQueriesOnly);
return S_OK;
});
(void)NS_WARN_IF(FAILED(uiSettings4->add_AdvancedEffectsEnabledChanged(
callback.Get(), &unusedToken)));
}
sUiSettingsAsInspectable = dont_AddRef(uiSettingsAsInspectable.Detach());
ClearOnShutdown(&sUiSettingsAsInspectable);
}
@ -551,6 +565,28 @@ double WindowsUIUtils::ComputeTextScaleFactor() {
#endif
}
bool WindowsUIUtils::ComputeTransparencyEffects() {
constexpr bool kDefault = true;
#ifndef __MINGW32__
ComPtr<IInspectable> settings = GetUISettings();
if (NS_WARN_IF(!settings)) {
return kDefault;
}
ComPtr<IUISettings4> uiSettings4;
if (NS_WARN_IF(FAILED(settings.As(&uiSettings4)))) {
return kDefault;
}
boolean transparencyEffects = kDefault;
if (NS_WARN_IF(FAILED(
uiSettings4->get_AdvancedEffectsEnabled(&transparencyEffects)))) {
return kDefault;
}
return transparencyEffects;
#else
return kDefault;
#endif
}
void WindowsUIUtils::UpdateInTabletMode() {
#ifndef __MINGW32__
if (!IsWin10OrLater()) {

View file

@ -41,6 +41,7 @@ class WindowsUIUtils final : public nsIWindowsUIUtils {
// Use LookAndFeel for a cached getter.
static bool ComputeOverlayScrollbars();
static double ComputeTextScaleFactor();
static bool ComputeTransparencyEffects();
protected:
~WindowsUIUtils();

View file

@ -608,6 +608,12 @@ nsresult nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) {
aResult = !enable;
break;
}
case IntID::PrefersReducedTransparency: {
// Prefers reduced transparency if the option for "Transparency Effects"
// is disabled
aResult = !WindowsUIUtils::ComputeTransparencyEffects();
break;
}
case IntID::PrimaryPointerCapabilities: {
aResult = static_cast<int32_t>(
widget::WinUtils::GetPrimaryPointerCapabilities());

View file

@ -1018,6 +1018,7 @@ STATIC_ATOMS = [
Atom("preceding", "preceding"),
Atom("precedingSibling", "preceding-sibling"),
Atom("prefersReducedMotion", "prefers-reduced-motion"),
Atom("prefersReducedTransparency", "prefers-reduced-transparency"),
Atom("prefersColorScheme", "prefers-color-scheme"),
Atom("prefersContrast", "prefers-contrast"),
Atom("prefix", "prefix"),