Skip to content

Commit

Permalink
[mediaqueries-5] Migrate Web Preferences API proposal
Browse files Browse the repository at this point in the history
  • Loading branch information
lukewarlow authored and svgeesus committed Sep 17, 2024
1 parent c43eb2a commit 64db295
Showing 1 changed file with 309 additions and 0 deletions.
309 changes: 309 additions & 0 deletions mediaqueries-5/Overview.bs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,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 @@ -3217,6 +3218,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
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 @@ -3404,6 +3705,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

0 comments on commit 64db295

Please sign in to comment.