summaryrefslogtreecommitdiffstats
path: root/mobile/android/chrome/geckoview/config.js
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/chrome/geckoview/config.js')
-rw-r--r--mobile/android/chrome/geckoview/config.js719
1 files changed, 719 insertions, 0 deletions
diff --git a/mobile/android/chrome/geckoview/config.js b/mobile/android/chrome/geckoview/config.js
new file mode 100644
index 0000000000..1cf92416ac
--- /dev/null
+++ b/mobile/android/chrome/geckoview/config.js
@@ -0,0 +1,719 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+var Cm = Components.manager;
+
+const VKB_ENTER_KEY = 13; // User press of VKB enter key
+const INITIAL_PAGE_DELAY = 500; // Initial pause on program start for scroll alignment
+const PREFS_BUFFER_MAX = 30; // Max prefs buffer size for getPrefsBuffer()
+const PAGE_SCROLL_TRIGGER = 200; // Triggers additional getPrefsBuffer() on user scroll-to-bottom
+const FILTER_CHANGE_TRIGGER = 200; // Delay between responses to filterInput changes
+const INNERHTML_VALUE_DELAY = 100; // Delay before providing prefs innerHTML value
+
+var gClipboardHelper = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(
+ Ci.nsIClipboardHelper
+);
+
+/* ============================== NewPrefDialog ==============================
+ *
+ * New Preference Dialog Object and methods
+ *
+ * Implements User Interfaces for creation of a single(new) Preference setting
+ *
+ */
+var NewPrefDialog = {
+ _prefsShield: null,
+
+ _newPrefsDialog: null,
+ _newPrefItem: null,
+ _prefNameInputElt: null,
+ _prefTypeSelectElt: null,
+
+ _booleanValue: null,
+ _booleanToggle: null,
+ _stringValue: null,
+ _intValue: null,
+
+ _positiveButton: null,
+
+ get type() {
+ return this._prefTypeSelectElt.value;
+ },
+
+ set type(aType) {
+ this._prefTypeSelectElt.value = aType;
+ switch (this._prefTypeSelectElt.value) {
+ case "boolean":
+ this._prefTypeSelectElt.selectedIndex = 0;
+ break;
+ case "string":
+ this._prefTypeSelectElt.selectedIndex = 1;
+ break;
+ case "int":
+ this._prefTypeSelectElt.selectedIndex = 2;
+ break;
+ }
+
+ this._newPrefItem.setAttribute("typestyle", aType);
+ },
+
+ // Init the NewPrefDialog
+ init: function AC_init() {
+ this._prefsShield = document.getElementById("prefs-shield");
+
+ this._newPrefsDialog = document.getElementById("new-pref-container");
+ this._newPrefItem = document.getElementById("new-pref-item");
+ this._prefNameInputElt = document.getElementById("new-pref-name");
+ this._prefTypeSelectElt = document.getElementById("new-pref-type");
+
+ this._booleanValue = document.getElementById("new-pref-value-boolean");
+ this._stringValue = document.getElementById("new-pref-value-string");
+ this._intValue = document.getElementById("new-pref-value-int");
+
+ this._positiveButton = document.getElementById("positive-button");
+ },
+
+ // Called to update positive button to display text ("Create"/"Change), and enabled/disabled status
+ // As new pref name is initially displayed, re-focused, or modifed during user input
+ _updatePositiveButton: function AC_updatePositiveButton(aPrefName) {
+ document.l10n.setAttributes(
+ this._positiveButton,
+ "config-new-pref-create-button"
+ );
+ this._positiveButton.setAttribute("disabled", true);
+ if (aPrefName == "") {
+ return;
+ }
+
+ // If item already in list, it's being changed, else added
+ const item = AboutConfig._list.filter(i => {
+ return i.name == aPrefName;
+ });
+ if (item.length) {
+ document.l10n.setAttributes(
+ this._positiveButton,
+ "config-new-pref-change-button"
+ );
+ } else {
+ this._positiveButton.removeAttribute("disabled");
+ }
+ },
+
+ // When we want to cancel/hide an existing, or show a new pref dialog
+ toggleShowHide: function AC_toggleShowHide() {
+ if (this._newPrefsDialog.classList.contains("show")) {
+ this.hide();
+ } else {
+ this._show();
+ }
+ },
+
+ // When we want to show the new pref dialog / shield the prefs list
+ _show: function AC_show() {
+ this._newPrefsDialog.classList.add("show");
+ this._prefsShield.setAttribute("shown", true);
+
+ // Initial default field values
+ this._prefNameInputElt.value = "";
+ this._updatePositiveButton(this._prefNameInputElt.value);
+
+ this.type = "boolean";
+ this._booleanValue.value = "false";
+ this._stringValue.value = "";
+ this._intValue.value = "";
+
+ this._prefNameInputElt.focus();
+
+ window.addEventListener("keypress", this.handleKeypress);
+ },
+
+ // When we want to cancel/hide the new pref dialog / un-shield the prefs list
+ hide: function AC_hide() {
+ this._newPrefsDialog.classList.remove("show");
+ this._prefsShield.removeAttribute("shown");
+
+ window.removeEventListener("keypress", this.handleKeypress);
+ },
+
+ // Watch user key input so we can provide Enter key action, commit input values
+ handleKeypress: function AC_handleKeypress(aEvent) {
+ // Close our VKB on new pref enter key press
+ if (aEvent.keyCode == VKB_ENTER_KEY) {
+ aEvent.target.blur();
+ }
+ },
+
+ // New prefs create dialog only allows creating a non-existing preference, doesn't allow for
+ // Changing an existing one on-the-fly, tap existing/displayed line item pref for that
+ create: function AC_create(aEvent) {
+ if (this._positiveButton.getAttribute("disabled") == "true") {
+ return;
+ }
+
+ switch (this.type) {
+ case "boolean":
+ Services.prefs.setBoolPref(
+ this._prefNameInputElt.value,
+ !!(this._booleanValue.value == "true")
+ );
+ break;
+ case "string":
+ Services.prefs.setCharPref(
+ this._prefNameInputElt.value,
+ this._stringValue.value
+ );
+ break;
+ case "int":
+ Services.prefs.setIntPref(
+ this._prefNameInputElt.value,
+ this._intValue.value
+ );
+ break;
+ }
+
+ // Ensure pref adds flushed to disk immediately
+ Services.prefs.savePrefFile(null);
+
+ this.hide();
+ },
+
+ // Display proper positive button text/state on new prefs name input focus
+ focusName: function AC_focusName(aEvent) {
+ this._updatePositiveButton(aEvent.target.value);
+ },
+
+ // Display proper positive button text/state as user changes new prefs name
+ updateName: function AC_updateName(aEvent) {
+ this._updatePositiveButton(aEvent.target.value);
+ },
+
+ // In new prefs dialog, bool prefs are <input type="text">, as they aren't yet tied to an
+ // Actual Services.prefs.*etBoolPref()
+ toggleBoolValue: function AC_toggleBoolValue() {
+ this._booleanValue.value =
+ this._booleanValue.value == "true" ? "false" : "true";
+ },
+};
+
+/* ============================== AboutConfig ==============================
+ *
+ * Main AboutConfig object and methods
+ *
+ * Implements User Interfaces for maintenance of a list of Preference settings
+ *
+ */
+var AboutConfig = {
+ contextMenuLINode: null,
+ filterInput: null,
+ _filterPrevInput: null,
+ _filterChangeTimer: null,
+ _prefsContainer: null,
+ _loadingContainer: null,
+ _list: null,
+
+ // Init the main AboutConfig dialog
+ init: function AC_init() {
+ this.filterInput = document.getElementById("filter-input");
+ this._prefsContainer = document.getElementById("prefs-container");
+ this._loadingContainer = document.getElementById("loading-container");
+
+ const list = Services.prefs.getChildList("");
+ this._list = list.sort().map(function AC_getMapPref(aPref) {
+ return new Pref(aPref);
+ }, this);
+
+ // Support filtering about:config via a ?filter=<string> param
+ const match = /[?&]filter=([^&]+)/i.exec(window.location.href);
+ if (match) {
+ this.filterInput.value = decodeURIComponent(match[1]);
+ }
+
+ // Display the current prefs list (retains searchFilter value)
+ this.bufferFilterInput();
+
+ // Setup the prefs observers
+ Services.prefs.addObserver("", this);
+ },
+
+ // Uninit the main AboutConfig dialog
+ uninit: function AC_uninit() {
+ // Remove the prefs observer
+ Services.prefs.removeObserver("", this);
+ },
+
+ // Clear the filterInput value, to display the entire list
+ clearFilterInput: function AC_clearFilterInput() {
+ this.filterInput.value = "";
+ this.bufferFilterInput();
+ },
+
+ // Buffer down rapid changes in filterInput value from keyboard
+ bufferFilterInput: function AC_bufferFilterInput() {
+ if (this._filterChangeTimer) {
+ clearTimeout(this._filterChangeTimer);
+ }
+
+ this._filterChangeTimer = setTimeout(() => {
+ this._filterChangeTimer = null;
+ // Display updated prefs list when filterInput value settles
+ this._displayNewList();
+ }, FILTER_CHANGE_TRIGGER);
+ },
+
+ // Update displayed list when filterInput value changes
+ _displayNewList: function AC_displayNewList() {
+ // This survives the search filter value past a page refresh
+ this.filterInput.setAttribute("value", this.filterInput.value);
+
+ // Don't start new filter search if same as last
+ if (this.filterInput.value == this._filterPrevInput) {
+ return;
+ }
+ this._filterPrevInput = this.filterInput.value;
+
+ // Clear list item selection / context menu, prefs list, get first buffer, set scrolling on
+ this.selected = "";
+ this._clearPrefsContainer();
+ this._addMorePrefsToContainer();
+ window.onscroll = this.onScroll.bind(this);
+
+ // Pause for screen to settle, then ensure at top
+ setTimeout(() => {
+ window.scrollTo(0, 0);
+ }, INITIAL_PAGE_DELAY);
+ },
+
+ // Clear the displayed preferences list
+ _clearPrefsContainer: function AC_clearPrefsContainer() {
+ // Quick clear the prefsContainer list
+ const empty = this._prefsContainer.cloneNode(false);
+ this._prefsContainer.parentNode.replaceChild(empty, this._prefsContainer);
+ this._prefsContainer = empty;
+
+ // Quick clear the prefs li.HTML list
+ this._list.forEach(function (item) {
+ delete item.li;
+ });
+ },
+
+ // Get a small manageable block of prefs items, and add them to the displayed list
+ _addMorePrefsToContainer: function AC_addMorePrefsToContainer() {
+ // Create filter regex
+ const filterExp = this.filterInput.value
+ ? new RegExp(this.filterInput.value, "i")
+ : null;
+
+ // Get a new block for the display list
+ const prefsBuffer = [];
+ for (
+ let i = 0;
+ i < this._list.length && prefsBuffer.length < PREFS_BUFFER_MAX;
+ i++
+ ) {
+ if (!this._list[i].li && this._list[i].test(filterExp)) {
+ prefsBuffer.push(this._list[i]);
+ }
+ }
+
+ // Add the new block to the displayed list
+ for (let i = 0; i < prefsBuffer.length; i++) {
+ this._prefsContainer.appendChild(prefsBuffer[i].getOrCreateNewLINode());
+ }
+
+ // Determine if anything left to add later by scrolling
+ let anotherPrefsBufferRemains = false;
+ for (let i = 0; i < this._list.length; i++) {
+ if (!this._list[i].li && this._list[i].test(filterExp)) {
+ anotherPrefsBufferRemains = true;
+ break;
+ }
+ }
+
+ if (anotherPrefsBufferRemains) {
+ // If still more could be displayed, show the throbber
+ this._loadingContainer.style.display = "block";
+ } else {
+ // If no more could be displayed, hide the throbber, and stop noticing scroll events
+ this._loadingContainer.style.display = "none";
+ window.onscroll = null;
+ }
+ },
+
+ // If scrolling at the bottom, maybe add some more entries
+ onScroll: function AC_onScroll(aEvent) {
+ if (
+ this._prefsContainer.scrollHeight -
+ (window.pageYOffset + window.innerHeight) <
+ PAGE_SCROLL_TRIGGER
+ ) {
+ if (!this._filterChangeTimer) {
+ this._addMorePrefsToContainer();
+ }
+ }
+ },
+
+ // Return currently selected list item node
+ get selected() {
+ return document.querySelector(".pref-item.selected");
+ },
+
+ // Set list item node as selected
+ set selected(aSelection) {
+ const currentSelection = this.selected;
+ if (aSelection == currentSelection) {
+ return;
+ }
+
+ // Clear any previous selection
+ if (currentSelection) {
+ currentSelection.classList.remove("selected");
+ currentSelection.removeEventListener("keypress", this.handleKeypress);
+ }
+
+ // Set any current selection
+ if (aSelection) {
+ aSelection.classList.add("selected");
+ aSelection.addEventListener("keypress", this.handleKeypress);
+ }
+ },
+
+ // Watch user key input so we can provide Enter key action, commit input values
+ handleKeypress: function AC_handleKeypress(aEvent) {
+ if (aEvent.keyCode == VKB_ENTER_KEY) {
+ aEvent.target.blur();
+ }
+ },
+
+ // Return the target list item node of an action event
+ getLINodeForEvent: function AC_getLINodeForEvent(aEvent) {
+ let node = aEvent.target;
+ while (node && node.nodeName != "li") {
+ node = node.parentNode;
+ }
+
+ return node;
+ },
+
+ // Return a pref of a list item node
+ _getPrefForNode: function AC_getPrefForNode(aNode) {
+ const pref = aNode.getAttribute("name");
+
+ return new Pref(pref);
+ },
+
+ // When list item name or value are tapped
+ selectOrToggleBoolPref: function AC_selectOrToggleBoolPref(aEvent) {
+ const node = this.getLINodeForEvent(aEvent);
+
+ // If not already selected, just do so
+ if (this.selected != node) {
+ this.selected = node;
+ return;
+ }
+
+ // If already selected, and value is boolean, toggle it
+ const pref = this._getPrefForNode(node);
+ if (pref.type != Services.prefs.PREF_BOOL) {
+ return;
+ }
+
+ this.toggleBoolPref(aEvent);
+ },
+
+ // When finalizing list input values due to blur
+ setIntOrStringPref: function AC_setIntOrStringPref(aEvent) {
+ const node = this.getLINodeForEvent(aEvent);
+
+ // Skip if locked
+ const pref = this._getPrefForNode(node);
+ if (pref.locked) {
+ return;
+ }
+
+ // Boolean inputs blur to remove focus from "button"
+ if (pref.type == Services.prefs.PREF_BOOL) {
+ return;
+ }
+
+ // String and Int inputs change / commit on blur
+ pref.value = aEvent.target.value;
+ },
+
+ // When we reset a pref to it's default value (note resetting a user created pref will delete it)
+ resetDefaultPref: function AC_resetDefaultPref(aEvent) {
+ const node = this.getLINodeForEvent(aEvent);
+
+ // If not already selected, do so
+ if (this.selected != node) {
+ this.selected = node;
+ }
+
+ // Reset will handle any locked condition
+ const pref = this._getPrefForNode(node);
+ pref.reset();
+
+ // Ensure pref reset flushed to disk immediately
+ Services.prefs.savePrefFile(null);
+ },
+
+ // When we want to toggle a bool pref
+ toggleBoolPref: function AC_toggleBoolPref(aEvent) {
+ const node = this.getLINodeForEvent(aEvent);
+
+ // Skip if locked, or not boolean
+ const pref = this._getPrefForNode(node);
+ if (pref.locked) {
+ return;
+ }
+
+ // Toggle, and blur to remove field focus
+ pref.value = !pref.value;
+ aEvent.target.blur();
+ },
+
+ // When Int inputs have their Up or Down arrows toggled
+ incrOrDecrIntPref: function AC_incrOrDecrIntPref(aEvent, aInt) {
+ const node = this.getLINodeForEvent(aEvent);
+
+ // Skip if locked
+ const pref = this._getPrefForNode(node);
+ if (pref.locked) {
+ return;
+ }
+
+ pref.value += aInt;
+ },
+
+ // Observe preference changes
+ observe: function AC_observe(aSubject, aTopic, aPrefName) {
+ const pref = new Pref(aPrefName);
+
+ // Ignore uninteresting changes, and avoid "private" preferences
+ if (aTopic != "nsPref:changed") {
+ return;
+ }
+
+ // If pref type invalid, refresh display as user reset/removed an item from the list
+ if (pref.type == Services.prefs.PREF_INVALID) {
+ document.location.reload();
+ return;
+ }
+
+ // If pref onscreen, update in place.
+ const item = document.querySelector(
+ '.pref-item[name="' + CSS.escape(pref.name) + '"]'
+ );
+ if (item) {
+ item.setAttribute("value", pref.value);
+ const input = item.querySelector("input");
+ input.setAttribute("value", pref.value);
+ input.value = pref.value;
+
+ pref.default
+ ? item.querySelector(".reset").setAttribute("disabled", "true")
+ : item.querySelector(".reset").removeAttribute("disabled");
+ return;
+ }
+
+ // If pref not already in list, refresh display as it's being added
+ const anyWhere = this._list.filter(i => {
+ return i.name == pref.name;
+ });
+ if (!anyWhere.length) {
+ document.location.reload();
+ }
+ },
+
+ // Quick context menu helpers for about:config
+ clipboardCopy: function AC_clipboardCopy(aField) {
+ const pref = this._getPrefForNode(this.contextMenuLINode);
+ if (aField == "name") {
+ gClipboardHelper.copyString(pref.name);
+ } else {
+ gClipboardHelper.copyString(pref.value);
+ }
+ },
+};
+
+/* ============================== Pref ==============================
+ *
+ * Individual Preference object / methods
+ *
+ * Defines a Pref object, a document list item tied to Preferences Services
+ * And the methods by which they interact.
+ *
+ */
+function Pref(aName) {
+ this.name = aName;
+}
+
+Pref.prototype = {
+ get type() {
+ return Services.prefs.getPrefType(this.name);
+ },
+
+ get value() {
+ switch (this.type) {
+ case Services.prefs.PREF_BOOL:
+ return Services.prefs.getBoolPref(this.name);
+ case Services.prefs.PREF_INT:
+ return Services.prefs.getIntPref(this.name);
+ case Services.prefs.PREF_STRING:
+ default:
+ return Services.prefs.getCharPref(this.name);
+ }
+ },
+ set value(aPrefValue) {
+ switch (this.type) {
+ case Services.prefs.PREF_BOOL:
+ Services.prefs.setBoolPref(this.name, aPrefValue);
+ break;
+ case Services.prefs.PREF_INT:
+ Services.prefs.setIntPref(this.name, aPrefValue);
+ break;
+ case Services.prefs.PREF_STRING:
+ default:
+ Services.prefs.setCharPref(this.name, aPrefValue);
+ }
+
+ // Ensure pref change flushed to disk immediately
+ Services.prefs.savePrefFile(null);
+ },
+
+ get default() {
+ return !Services.prefs.prefHasUserValue(this.name);
+ },
+
+ get locked() {
+ return Services.prefs.prefIsLocked(this.name);
+ },
+
+ reset: function AC_reset() {
+ Services.prefs.clearUserPref(this.name);
+ },
+
+ test: function AC_test(aValue) {
+ return aValue ? aValue.test(this.name) : true;
+ },
+
+ // Get existing or create new LI node for the pref
+ getOrCreateNewLINode: function AC_getOrCreateNewLINode() {
+ if (!this.li) {
+ this.li = document.createElement("li");
+
+ this.li.className = "pref-item";
+ this.li.setAttribute("name", this.name);
+
+ // Click callback to ensure list item selected even on no-action tap events
+ this.li.addEventListener("click", function (aEvent) {
+ AboutConfig.selected = AboutConfig.getLINodeForEvent(aEvent);
+ });
+
+ // Contextmenu callback to identify selected list item
+ this.li.addEventListener("contextmenu", function (aEvent) {
+ AboutConfig.contextMenuLINode = AboutConfig.getLINodeForEvent(aEvent);
+ });
+
+ this.li.setAttribute("contextmenu", "prefs-context-menu");
+
+ const prefName = document.createElement("div");
+ prefName.className = "pref-name";
+ prefName.addEventListener("click", function (event) {
+ AboutConfig.selectOrToggleBoolPref(event);
+ });
+ prefName.textContent = this.name;
+
+ this.li.appendChild(prefName);
+
+ const prefItemLine = document.createElement("div");
+ prefItemLine.className = "pref-item-line";
+
+ const prefValue = document.createElement("input");
+ prefValue.className = "pref-value";
+ prefValue.addEventListener("blur", function (event) {
+ AboutConfig.setIntOrStringPref(event);
+ });
+ prefValue.addEventListener("click", function (event) {
+ AboutConfig.selectOrToggleBoolPref(event);
+ });
+ prefValue.value = "";
+ prefItemLine.appendChild(prefValue);
+
+ const resetButton = document.createElement("div");
+ resetButton.className = "pref-button reset";
+ resetButton.addEventListener("click", function (event) {
+ AboutConfig.resetDefaultPref(event);
+ });
+ resetButton.setAttribute("data-l10n-id", "config-pref-reset-button");
+ prefItemLine.appendChild(resetButton);
+
+ const toggleButton = document.createElement("div");
+ toggleButton.className = "pref-button toggle";
+ toggleButton.addEventListener("click", function (event) {
+ AboutConfig.toggleBoolPref(event);
+ });
+ toggleButton.setAttribute("data-l10n-id", "config-pref-toggle-button");
+ prefItemLine.appendChild(toggleButton);
+
+ const upButton = document.createElement("div");
+ upButton.className = "pref-button up";
+ upButton.addEventListener("click", function (event) {
+ AboutConfig.incrOrDecrIntPref(event, 1);
+ });
+ prefItemLine.appendChild(upButton);
+
+ const downButton = document.createElement("div");
+ downButton.className = "pref-button down";
+ downButton.addEventListener("click", function (event) {
+ AboutConfig.incrOrDecrIntPref(event, -1);
+ });
+ prefItemLine.appendChild(downButton);
+
+ this.li.appendChild(prefItemLine);
+
+ // Delay providing the list item values, until the LI is returned and added to the document
+ setTimeout(this._valueSetup.bind(this), INNERHTML_VALUE_DELAY);
+ }
+
+ return this.li;
+ },
+
+ // Initialize list item object values
+ _valueSetup: function AC_valueSetup() {
+ this.li.setAttribute("type", this.type);
+ this.li.setAttribute("value", this.value);
+
+ const valDiv = this.li.querySelector(".pref-value");
+ valDiv.value = this.value;
+
+ switch (this.type) {
+ case Services.prefs.PREF_BOOL:
+ valDiv.setAttribute("type", "button");
+ this.li.querySelector(".up").setAttribute("disabled", true);
+ this.li.querySelector(".down").setAttribute("disabled", true);
+ break;
+ case Services.prefs.PREF_STRING:
+ valDiv.setAttribute("type", "text");
+ this.li.querySelector(".up").setAttribute("disabled", true);
+ this.li.querySelector(".down").setAttribute("disabled", true);
+ this.li.querySelector(".toggle").setAttribute("disabled", true);
+ break;
+ case Services.prefs.PREF_INT:
+ valDiv.setAttribute("type", "number");
+ this.li.querySelector(".toggle").setAttribute("disabled", true);
+ break;
+ }
+
+ this.li.setAttribute("default", this.default);
+ if (this.default) {
+ this.li.querySelector(".reset").setAttribute("disabled", true);
+ }
+
+ if (this.locked) {
+ valDiv.setAttribute("disabled", this.locked);
+ this.li.querySelector(".pref-name").setAttribute("locked", true);
+ }
+ },
+};