Reading/Web/Preference Persistence For Anonymous Users/Prototype Summary

From mediawiki.org

Prototype Summary[edit]

Patch: https://gerrit.wikimedia.org/r/c/mediawiki/core/+/932819

Current Status[edit]

The patch aims to add or modify client preference classes to the documentElement. It uses cookies to store key-value pairs representing preference/feature names and their values. The patch also provides an API, mw.user.clientPrefs, with set and get methods to manage preferences.

Technical Design[edit]

The patch modifies the ResourceLoader/ClientHtml.php and resources/src/mediawiki.user.js files. It introduces a script in ClientHtml.php that extracts client preferences from cookies and applies them to the HTML document. The mediawiki.user.js file contains the implementation of the mw.user.clientPrefs API, allowing clients to set and get preferences.

Prototype Findings:[edit]

Performance[edit]

The patch utilises cookies for storing client preferences, as it is easier to monitor on production systems. The choice of cookies should be validated further to ensure that it does not impact performance negatively. Future steps involve devising a monitoring and measuring mechanism for localStorage so that we can make informed decisions about potential future modifications.

Safety and Security[edit]

The patch uses existing classes and ensures that new preference/features' classes are added in advance before deployment. The mw.user.clientPrefs API guards against unexpected side effects, and tests are defined to ensure proper behavior, including handling cases with multiple tabs open.

To manage client preferences in a controlled and secure manner, the patch introduces a value suffix of "-clientpref-" for any class managed by the mw.user.clientPrefs API. This suffix serves two primary purposes: aiding discoverability and acting as an allow list for which classes can be modified by the API. All client preference classes must adhere to this naming convention to be recognized and managed by the API.

Matching of Class Names: The script in mediawiki.user.js uses regular expressions to match and update the classes on the HTML document based on the client preferences stored in the cookie. The following pattern is used to find and replace class names:

Regex Pattern: /(^| )([^ ]+)-clientpref-[a-zA-Z0-9]+( |$)/g

Explanation:

  • (^| ): Matches the start of the string or a space, ensuring that the class name is not part of a longer word.
  • ([^ ]+): Captures the preference/feature name (e.g., "dark-mode", "font-size") in a group, excluding spaces.
  • -clientpref-: Matches the literal string "-clientpref-" that follows the preference/feature name.
  • [a-zA-Z0-9]+: Matches one or more alphanumeric characters, representing the value suffix (e.g., "enabled", "10").
  • ( |$): Matches a space or the end of the string, ensuring that the class name is not part of a longer word.

Replacement: The script utilizes a replace function to update the class names with the corresponding client preference values. When a match is found, it replaces the class name with the new name that includes the updated value suffix from the client preferences stored in the cookie.

By enforcing the "-clientpref-" suffix and using precise matching patterns, the API ensures that only recognized and allowed client preference classes are managed and modified, mitigating potential security risks associated with unauthorized changes to HTML classes.

Storage[edit]

Client preferences are stored as key-value pairs in a cookie named mwclientpreferences. The value corresponds to a suffix on the class, making it easy to manage and update the preferences in the HTML document. The cookie string is formatted with '!' as the delimiter between each key-value pair, and '~' as the delimiter between the key and its corresponding value. For example:

Cookie String Format: "key1~value1!key2~value2!key3~value3"

The cookie string stores the client preferences in the following format:

  • "key1": Represents the name of the preference/feature (e.g., "dark-mode", "font-size").
  • "value1": Represents the value of the preference/feature (e.g., "enabled", "10").

Example of a Cookie String: Suppose we have two client preferences set: "dark-mode" with the value "enabled" and "font-size" with the value "10". The corresponding cookie string will be:

Cookie String: "dark-mode~enabled!font-size~10"

When retrieving client preferences from the cookie, the script in mediawiki.user.js parses this cookie string to obtain the preference/feature names and their corresponding values, which are then applied to the HTML document by updating the classes accordingly.

The use of '!' and '~' as delimiters ensures that the cookie string is easy to read and parse, making it a convenient format for storing and managing client preferences.

Notable Changes in Code:[edit]

  • Example of Setting Client Preferences using the mw.user.clientPrefs.set API:
mw.user.clientPrefs.set('dark-mode', 'enabled');
mw.user.clientPrefs.set('font-size', '10');
  • Example of Getting Client Preferences using the mw.user.clientPrefs.get API
var fontSizeValue = mw.user.clientPrefs.get('font-size');
console.log('Font size value:', fontSizeValue); // Output: "10"

Notable Changes from Patch Notes:[edit]

  • The patch switched to using cookies to store client preferences.
  • The mw.user.clientPrefs API is introduced, providing set and get methods.
  • A prefix "-clientpref-" is required on any class managed by the API to aid discoverability and act as an allow list for which classes can be modified by the API.
  • Backwards Compatibility for Limited Width preference/feature (Patch Note): The patch is considered a breaking change for Vector 2022's existing limited width preference/feature. To ensure backwards compatibility, this patch should be merged around the same time as the Vector 2022 patch. Backwards compatibility will be handled in the skin.