Bug config - Make Metro about:config better, r=mbrubeck

* * *
Bug 959031 - Make Metro about:config better -- Nits, r=mbrubeck
* * *
Bug 959031 - Make Metro about:config better -- Retro Fit Strings, r=mbrubeck

--HG--
rename : mobile/android/chrome/content/config.js => browser/metro/base/content/pages/config.js
rename : mobile/android/chrome/content/config.xhtml => browser/metro/base/content/pages/config.xhtml
This commit is contained in:
Mark Capella 2014-01-22 05:57:23 -05:00
Родитель 196e368400
Коммит 5f0e931495
15 изменённых файлов: 1055 добавлений и 538 удалений

Просмотреть файл

@ -1,407 +0,0 @@
/* 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/. */
let Ci = Components.interfaces;
Components.utils.import("resource://gre/modules/Services.jsm");
var ViewConfig = {
get _main() {
delete this._main;
return this._main = document.getElementById("main-container");
},
get _container() {
delete this._container;
return this._container = document.getElementById("prefs-container");
},
get _editor() {
delete this._editor;
return this._editor = document.getElementById("editor");
},
init: function init() {
this._main.addEventListener("click", this, false);
window.addEventListener("resize", this, false);
window.addEventListener("prefchange", this, false);
window.addEventListener("prefnew", this, false);
this._handleWindowResize();
this.filter("");
document.getElementById("textbox").focus();
},
uninit: function uninit() {
this._main.removeEventListener("click", this, false);
window.removeEventListener("resize", this, false);
window.removeEventListener("prefchange", this, false);
window.removeEventListener("prefnew", this, false);
},
filter: function filter(aValue) {
let row = document.getElementById("editor-row");
let container = this._container;
container.scrollBoxObject.scrollTo(0, 0);
// Clear the list by replacing with a shallow copy
let empty = container.cloneNode(false);
empty.appendChild(row);
container.parentNode.replaceChild(empty, container);
this._container = empty;
let result = Utils.getPrefs(aValue);
this._container.setItems(result.map(this._createItem, this));
},
open: function open(aType) {
let buttons = document.getElementById("editor-buttons-add");
buttons.setAttribute("hidden", "true");
let shouldFocus = false;
let setting = document.getElementById("editor-setting");
switch (aType) {
case Ci.nsIPrefBranch.PREF_INT:
setting.setAttribute("type", "integer");
setting.setAttribute("min", -Infinity);
break;
case Ci.nsIPrefBranch.PREF_BOOL:
setting.setAttribute("type", "bool");
break;
case Ci.nsIPrefBranch.PREF_STRING:
setting.setAttribute("type", "string");
break;
}
setting.removeAttribute("title");
setting.removeAttribute("pref");
if (setting.input)
setting.input.value = "";
document.getElementById("editor-container").appendChild(this._editor);
let nameField = document.getElementById("editor-name");
nameField.value = "";
this._editor.setAttribute("hidden", "false");
this._currentItem = null;
nameField.focus();
},
close: function close(aValid) {
this._editor.setAttribute("hidden", "true");
let buttons = document.getElementById("editor-buttons-add");
buttons.setAttribute("hidden", "false");
if (aValid) {
let name = document.getElementById("editor-name").inputField.value;
if (name != "") {
let setting = document.getElementById("editor-setting");
setting.setAttribute("pref", name);
setting.valueToPreference();
}
}
document.getElementById("editor-container").appendChild(this._editor);
},
_currentItem: null,
delayEdit: function(aItem) {
setTimeout(this.edit.bind(this), 0, aItem);
},
edit: function(aItem) {
if (!aItem)
return;
let pref = Utils.getPref(aItem.getAttribute("name"));
if (pref.lock || !pref.name || aItem == this._currentItem)
return;
this.close(false);
this._currentItem = aItem;
let setting = document.getElementById("editor-setting");
let shouldFocus = false;
switch (pref.type) {
case Ci.nsIPrefBranch.PREF_BOOL:
setting.setAttribute("type", "bool");
break;
case Ci.nsIPrefBranch.PREF_INT:
setting.setAttribute("type", "integer");
setting.setAttribute("increment", this.getIncrementForValue(pref.value));
setting.setAttribute("min", -Infinity);
shouldFocus = true;
break;
case Ci.nsIPrefBranch.PREF_STRING:
setting.setAttribute("type", "string");
shouldFocus = true;
break;
}
setting.setAttribute("title", pref.name);
setting.setAttribute("pref", pref.name);
this._container.insertBefore(this._editor, aItem);
let resetButton = document.getElementById("editor-reset");
resetButton.setAttribute("disabled", pref.default);
this._editor.setAttribute("default", pref.default);
this._editor.setAttribute("hidden", "false");
if (shouldFocus && setting.input)
setting.input.focus();
},
reset: function reset(aItem) {
let setting = document.getElementById("editor-setting");
let pref = Utils.getPref(setting.getAttribute("pref"));
if (!pref.default)
Utils.resetPref(pref.name);
},
handleEvent: function handleEvent(aEvent) {
switch (aEvent.type) {
case "resize":
this._handleWindowResize();
break;
case "prefchange":
case "prefnew":
this._handlePrefChange(aEvent.detail, aEvent.type == "prefnew");
break;
case "click":
this._onClick();
break;
}
},
_handleWindowResize: function _handleWindowResize() {
let mainBox = document.getElementById("main-container");
let textbox = document.getElementById("textbox");
let height = window.innerHeight - textbox.getBoundingClientRect().height;
mainBox.setAttribute("height", height);
},
_onClick: function () {
// Blur the search box when tapping anywhere else in the content
// in order to close the soft keyboard.
document.getElementById("textbox").blur();
},
_handlePrefChange: function _handlePrefChange(aIndex, aNew) {
let isEditing = !this._editor.hidden;
let shouldUpdateEditor = false;
if (isEditing) {
let setting = document.getElementById("editor-setting");
let editorIndex = Utils.getPrefIndex(setting.getAttribute("pref"));
shouldUpdateEditor = (aIndex == editorIndex);
if(shouldUpdateEditor || aIndex > editorIndex)
aIndex += 1;
}
// XXX An item display value will probably fail if a pref is changed in the
// background while there is a filter on the pref
let item = shouldUpdateEditor ? this._editor.nextSibling
: this._container.childNodes[aIndex + 1];// add 1 because of the new pref row
if (!item) // the pref is not viewable
return;
if (aNew) {
let pref = Utils.getPrefByIndex(aIndex);
let row = this._createItem(pref);
this._container.insertBefore(row, item);
return;
}
let pref = Utils.getPref(item.getAttribute("name"));
if (shouldUpdateEditor) {
this._editor.setAttribute("default", pref.default);
let resetButton = document.getElementById("editor-reset");
resetButton.disabled = pref.default;
}
item.setAttribute("default", pref.default);
item.lastChild.setAttribute("value", pref.value);
},
_createItem: function _createItem(aPref) {
let row = document.createElement("richlistitem");
row.setAttribute("name", aPref.name);
row.setAttribute("type", aPref.type);
row.setAttribute("role", "button");
row.setAttribute("default", aPref.default);
let label = document.createElement("label");
label.setAttribute("class", "preferences-title");
label.setAttribute("value", aPref.name);
label.setAttribute("crop", "end");
row.appendChild(label);
label = document.createElement("label");
label.setAttribute("class", "preferences-value");
label.setAttribute("value", aPref.value);
label.setAttribute("crop", "end");
row.appendChild(label);
return row;
},
getIncrementForValue: function getIncrementForValue(aValue) {
let count = 1;
while (aValue >= 100) {
aValue /= 10;
count *= 10;
}
return count;
}
};
var Utils = {
QueryInterface: function(aIID) {
if (!aIID.equals(Ci.nsIObserver) && !aIID.equals(Ci.nsISupportsWeakReference))
throw Components.results.NS_ERROR_NO_INTERFACE;
return this;
},
get _branch() {
delete this._branch;
this._branch = Services.prefs.getBranch(null);
this._branch.addObserver("", this, true);
return this._branch;
},
get _preferences() {
delete this._preferences;
let list = this._branch.getChildList("", {}).filter(function(element) {
return !(/^capability\./.test(element));
});
return this._preferences = list.sort().map(this.getPref, this);
},
getPrefs: function getPrefs(aValue) {
let result = this._preferences.slice();;
if (aValue != "") {
let reg = this._generateRegexp(aValue);
if (!reg)
return [];
result = this._preferences.filter(function(element, index, array) {
return reg.test(element.name + ";" + element.value);
});
}
return result;
},
getPref: function getPref(aPrefName) {
let branch = this._branch;
let pref = {
name: aPrefName,
value: "",
default: !branch.prefHasUserValue(aPrefName),
lock: branch.prefIsLocked(aPrefName),
type: branch.getPrefType(aPrefName)
};
try {
switch (pref.type) {
case Ci.nsIPrefBranch.PREF_BOOL:
pref.value = branch.getBoolPref(aPrefName).toString();
break;
case Ci.nsIPrefBranch.PREF_INT:
pref.value = branch.getIntPref(aPrefName).toString();
break;
default:
case Ci.nsIPrefBranch.PREF_STRING:
pref.value = branch.getComplexValue(aPrefName, Ci.nsISupportsString).data;
// Try in case it's a localized string (will throw an exception if not)
if (pref.default && /^chrome:\/\/.+\/locale\/.+\.properties/.test(pref.value))
pref.value = branch.getComplexValue(aPrefName, Ci.nsIPrefLocalizedString).data;
break;
}
} catch (e) {}
return pref;
},
getPrefByIndex: function getPrefByIndex(aIndex) {
return this._preferences[aIndex];
},
getPrefIndex: function getPrefIndex(aPrefName) {
let prefs = this._preferences;
let high = prefs.length - 1;
let low = 0, middle, element;
while (low <= high) {
middle = parseInt((low + high) / 2);
element = prefs[middle];
if (element.name > aPrefName)
high = middle - 1;
else if (element.name < aPrefName)
low = middle + 1;
else
return middle;
}
return -1;
},
resetPref: function resetPref(aPrefName) {
this._branch.clearUserPref(aPrefName);
},
observe: function observe(aSubject, aTopic, aPrefName) {
if (aTopic != "nsPref:changed" || /^capability\./.test(aPrefName)) // avoid displaying "private" preferences
return;
let type = "prefchange";
let index = this.getPrefIndex(aPrefName);
if (index != - 1) {
// update the inner array
let pref = this.getPref(aPrefName);
this._preferences[index].value = pref.value;
}
else {
// XXX we could do better here
let list = this._branch.getChildList("", {}).filter(function(element, index, array) {
return !(/^capability\./.test(element));
});
this._preferences = list.sort().map(this.getPref, this);
type = "prefnew";
index = this.getPrefIndex(aPrefName);
}
let evt = document.createEvent("UIEvents");
evt.initUIEvent(type, true, true, window, index);
window.dispatchEvent(evt);
},
_generateRegexp: function _generateRegexp(aValue) {
if (aValue.charAt(0) == "/") {
try {
let rv = aValue.match(/^\/(.*)\/(i?)$/);
return RegExp(rv[1], rv[2]);
}
catch (e) {
return null; // Do nothing on incomplete or bad RegExp
}
}
return RegExp(aValue.replace(/([^* \w])/g, "\\$1").replace(/^\*+/, "")
.replace(/\*+/g, ".*"), "i");
}
};

Просмотреть файл

@ -1,65 +0,0 @@
<?xml version="1.0"?>
<!-- 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/. -->
<?xml-stylesheet href="chrome://browser/skin/platform.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/browser.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/config.css" type="text/css"?>
<!DOCTYPE window [
<!ENTITY % configDTD SYSTEM "chrome://browser/locale/config.dtd">
%configDTD;
]>
<window id="about:config"
onload="ViewConfig.init();"
onunload="ViewConfig.uninit();"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/x-javascript" src="chrome://browser/content/config.js"/>
<vbox class="panel-dark" flex="1">
<textbox id="textbox"
oncommand="ViewConfig.filter(this.value)"
type="search"
timeout="400"
emptytext="&empty.label;"/>
<hbox id="main-container" class="panel-dark">
<richlistbox id="prefs-container" flex="1" onselect="ViewConfig.delayEdit(this.selectedItem)" batch="25">
<richlistitem id="editor-row">
<vbox id="editor-container" flex="1">
<hbox align="center" flex="1">
<label value="&newpref.label;" flex="1"/>
<spacer flex="1" />
<hbox id="editor-buttons-add">
<button label="&integer.label;" oncommand="ViewConfig.open(Ci.nsIPrefBranch.PREF_INT)"/>
<button label="&boolean.label;" oncommand="ViewConfig.open(Ci.nsIPrefBranch.PREF_BOOL)"/>
<button label="&string.label;" oncommand="ViewConfig.open(Ci.nsIPrefBranch.PREF_STRING)"/>
</hbox>
</hbox>
<vbox id="editor" hidden="true">
<hbox align="center">
<textbox id="editor-name" emptytext="&addpref.name;" flex="1"/>
<setting id="editor-setting" emptytext="&addpref.value;" onlabel="true" offlabel="false" flex="1"/>
</hbox>
<hbox id="editor-buttons">
<button id="editor-cancel" label="&cancel.label;" oncommand="ViewConfig.close(false)"/>
<spacer flex="1"/>
<button id="editor-reset" label="&reset.label;" oncommand="ViewConfig.reset(this.parentNode.parentNode.nextSibling)"/>
<button id="editor-done" label="&done.label;" oncommand="ViewConfig.close(true)"/>
</hbox>
</vbox>
</vbox>
</richlistitem>
</richlistbox>
</hbox>
</vbox>
</window>

Просмотреть файл

@ -0,0 +1,642 @@
/* 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";
const {classes: Cc, interfaces: Ci, manager: Cm, utils: Cu} = Components;
Cu.import("resource://gre/modules/Services.jsm");
const PRIVATE_PREF_PREFIX = "capability."; // Tag to prevent exposing private preferences
const INITIAL_PAGE_DELAY = 500; // Initial pause on program start for scroll alignment
const PREFS_BUFFER_MAX = 100; // 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
let gStringBundle = Services.strings.createBundle("chrome://browser/locale/config.properties");
let 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) {
this._positiveButton.textContent = gStringBundle.GetStringFromName("newPref.createButton");
this._positiveButton.setAttribute("disabled", true);
if (aPrefName == "") {
return;
}
// Prevent addition of new "private" preferences
if (aPrefName.startsWith(PRIVATE_PREF_PREFIX)) {
this._positiveButton.textContent = gStringBundle.GetStringFromName("newPref.privateButton");
return;
}
// If item already in list, it's being changed, else added
let item = document.querySelector(".pref-item[name=" + aPrefName.quote() + "]");
if (item) {
this._positiveButton.textContent = gStringBundle.GetStringFromName("newPref.changeButton");
} 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, false);
},
// 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, false);
},
// 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 == KeyEvent.DOM_VK_RETURN)
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") ? true : false);
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;
}
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 = {
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");
let list = Services.prefs.getChildList("", {}).filter(function(aElement) {
// Prevent display of "private" preferences
return !aElement.startsWith(PRIVATE_PREF_PREFIX);
});
this._list = list.sort().map( function AC_getMapPref(aPref) {
return new Pref(aPref);
}, this);
// Display the current prefs list (retains searchFilter value)
this.bufferFilterInput();
// Setup the prefs observers
Services.prefs.addObserver("", this, false);
},
// 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((function() {
this._filterChangeTimer = null;
// Display updated prefs list when filterInput value settles
this._displayNewList();
}).bind(this), 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 and 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((function() {
window.scrollTo(0, 0);
}).bind(this), INITIAL_PAGE_DELAY);
},
// Clear the displayed preferences list
_clearPrefsContainer: function AC_clearPrefsContainer() {
// Quick clear the prefsContainer list
let 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
let filterExp = this.filterInput.value ?
new RegExp(this.filterInput.value, "i") : null;
// Get a new block for the display list
let 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) {
let currentSelection = this.selected;
if (aSelection == currentSelection) {
return;
}
// Clear any previous selection
if (currentSelection) {
currentSelection.classList.remove("selected");
currentSelection.removeEventListener("keypress", this.handleKeypress, false);
}
// Set any current selection
if (aSelection) {
aSelection.classList.add("selected");
aSelection.addEventListener("keypress", this.handleKeypress, false);
}
},
// Watch user key input so we can provide Enter key action, commit input values
handleKeypress: function AC_handleKeypress(aEvent) {
if (aEvent.keyCode == KeyEvent.DOM_VK_RETURN)
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) {
let pref = aNode.getAttribute("name");
return new Pref(pref);
},
// When list item name or value are tapped
selectOrToggleBoolPref: function AC_selectOrToggleBoolPref(aEvent) {
let 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
let 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) {
let node = this.getLINodeForEvent(aEvent);
// Skip if locked
let 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) {
let node = this.getLINodeForEvent(aEvent);
// If not already selected, do so
if (this.selected != node) {
this.selected = node;
}
// Reset will handle any locked condition
let pref = this._getPrefForNode(node);
pref.reset();
},
// When we want to toggle a bool pref
toggleBoolPref: function AC_toggleBoolPref(aEvent) {
let node = this.getLINodeForEvent(aEvent);
// Skip if locked, or not boolean
let 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) {
let node = this.getLINodeForEvent(aEvent);
// Skip if locked
let pref = this._getPrefForNode(node);
if (pref.locked) {
return;
}
pref.value += aInt;
},
// Observe preference changes
observe: function AC_observe(aSubject, aTopic, aPrefName) {
let pref = new Pref(aPrefName);
// Ignore uninteresting preference changes, and external changes to "private" preferences
if ((aTopic != "nsPref:changed") || pref.name.startsWith(PRIVATE_PREF_PREFIX)) {
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 not already in list, refresh display as it's being added
let item = document.querySelector(".pref-item[name=" + pref.name.quote() + "]");
if (!item) {
document.location.reload();
return;
}
// Else we're modifying a pref
item.setAttribute("value", pref.value);
let 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");
}
}
/* ============================== 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);
}
},
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);
},
false
);
// Create list item outline, bind to object actions
this.li.innerHTML =
"<div class='pref-name' " +
"onclick='AboutConfig.selectOrToggleBoolPref(event);'>" +
this.name +
"</div>" +
"<div class='pref-item-line'>" +
"<input class='pref-value' value='' " +
"onblur='AboutConfig.setIntOrStringPref(event);' " +
"onclick='AboutConfig.selectOrToggleBoolPref(event);'>" +
"</input>" +
"<div class='pref-button reset' " +
"onclick='AboutConfig.resetDefaultPref(event);'>" +
gStringBundle.GetStringFromName("pref.resetButton") +
"</div>" +
"<div class='pref-button toggle' " +
"onclick='AboutConfig.toggleBoolPref(event);'>" +
gStringBundle.GetStringFromName("pref.toggleButton") +
"</div>" +
"<div class='pref-button up' " +
"onclick='AboutConfig.incrOrDecrIntPref(event, 1);'>" +
"</div>" +
"<div class='pref-button down' " +
"onclick='AboutConfig.incrOrDecrIntPref(event, -1);'>" +
"</div>" +
"</div>";
// 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);
let 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);
}
}
}

Просмотреть файл

@ -0,0 +1,81 @@
<?xml version="1.0"?>
<!-- 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/. -->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
%globalDTD;
<!ENTITY % configDTD SYSTEM "chrome://browser/locale/config.dtd">
%configDTD;
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="viewport" content="width=device-width; user-scalable=false" />
<link rel="stylesheet" href="chrome://browser/skin/config.css" type="text/css"/>
<script type="text/javascript;version=1.8" src="chrome://browser/content/pages/config.js"/>
</head>
<body dir="&locale.dir;" onload="NewPrefDialog.init(); AboutConfig.init();"
onunload="AboutConfig.uninit();">
<div class="toolbar">
<div class="toolbar-container">
<div id="new-pref-toggle-button" onclick="NewPrefDialog.toggleShowHide();"/>
<div class="toolbar-item" id="filter-container">
<div id="filter-search-button"/>
<input id="filter-input" type="search" placeholder="&empty.label;" value=""
oninput="AboutConfig.bufferFilterInput();"/>
<div id="filter-input-clear-button" onclick="AboutConfig.clearFilterInput();"/>
</div>
</div>
</div>
<div id="content" ontouchstart="AboutConfig.filterInput.blur();">
<div id="new-pref-container">
<li class="pref-item" id="new-pref-item">
<div class="pref-item-line">
<input class="pref-name" id="new-pref-name" type="text" placeholder="&addpref.name;"
onfocus="NewPrefDialog.focusName(event);"
oninput="NewPrefDialog.updateName(event);"/>
<select class="pref-value" id="new-pref-type" onchange="NewPrefDialog.type = event.target.value;">
<option value="boolean">&boolean.label;</option>
<option value="string">&string.label;</option>
<option value="int">&integer.label;</option>
</select>
</div>
<div class="pref-item-line" id="new-pref-line-boolean">
<input class="pref-value" id="new-pref-value-boolean" disabled="disabled"/>
<div class="pref-button toggle" onclick="NewPrefDialog.toggleBoolValue();">&toggle.label;</div>
</div>
<div class="pref-item-line">
<input class="pref-value" id="new-pref-value-string" placeholder="&string.placeholder;"/>
<input class="pref-value" id="new-pref-value-int" placeholder="&number.placeholder;" type="number"/>
</div>
<div class="pref-item-line">
<div class="pref-button cancel" id="negative-button" onclick="NewPrefDialog.hide();">&cancel.label;</div>
<div class="pref-button create" id="positive-button" onclick="NewPrefDialog.create(event);"></div>
</div>
</li>
</div>
<div id="prefs-shield"></div>
<ul id="prefs-container"/>
<ul id="loading-container"><li></li></ul>
</div>
</body>
</html>

Просмотреть файл

@ -6,11 +6,12 @@
chrome.jar:
% content browser %content/
content/aboutAddons.xhtml (content/pages/aboutAddons.xhtml)
content/aboutCertError.xhtml (content/pages/aboutCertError.xhtml)
content/aboutRights.xhtml (content/pages/aboutRights.xhtml)
content/blockedSite.xhtml (content/pages/blockedSite.xhtml)
content/config.xhtml (content/pages/config.xhtml)
content/netError.xhtml (content/pages/netError.xhtml)
content/aboutAddons.xhtml (content/pages/aboutAddons.xhtml)
#ifdef MOZ_CRASHREPORTER
content/crashprompt.xhtml (content/pages/crashprompt.xhtml)
#endif
@ -64,8 +65,7 @@ chrome.jar:
content/ContentAreaObserver.js (content/ContentAreaObserver.js)
content/BrowserTouchHandler.js (content/BrowserTouchHandler.js)
* content/WebProgress.js (content/WebProgress.js)
content/config.xul (content/config.xul)
content/config.js (content/config.js)
content/pages/config.js (content/pages/config.js)
* content/browser.xul (content/browser.xul)
content/browser.js (content/browser.js)
* content/browser-ui.js (content/browser-ui.js)
@ -103,6 +103,6 @@ chrome.jar:
content/RemoteTabsView.js (content/startui/RemoteTabsView.js)
#endif
% override chrome://global/content/config.xul chrome://browser/content/config.xul
% override chrome://global/content/config.xul chrome://browser/content/config.xhtml
% override chrome://global/content/netError.xhtml chrome://browser/content/netError.xhtml
% override chrome://mozapps/content/extensions/extensions.xul chrome://browser/content/aboutAddons.xhtml

Просмотреть файл

@ -3,14 +3,14 @@
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!ENTITY empty.label "Search">
<!ENTITY newpref.label "Add a New Preference">
<!ENTITY addpref.name "Name">
<!ENTITY addpref.value "Value">
<!ENTITY cancel.label "Cancel">
<!ENTITY reset.label "Reset">
<!ENTITY done.label "Done">
<!ENTITY toggle.label "Toggle">
<!ENTITY integer.label "Integer">
<!ENTITY string.label "String">
<!ENTITY boolean.label "Boolean">
<!ENTITY string.placeholder "Enter a string">
<!ENTITY number.placeholder "Enter a number">

Просмотреть файл

@ -0,0 +1,10 @@
# 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/.
newPref.createButton=Create
newPref.privateButton=Private
newPref.changeButton=Change
pref.toggleButton=Toggle
pref.resetButton=Reset

Просмотреть файл

@ -12,8 +12,9 @@
locale/browser/aboutCertError.dtd (%chrome/aboutCertError.dtd)
locale/browser/browser.dtd (%chrome/browser.dtd)
locale/browser/browser.properties (%chrome/browser.properties)
locale/browser/region.properties (%chrome/region.properties)
locale/browser/config.dtd (%chrome/config.dtd)
locale/browser/config.properties (%chrome/config.properties)
locale/browser/region.properties (%chrome/region.properties)
locale/browser/preferences.dtd (%chrome/preferences.dtd)
locale/browser/aboutPanel.dtd (%chrome/aboutPanel.dtd)
locale/browser/searchPanel.dtd (%chrome/searchPanel.dtd)

Просмотреть файл

@ -2,96 +2,344 @@
* 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/. */
@media (max-width: 499px) {
#editor-container > hbox {
-moz-box-orient: vertical;
}
html,
body {
margin: 0;
padding: 0;
background-color: #ced7de;
-moz-user-select: none;
font-family: "Segoe UI", sans-serif;
-moz-text-size-adjust: none;
}
richlistitem {
-moz-box-align: center;
.toolbar {
width: 100%;
height: 3em;
position: fixed;
top: 0;
left: 0;
z-index: 10;
box-shadow: 0 0 3px #444;
background-color: #ced7de;
color: #000000;
font-weight: bold;
border-bottom: 2px solid;
-moz-border-bottom-colors: #ff9100 #f27900;
}
richlistitem .preferences-title {
pointer-events: none;
min-width: 200px;
-moz-box-flex: 1;
margin-right: 8px;
.toolbar-container {
max-width: 40em;
margin-left: auto;
margin-right: auto;
}
/* XXX look + sync */
richlistitem[default="false"] .preferences-title {
font-weight: bold;
#filter-container {
margin-top: 0.5em;
margin-bottom: 0.5em;
margin-right: 0.5em;
height: 2em;
border: 1px solid transparent;
border-image-source: url("chrome://browser/skin/images/textfield.png");
border-image-slice: 1 1 3 1;
border-image-width: 1px 1px 3px 1px;
overflow: hidden;
display: flex;
flex-direction: row;
}
richlistitem .preferences-value {
min-width: 200px;
pointer-events: none;
-moz-box-flex: 4;
text-align: end;
color: grey;
#filter-input {
-moz-appearance: none;
border: none;
background-image: none;
background-color: transparent;
display: inline-block;
width: 12em;
min-width: 0;
color: #000000;
opacity: 1;
flex: 1 1 auto;
}
/* Editor */
#editor-row {
padding: 0;
background: #E9E9E9;
#filter-input:-moz-placeholder {
color: rgba(255,255,255,0.5);
}
#editor {
border-bottom: 1px solid rgb(207,207,207);
.toolbar input {
display: inline-block;
height: 100%;
min-width: 3em;
-moz-box-sizing: border-box;
opacity: 0.75;
}
#editor > hbox > #editor-name,
#editor > hbox > #editor-cancel,
#editor > hbox > #editor-done {
display: none;
#new-pref-toggle-button {
background-position: center center;
background-image: url("chrome://browser/skin/images/reader-plus-icon-xhdpi.png");
background-size: 48px 48px;
height: 48px;
width: 48px;
display: inline-block;
outline-style: none;
}
#editor-container > #editor > hbox > #editor-name,
#editor-container > #editor > hbox > #editor-cancel,
#editor-container > #editor > hbox > #editor-done {
display: -moz-box;
#filter-search-button {
background-image: url("chrome://browser/skin/images/search.png");
background-size: 32px 32px;
height: 32px;
width: 32px;
display: inline-block;
outline-style: none;
}
#editor-container > #editor > hbox > #editor-reset {
display: none;
#filter-input-clear-button {
background-image: url("chrome://browser/skin/images/search-clear-30.png");
background-size: 32px 32px;
height: 32px;
width: 32px;
display: inline-block;
outline-style: none;
}
#editor-container > hbox > label {
pointer-events: none;
color: black;
#filter-input[value=""] + #filter-input-clear-button {
display: none;
}
#editor + richlistitem {
display: none;
.toolbar-item {
display: inline-block;
height: 3em;
min-width: 3em;
float: right;
}
#editor[default="false"] .preferences-title {
font-weight: bold;
#content {
position: relative;
margin: 0;
margin-left: auto;
margin-right: auto;
padding-top: 3em;
padding-left: 0;
padding-right: 0;
min-height: 100%;
max-width: 40em;
}
#editor-setting setting {
border-color: transparent !important;
ul {
list-style-position: inside;
border: 1px solid #808080;
background-color: #ffffff;
min-height: 100%;
width: 100%;
padding-top: 0;
margin: 0;
padding-left: 0;
-moz-box-sizing: border-box;
box-shadow: 0 0 5px #000000;
overflow-x: hidden;
}
#editor-setting[type="string"] .setting-input {
-moz-box-flex: 4;
#new-pref-container {
width: 100%;
margin: 0;
background-color: #ffffff;
-moz-box-sizing: border-box;
box-shadow: 0 0 5px #000000;
overflow-x: hidden;
max-width: 40em;
max-height: 100%;
position: fixed;
top: 3em;
left: auto;
display: none;
z-index: 5;
}
#editor-setting[type="string"] .setting-input > textbox {
-moz-box-flex: 1;
#new-pref-container input,
#new-pref-container select {
border: none;
background-image: none;
}
/* bug 647650: keep 'text-align: right' here instead of using start/end since
* the field should looks like ltr as much as possible
*/
#editor-setting[type="string"] .setting-input > textbox:-moz-locale-dir(rtl) {
direction: ltr;
text-align: right;
#new-pref-container.show {
display: block;
}
#editor-buttons {
margin: 2px;
li {
list-style-type: none;
border-bottom: 1px solid #d3d3d3;
opacity: 1;
background-color: #ffffff;
cursor: pointer;
}
#new-pref-line-boolean,
#new-pref-value-string,
#new-pref-value-int {
display: none;
}
#new-pref-item[typestyle="boolean"] #new-pref-line-boolean,
#new-pref-item[typestyle="string"] #new-pref-value-string,
#new-pref-item[typestyle="int"] #new-pref-value-int {
display: block;
}
.pref-name,
.pref-value {
padding: 15px 10px;
text-align: left;
text-overflow: ellipsis;
overflow: hidden;
background-image: none;
}
.pref-value {
color: rgba(0,0,0,0.5);
flex: 1 1 auto;
border: none;
-moz-appearance: none;
background-image: none;
background-color: transparent;
}
.pref-name[locked] {
padding-right: 20px;
background-image: url("chrome://browser/skin/images/lock.png");
background-repeat: no-repeat;
background-position: right 50%;
background-size: auto 60%;
}
#new-pref-name {
width: 30em;
}
#new-pref-type {
display: inline-block !important;
border-left: 1px solid #d3d3d3;
width: 10em;
text-align: right;
}
.pref-item-line {
border-top: 1px solid rgba(0,0,0,0.05);
color: rgba(0,0,0,0.5);
display: flex;
flex-direction: row;
}
#new-pref-value-boolean {
flex: 1 1 auto;
}
/* Disable newPref dialog spinbuttons, use custom version from Android */
/* Filed Bug 962359 to enhance the default spinbutton style to be touch-friendly */
#new-pref-value-int {
-moz-appearance: textfield;
}
#new-pref-container .pref-button.toggle {
display: inline-block;
opacity: 1;
flex: 0 1 auto;
float: right;
}
#new-pref-container .pref-button.cancel,
#new-pref-container .pref-button.create {
display: inline-block;
opacity: 1;
flex: 1 1 auto;
}
.pref-item-line {
pointer-events: none;
}
#new-pref-container .pref-item-line,
.pref-item.selected .pref-item-line,
.pref-item:not(.selected) .pref-button.reset {
pointer-events: auto;
}
#new-pref-container .pref-button.create[disabled] {
color: #d3d3d3;
}
.pref-item.selected {
background-color: rgba(0,0,255,0.05);
}
.pref-button {
display: inline-block;
-moz-box-sizing: border-box;
text-align: center;
padding: 10px 1em;
border-left: 1px solid rgba(0,0,0,0.1);
opacity: 0;
transition-property: opacity;
transition-duration: 500ms;
}
.pref-item.selected .pref-item-line .pref-button {
opacity: 1;
}
.pref-item:not(.selected) .pref-item-line .pref-button:not(.reset) {
display: none;
}
.pref-item:not(.selected) .pref-button.reset {
opacity: 1;
}
/* Disable detail list item spinbuttons, use custom version from Android */
/* Filed Bug 962359 to enhance the default spinbutton style to be touch-friendly */
.pref-item input[type="number"] {
-moz-appearance: textfield;
}
.pref-button:active {
background-color: rgba(0,0,255,0.2);
}
.pref-button[disabled] {
display: none;
}
.pref-button.up {
background-image: url("chrome://browser/skin/images/arrowup-16.png");
background-position: center center;
background-repeat: no-repeat;
}
.pref-button.down {
background-image: url("chrome://browser/skin/images/arrowdown-16.png");
background-position: center center;
background-repeat: no-repeat;
}
#prefs-shield {
width: 100%;
height: 100%;
background-color: rgba(0,0,0,0.5);
position: fixed;
top: 0;
left: 0;
opacity: 0;
transition-property: opacity;
transition-duration: 500ms;
display: none;
}
#prefs-shield[shown] {
display: block;
opacity: 1;
}
#loading-container > li {
background-image: url(chrome://global/skin/media/throbber.png);
background-position: center center;
background-repeat: no-repeat;
padding-left: 40px;
height: 3em;
width: 100%;
}

Двоичные данные
browser/metro/theme/images/lock.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 636 B

Двоичные данные
browser/metro/theme/images/reader-plus-icon-xhdpi.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 274 B

Двоичные данные
browser/metro/theme/images/search-clear-30.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 476 B

Двоичные данные
browser/metro/theme/images/search.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 858 B

Двоичные данные
browser/metro/theme/images/textfield.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 166 B

Просмотреть файл

@ -150,3 +150,10 @@ chrome.jar:
skin/images/arrow-left.png (images/arrow-left.png)
skin/images/arrow-left@1.4x.png (images/arrow-left@1.4x.png)
skin/images/arrow-left@1.8x.png (images/arrow-left@1.8x.png)
# AboutConfig specific:
skin/images/textfield.png (images/textfield.png)
skin/images/reader-plus-icon-xhdpi.png (images/reader-plus-icon-xhdpi.png)
skin/images/search.png (images/search.png)
skin/images/search-clear-30.png (images/search-clear-30.png)
skin/images/lock.png (images/lock.png)