Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[mediaqueries-5] Migrate Web Preferences API proposal #10597

Merged
merged 1 commit into from
Sep 17, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
309 changes: 309 additions & 0 deletions mediaqueries-5/Overview.bs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ spec:css-values-4;
text:<number>
text:<resolution>
type:dfn; text:relative length
spec:dom; type:dfn; text:origin

</pre>

Expand Down Expand Up @@ -3216,6 +3217,306 @@ Automatic handling of User Preferences</h3>
allows for unlimited data or is on a metered plan.
</div>

<h2 id=auto-pref>
Script Control of User Preferences</h3>

It is common for website authors to want to respect the user's system preferences while also allowing
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably needs a bit of a better introduction here. Perhaps with more from https://wicg.github.io/web-preferences-api/#sec-intro

those preferences to be overridden. To help with this, this specification defines a way for authors to
override the [[#mf-user-preferences]] using the {{PreferenceManager}} interface.

This override allows the preference to integrate with various platform features that are affected by these preferences.

<h3 id=auto-pref>
Extensions to the {{Navigator}} interface</h3>

<script type=idl>
[Exposed=Window, SecureContext]
partial interface Navigator {
[SameObject] readonly attribute PreferenceManager preferences;
};
</script>

<h4 id=auto-pref>
{{preferences}} attribute</h4>

When getting the {{preferences}} attribute always return the same instance of the {{PreferenceManager}} object.

### {{PreferenceManager}} interface ### {#preference-manager}

<script type=idl>
[Exposed=Window, SecureContext]
interface PreferenceManager {
readonly attribute PreferenceObject colorScheme;
readonly attribute PreferenceObject contrast;
readonly attribute PreferenceObject reducedMotion;
readonly attribute PreferenceObject reducedTransparency;
readonly attribute PreferenceObject reducedData;
};
</script>

<h4 id=auto-pref>
{{colorScheme}} attribute</h4>

The {{colorScheme}} attribute is a {{PreferenceObject}} used to override the user's preference for the color scheme of the site.
This is modeled after the [[#prefers-color-scheme]].

<div algorithm='get valid values for colorScheme'>
The <dfn>get valid values for colorScheme</dfn> algorithm, when invoked, must run these steps:

1. Let |validValues| be a new empty [=sequence=].
1. Add ''light'' to |validValues|.
1. Add ''dark'' to |validValues|.
1. Return |validValues|.
</div>

If an override is set for this preference:
- The user agent MUST use this override for the [[#prefers-color-scheme]] in all stylesheets applied to an [=origin=] including the UA style sheet.
- The user agent MUST also use this override when queried via `matchMedia()` from [[cssom-view#extensions-to-the-window-interface]].
- The user agent MUST also use this override when calculating the [=used color scheme=].
- The user agent MUST also use this override when sending [[USER-PREFERENCE-MEDIA-FEATURES-HEADERS#sec-ch-prefers-color-scheme]].
- The user agent MUST also use this override for any UA features that are normally affected by [[#prefers-color-scheme]].

<h4 id=auto-pref>
{{contrast}} attribute</h4>

The {{contrast}} attribute is a {{PreferenceObject}} used to override the user's preference for the contrast of the site.
This is modeled after the [[#prefers-contrast]].

<div algorithm='get valid values for contrast'>
The <dfn>get valid values for contrast</dfn> algorithm, when invoked, must run these steps:

1. Let |validValues| be a new empty [=sequence=].
1. Add ''@media/prefers-contrast/more'' to |validValues|.
1. Add ''@media/prefers-contrast/less'' to |validValues|.
1. Add ''@media/prefers-contrast/no-preference'' to |validValues|.
1. Return |validValues|.
</div>

If an override is set for this preference:
- The user agent MUST use this override for the [[#prefers-contrast]] in all stylesheets applied to an [=origin=] including the UA style sheet.
- The user agent MUST also use this override when queried via `matchMedia()` from [[cssom-view#extensions-to-the-window-interface]].
- The user agent MUST also use this override when sending [[USER-PREFERENCE-MEDIA-FEATURES-HEADERS#sec-ch-prefers-contrast]].
- The user agent MUST also use this override for any UA features that are normally affected by [[#prefers-contrast]].

Note: Unlike the media feature this preference is NOT able to be set to ''@media/prefers-contrast/custom'' as this is tightly coupled to the [[#forced-colors]].

<h4 id=auto-pref>
{{reducedMotion}} attribute</h4>

The {{reducedMotion}} attribute is a {{PreferenceObject}} used to override the user's preference for reduced motion on the site.
This is modeled after the [[#prefers-reduced-motion]].

<div algorithm='get valid values for reducedMotion'>
The <dfn>get valid values for reducedMotion</dfn> algorithm, when invoked, must run these steps:

1. Let |validValues| be a new empty [=sequence=].
1. Add ''@media/prefers-reduced-motion/reduce'' to |validValues|.
1. Add ''@media/prefers-reduced-motion/no-preference'' to |validValues|.
1. Return |validValues|.
</div>

If an override is set for this preference:
- The user agent MUST use this override for the [[#prefers-reduced-motion]] in all stylesheets applied to an [=origin=] including the UA style sheet.
- The user agent MUST also use this override when queried via `matchMedia()` from [[cssom-view#extensions-to-the-window-interface]].
- The user agent MUST also use this override when sending [[USER-PREFERENCE-MEDIA-FEATURES-HEADERS#sec-ch-prefers-reduced-motion]].
- The user agent MUST also use this override for any UA features that are normally affected by [[#prefers-reduced-motion]].

Note: An example of a UA feature that is affected by this preference could be disabling smooth scrolling, or pausing marquee elements.

<h4 id=auto-pref>
{{reducedTransparency}} attribute</h4>

The {{reducedTransparency}} attribute is a {{PreferenceObject}} used to override the user's preference for reduced transparency on the site.
This is modeled after the [[#prefers-reduced-transparency]].

<div algorithm='get valid values for reducedTransparency'>
The <dfn>get valid values for reducedTransparency</dfn> algorithm, when invoked, must run these steps:

1. Let |validValues| be a new empty [=sequence=].
1. Add ''@media/prefers-reduced-transparency/reduce'' to |validValues|.
1. Add ''@media/prefers-reduced-transparency/no-preference'' to |validValues|.
1. Return |validValues|.
</div>

If an override is set for this preference:
- The user agent MUST use this override for the [[#prefers-reduced-transparency]] in all stylesheets applied to an [=origin=] including the UA style sheet.
- The user agent MUST also use this override when queried via `matchMedia()` from [[cssom-view#extensions-to-the-window-interface]].
- The user agent MUST also use this override when sending [[USER-PREFERENCE-MEDIA-FEATURES-HEADERS#sec-ch-prefers-reduced-transparency]].
- The user agent MUST also use this override for any UA features that are normally affected by [[#prefers-reduced-transparency]].

<h4 id=auto-pref>
{{reducedData}} attribute</h4>

The {{reducedData}} attribute is a {{PreferenceObject}} used to override the user's preference for reduced data usage on the site.
This is modeled after the [[#prefers-reduced-data]].

<div algorithm='get valid values for reducedData'>
The <dfn>get valid values for reducedData</dfn> algorithm, when invoked, must run these steps:

1. Let |validValues| be a new empty [=sequence=].
1. Add ''@media/prefers-reduced-data/reduce'' to |validValues|.
1. Add ''@media/prefers-reduced-data/no-preference'' to |validValues|.
1. Return |validValues|.
</div>

If an override is set for this preference:
- The user agent MUST use this override for the [[#prefers-reduced-data]] in all stylesheets applied to an [=origin=] including the UA style sheet.
- The user agent MUST also use this override when queried via `matchMedia()` from [[cssom-view#extensions-to-the-window-interface]].
- The user agent MUST also use this override when sending [[SAVEDATA#save-data-request-header-field]].
- The user agent MUST also use this override when calculating the [[SAVEDATA#savedata-attribute]].
- The user agent MUST also use this override for any UA features that are normally affected by [[#prefers-reduced-data]].

<h4 id=auto-pref>
{{PreferenceObject}} interface</h4>

<script type=idl>
[Exposed=Window, SecureContext]
interface PreferenceObject : EventTarget {
readonly attribute DOMString? override;
readonly attribute DOMString value;
readonly attribute FrozenArray<DOMString> validValues;

undefined clearOverride();
Promise<undefined> requestOverride(DOMString? value);

attribute EventHandler onchange;
};
</script>

<h5 id=auto-pref>
{{override}} attribute</h5>

<div algorithm='get preference override'>
The <dfn attribute for=PreferenceObject>override</dfn> attribute, when accessed, must run these steps:

1. Let |preference| be the preference object's name.
1. Let |override| be null.
1. If an override for |preference| exists, set |override| to the value of that override.
1. Return |override|.
</div>

<h5 id=auto-pref>
{{PreferenceObject/value}} attribute</h5>

<div algorithm='get preference value'>
The <dfn attribute for=PreferenceObject>value</dfn> attribute, when accessed, must run these steps:

1. Let |preference| be the preference object's name.
1. Let |value| be null.
1. If an override for |preference| exists, set |value| to the value of that override.
1. If |value| is null, set |value| to the UA value of the preference.
1. Return |value|.
</div>

<h5 id=auto-pref>
{{validValues}} attribute</h5>

<div algorithm>
The <dfn attribute for=PreferenceObject>validValues</dfn> attribute, when accessed, must run these steps:

<ol>
<li>Let |preference| be the preference object's name.

<li>Switch on |preference|:
<dl class="switch">
<dt>"{{colorScheme}}"</dt>
<dd>Return the result of [=get valid values for colorScheme=].</dd>
<dt>"{{contrast}}"</dt>
<dd>Return the result of [=get valid values for contrast=].</dd>
<dt>"{{reducedMotion}}"</dt>
<dd>Return the result of [=get valid values for reducedMotion=].</dd>
<dt>"{{reducedTransparency}}"</dt>
<dd>Return the result of [=get valid values for reducedTransparency=].</dd>
<dt>"{{reducedData}}"</dt>
<dd>Return the result of [=get valid values for reducedData=].</dd>
</dl>
</div>

<h5 id=auto-pref>
{{onchange}} event handler attribute</h5>

The <dfn attribute for=PreferenceObject>onchange</dfn> attribute is an [=event handler IDL attribute=] for
the {{onchange}} [=event handler=], whose [=event handler event type=]
is <dfn class="event" data-dfn-for="PreferenceObject">change</dfn>.

<div algorithm="update steps">
Whenever the [=user agent=] is aware that the state of a {{PreferenceObject}}
instance |value| has changed, it runs the <dfn algorithm for="PreferenceObject">{{PreferenceObject}}
update steps</dfn>:

1. Let |preference| be the {{PreferenceObject}} object that |value| is associated with.
1. If [=this=]'s [=relevant global object=] is a {{Window}} object, then:
1. Let |document| be |preference|'s [=relevant global object=]'s [=associated Document=].
1. If |document| is null or |document| is not [=Document/fully active=], terminate this algorithm.
1. <a>Fire an event</a> named <code>change</code> at |preference|.

<h5 id=auto-pref>
{{requestOverride()}} method</h5>

<div algorithm='request preference override'>
The <dfn method for=PreferenceObject>requestOverride(value)</dfn> method, when invoked, must run these steps:

1. Let |result| be [=a new promise=].
1. Let |allowed| be false.
1. Set |allowed| to the result of executing a UA defined algorithm for deciding whether the request is allowed.
1. If |allowed| is false, return [=a promise rejected with=] a "{{NotAllowedError}}" {{DOMException}}.
1. Let |value| be the method's argument.
1. Let |result| be [=a new promise=].
1. If |value| is null or the empty string:
1. Run {{clearOverride}}.
1. [=Resolve=] and return |result|.
1. Let |currentValue| be the preference object's |value|.
1. Let |validValues| be null.
1. Switch on |preference|:
<dl class="switch">
<dt>"{{colorScheme}}"</dt>
<dd>Set |validValues| to the result of [=get valid values for colorScheme=].</dd>
<dt>"{{contrast}}"</dt>
<dd>Set |validValues| to the result of [=get valid values for contrast=].</dd>
<dt>"{{reducedMotion}}"</dt>
<dd>Set |validValues| to the result of [=get valid values for reducedMotion=].</dd>
<dt>"{{reducedTransparency}}"</dt>
<dd>Set |validValues| to the result of [=get valid values for reducedTransparency=].</dd>
<dt>"{{reducedData}}"</dt>
<dd>Set |validValues| to the result of [=get valid values for reducedData=].</dd>
</dl>
1. If |value| is not in |validValues|:
1. [=Reject=] |result| with a "{{TypeError}}" {{DOMException}}.
1. Return |result|.
1. Let |previousOverride| be null.
1. If an override for |preference| exists, set |previousOverride| to the value of that override.
1. If |value| is different from |previousOverride|:
1. Set the preference override for |preference| to |value|.
1. If |previousOverride| is null, then:
1. If |value| is the same as |currentValue|, then:
1. <a>Fire an event</a> named <code>change</code> at [=this=].
1. [=Resolve=] and return |result|.
</div>

Issue: This algorithm needs more detail on what exactly setting the preference override does.

Issue: Is TypeError correct here?

Note: The `change` event is fired when the computed value changes, but when a new override is set it is also fired if the value hasn't changed.

<h5 id=auto-pref>
{{clearOverride()}} method</h5>

<div algorithm='clear preference override'>
The <dfn method for=PreferenceObject>clearOverride()</dfn> method, when invoked, must run these steps:

1. Let |preference| be the preference object's name.
1. Let |override| be null.
1. If an override for |preference| exists, set |override| to the value of that override.
1. If |override| is null, then return.
1. Clear the override for |preference|.
1. Let |newValue| be the preference object's |value|.
1. If |newValue| is equal to |override|, then:
1. <a>Fire an event</a> named <code>change</code> at [=this=].
</div>

Note: The `change` event is fired when the computed value changes, but when an override is cleared it is also fired if the value hasn't changed.

<!--
████ ██████ ██████ ██ ██ ████████ ██████
██ ██ ██ ██ ██ ██ ██ ██ ██ ██
Expand Down Expand Up @@ -3403,6 +3704,14 @@ Appendix B: Privacy and Security Considerations</h2>
may be an undesired source of fingerprinting,
with a bias towards low income with limited data.

The {{PreferenceManager}} object allows querying some user-preference [=media features=]. This
is not a privacy leak, as that information is already trivially
available by using [=media features=] themselves.

The {{PreferenceManager}} object also allows overriding these user-preference [=media features=]; this
is also neither a privacy nor accessibility regression, as the [=media features=] were already ignorable by simply
not querying them.

</div>

<h2 id="changes" class="no-num">
Expand Down