Bug 1106559 - Improve the in-content search preference UI. r=felipe, a=sledru
authorFlorian Quèze <florian@queze.net>
Wed, 17 Dec 2014 16:20:04 +0100
changeset 242498 6aa407bf00ef2e5230dae14133b42f0ed0134950
parent 242497 6a72715d7035e468690727cedfe0aa77f4026f08
child 242499 22720aceab14f4aa1d6098eda1f3077164c8b180
push id4311
push userraliiev@mozilla.com
push dateMon, 12 Jan 2015 19:37:41 +0000
treeherdermozilla-beta@150c9fed433b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfelipe, sledru
bugs1106559
milestone36.0a2
Bug 1106559 - Improve the in-content search preference UI. r=felipe, a=sledru
browser/components/preferences/in-content/preferences.xul
browser/components/preferences/in-content/search.js
browser/components/preferences/in-content/search.xul
browser/themes/linux/jar.mn
browser/themes/osx/jar.mn
browser/themes/shared/incontentprefs/check.png
browser/themes/shared/incontentprefs/check@2x.png
browser/themes/shared/incontentprefs/search.css
browser/themes/windows/jar.mn
--- a/browser/components/preferences/in-content/preferences.xul
+++ b/browser/components/preferences/in-content/preferences.xul
@@ -9,17 +9,17 @@
 
 <?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css"?>
 <?xml-stylesheet href="chrome://global/skin/in-content/common.css"?>
 <?xml-stylesheet
   href="chrome://browser/skin/preferences/in-content/preferences.css"?>
 <?xml-stylesheet
   href="chrome://browser/content/preferences/handlers.css"?>
 <?xml-stylesheet href="chrome://browser/skin/preferences/applications.css"?>
-<?xml-stylesheet href="chrome://browser/content/preferences/in-content/search.css"?>
+<?xml-stylesheet href="chrome://browser/skin/preferences/in-content/search.css"?>
 
 <!DOCTYPE page [
 <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
 <!ENTITY % globalPreferencesDTD SYSTEM "chrome://global/locale/preferences.dtd">
 <!ENTITY % preferencesDTD SYSTEM
   "chrome://browser/locale/preferences/preferences.dtd">
 <!ENTITY % privacyDTD SYSTEM "chrome://browser/locale/preferences/privacy.dtd">
 <!ENTITY % tabsDTD SYSTEM "chrome://browser/locale/preferences/tabs.dtd">
--- a/browser/components/preferences/in-content/search.js
+++ b/browser/components/preferences/in-content/search.js
@@ -1,75 +1,451 @@
 /* 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/. */
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 
+const ENGINE_FLAVOR = "text/x-moz-search-engine";
+
+var gEngineView = null;
+
 var gSearchPane = {
 
   init: function ()
   {
     if (!Services.prefs.getBoolPref("browser.search.showOneOffButtons")) {
       document.getElementById("category-search").hidden = true;
       if (document.location.hash == "#search")
         document.location.hash = "";
       return;
     }
 
+    gEngineView = new EngineView(new EngineStore());
+    document.getElementById("engineList").view = gEngineView;
+    this.buildDefaultEngineDropDown();
+
+    Services.obs.addObserver(this, "browser-search-engine-modified", false);
+    window.addEventListener("unload", () => {
+      Services.obs.removeObserver(this, "browser-search-engine-modified", false);
+    });
+  },
+
+  buildDefaultEngineDropDown: function() {
+    // This is called each time something affects the list of engines.
     let list = document.getElementById("defaultEngine");
-    let currentEngine = Services.search.currentEngine.name;
-    Services.search.getVisibleEngines().forEach(e => {
+    let currentEngine;
+
+    // First, try to preserve the current selection.
+    if (list.selectedItem)
+      currentEngine = list.selectedItem.label;
+
+    // If there's no current selection, use the current default engine.
+    if (!currentEngine)
+      currentEngine = Services.search.currentEngine.name;
+
+    // If the current engine isn't in the list any more, select the first item.
+    let engines = gEngineView._engineStore._engines;
+    if (!engines.some(e => e.name == currentEngine))
+      currentEngine = engines[0].name;
+
+    // Now clean-up and rebuild the list.
+    list.removeAllItems();
+    gEngineView._engineStore._engines.forEach(e => {
       let item = list.appendItem(e.name);
       item.setAttribute("class", "menuitem-iconic searchengine-menuitem menuitem-with-favicon");
       if (e.iconURI)
         item.setAttribute("image", e.iconURI.spec);
       item.engine = e;
       if (e.name == currentEngine)
         list.selectedItem = item;
     });
+  },
 
-    this.displayOneClickEnginesList();
+  observe: function(aEngine, aTopic, aVerb) {
+    if (aTopic == "browser-search-engine-modified") {
+      aEngine.QueryInterface(Components.interfaces.nsISearchEngine);
+      switch (aVerb) {
+      case "engine-added":
+        gEngineView._engineStore.addEngine(aEngine);
+        gEngineView.rowCountChanged(gEngineView.lastIndex, 1);
+        gSearchPane.buildDefaultEngineDropDown();
+        break;
+      case "engine-changed":
+        gEngineView._engineStore.reloadIcons();
+        gEngineView.invalidate();
+        break;
+      case "engine-removed":
+      case "engine-current":
+      case "engine-default":
+        // Not relevant
+        break;
+      }
+    }
+  },
 
-    document.getElementById("oneClickProvidersList")
-            .addEventListener("CheckboxStateChange", gSearchPane.saveOneClickEnginesList);
+  onTreeSelect: function() {
+    document.getElementById("removeEngineButton").disabled =
+      gEngineView.selectedIndex == -1 || gEngineView.lastIndex == 0;
+  },
+
+  onTreeKeyPress: function(aEvent) {
+    let index = gEngineView.selectedIndex;
+    let tree = document.getElementById("engineList");
+    if (aEvent.charCode == KeyEvent.DOM_VK_SPACE) {
+      // Space toggles the checkbox.
+      let newValue = !gEngineView._engineStore.engines[index].shown;
+      gEngineView.setCellValue(index, tree.columns.getFirstColumn(),
+                               newValue.toString());
+    }
+    else {
+      let isMac = Services.appinfo.OS == "Darwin";
+      if ((isMac && aEvent.keyCode == KeyEvent.DOM_VK_RETURN) ||
+          (!isMac && aEvent.keyCode == KeyEvent.DOM_VK_F2))
+        tree.startEditing(index, tree.columns.getLastColumn());
+    }
+  },
+
+  onRestoreDefaults: function() {
+    let num = gEngineView._engineStore.restoreDefaultEngines();
+    gEngineView.rowCountChanged(0, num);
+    gEngineView.invalidate();
   },
 
-  displayOneClickEnginesList: function () {
-    let richlistbox = document.getElementById("oneClickProvidersList");
-    let pref = document.getElementById("browser.search.hiddenOneOffs").value;
-    let hiddenList = pref ? pref.split(",") : [];
+  showRestoreDefaults: function(aEnable) {
+    document.getElementById("restoreDefaultSearchEngines").disabled = !aEnable;
+  },
 
-    while (richlistbox.firstChild)
-      richlistbox.firstChild.remove();
+  remove: function() {
+    gEngineView._engineStore.removeEngine(gEngineView.selectedEngine);
+    let index = gEngineView.selectedIndex;
+    gEngineView.rowCountChanged(index, -1);
+    gEngineView.invalidate();
+    gEngineView.selection.select(Math.min(index, gEngineView.lastIndex));
+    gEngineView.ensureRowIsVisible(gEngineView.currentIndex);
+    document.getElementById("engineList").focus();
+  },
+
+  editKeyword: function(aEngine, aNewKeyword) {
+    if (aNewKeyword) {
+      let bduplicate = false;
+      let eduplicate = false;
+      let dupName = "";
 
-    let currentEngine = Services.search.currentEngine.name;
-    Services.search.getVisibleEngines().forEach(e => {
-      if (e.name == currentEngine)
-        return;
+      try {
+        let bmserv =
+          Components.classes["@mozilla.org/browser/nav-bookmarks-service;1"]
+                    .getService(Components.interfaces.nsINavBookmarksService);
+        if (bmserv.getURIForKeyword(aNewKeyword))
+          bduplicate = true;
+      } catch(ex) {}
 
-      let item = document.createElement("richlistitem");
-      item.setAttribute("label", e.name);
-      if (hiddenList.indexOf(e.name) == -1)
-        item.setAttribute("checked", "true");
-      if (e.iconURI)
-        item.setAttribute("src", e.iconURI.spec);
-      richlistbox.appendChild(item);
-    });
+      // Check for duplicates in changes we haven't committed yet
+      let engines = gEngineView._engineStore.engines;
+      for each (let engine in engines) {
+        if (engine.alias == aNewKeyword &&
+            engine.name != aEngine.name) {
+          eduplicate = true;
+          dupName = engine.name;
+          break;
+        }
+      }
+
+      // Notify the user if they have chosen an existing engine/bookmark keyword
+      if (eduplicate || bduplicate) {
+        let strings = document.getElementById("engineManagerBundle");
+        let dtitle = strings.getString("duplicateTitle");
+        let bmsg = strings.getString("duplicateBookmarkMsg");
+        let emsg = strings.getFormattedString("duplicateEngineMsg", [dupName]);
+
+        Services.prompt.alert(window, dtitle, eduplicate ? emsg : bmsg);
+        return false;
+      }
+    }
+
+    gEngineView._engineStore.changeEngine(aEngine, "alias", aNewKeyword);
+    gEngineView.invalidate();
+    return true;
   },
 
   saveOneClickEnginesList: function () {
-    let richlistbox = document.getElementById("oneClickProvidersList");
     let hiddenList = [];
-    for (let child of richlistbox.childNodes) {
-      if (!child.checked)
-        hiddenList.push(child.getAttribute("label"));
+    for (let engine of gEngineView._engineStore.engines) {
+      if (!engine.shown)
+        hiddenList.push(engine.name);
     }
     document.getElementById("browser.search.hiddenOneOffs").value =
       hiddenList.join(",");
   },
 
   setDefaultEngine: function () {
     Services.search.currentEngine =
       document.getElementById("defaultEngine").selectedItem.engine;
-    this.displayOneClickEnginesList();
+  }
+};
+
+function onDragEngineStart(event) {
+  var selectedIndex = gEngineView.selectedIndex;
+  if (selectedIndex >= 0) {
+    event.dataTransfer.setData(ENGINE_FLAVOR, selectedIndex.toString());
+    event.dataTransfer.effectAllowed = "move";
+  }
+}
+
+
+function EngineStore() {
+  let pref = document.getElementById("browser.search.hiddenOneOffs").value;
+  this.hiddenList = pref ? pref.split(",") : [];
+
+  this._engines = Services.search.getVisibleEngines().map(this._cloneEngine, this);
+  this._defaultEngines = Services.search.getDefaultEngines().map(this._cloneEngine, this);
+
+  // check if we need to disable the restore defaults button
+  var someHidden = this._defaultEngines.some(function (e) e.hidden);
+  gSearchPane.showRestoreDefaults(someHidden);
+}
+EngineStore.prototype = {
+  _engines: null,
+  _defaultEngines: null,
+
+  get engines() {
+    return this._engines;
+  },
+  set engines(val) {
+    this._engines = val;
+    return val;
+  },
+
+  _getIndexForEngine: function ES_getIndexForEngine(aEngine) {
+    return this._engines.indexOf(aEngine);
+  },
+
+  _getEngineByName: function ES_getEngineByName(aName) {
+    for each (var engine in this._engines)
+      if (engine.name == aName)
+        return engine;
+
+    return null;
+  },
+
+  _cloneEngine: function ES_cloneEngine(aEngine) {
+    var clonedObj={};
+    for (var i in aEngine)
+      clonedObj[i] = aEngine[i];
+    clonedObj.originalEngine = aEngine;
+    clonedObj.shown = this.hiddenList.indexOf(clonedObj.name) == -1;
+    return clonedObj;
+  },
+
+  // Callback for Array's some(). A thisObj must be passed to some()
+  _isSameEngine: function ES_isSameEngine(aEngineClone) {
+    return aEngineClone.originalEngine == this.originalEngine;
+  },
+
+  addEngine: function ES_addEngine(aEngine) {
+    this._engines.push(this._cloneEngine(aEngine));
+  },
+
+  moveEngine: function ES_moveEngine(aEngine, aNewIndex) {
+    if (aNewIndex < 0 || aNewIndex > this._engines.length - 1)
+      throw new Error("ES_moveEngine: invalid aNewIndex!");
+    var index = this._getIndexForEngine(aEngine);
+    if (index == -1)
+      throw new Error("ES_moveEngine: invalid engine?");
+
+    if (index == aNewIndex)
+      return; // nothing to do
+
+    // Move the engine in our internal store
+    var removedEngine = this._engines.splice(index, 1)[0];
+    this._engines.splice(aNewIndex, 0, removedEngine);
+
+    Services.search.moveEngine(aEngine.originalEngine, aNewIndex);
+  },
+
+  removeEngine: function ES_removeEngine(aEngine) {
+    var index = this._getIndexForEngine(aEngine);
+    if (index == -1)
+      throw new Error("invalid engine?");
+
+    this._engines.splice(index, 1);
+    Services.search.removeEngine(aEngine.originalEngine);
+
+    if (this._defaultEngines.some(this._isSameEngine, aEngine))
+      gSearchPane.showRestoreDefaults(true);
+    gSearchPane.buildDefaultEngineDropDown();
+  },
+
+  restoreDefaultEngines: function ES_restoreDefaultEngines() {
+    var added = 0;
+
+    for (var i = 0; i < this._defaultEngines.length; ++i) {
+      var e = this._defaultEngines[i];
+
+      // If the engine is already in the list, just move it.
+      if (this._engines.some(this._isSameEngine, e)) {
+        this.moveEngine(this._getEngineByName(e.name), i);
+      } else {
+        // Otherwise, add it back to our internal store
+        this._engines.splice(i, 0, e);
+        let engine = e.originalEngine;
+        engine.hidden = false;
+        Services.search.moveEngine(engine, i);
+        added++;
+      }
+    }
+    gSearchPane.showRestoreDefaults(false);
+    gSearchPane.buildDefaultEngineDropDown();
+    return added;
+  },
+
+  changeEngine: function ES_changeEngine(aEngine, aProp, aNewValue) {
+    var index = this._getIndexForEngine(aEngine);
+    if (index == -1)
+      throw new Error("invalid engine?");
+
+    this._engines[index][aProp] = aNewValue;
+    aEngine.originalEngine[aProp] = aNewValue;
+  },
+
+  reloadIcons: function ES_reloadIcons() {
+    this._engines.forEach(function (e) {
+      e.uri = e.originalEngine.uri;
+    });
   }
 };
+
+function EngineView(aEngineStore) {
+  this._engineStore = aEngineStore;
+}
+EngineView.prototype = {
+  _engineStore: null,
+  tree: null,
+
+  get lastIndex() {
+    return this.rowCount - 1;
+  },
+  get selectedIndex() {
+    var seln = this.selection;
+    if (seln.getRangeCount() > 0) {
+      var min = {};
+      seln.getRangeAt(0, min, {});
+      return min.value;
+    }
+    return -1;
+  },
+  get selectedEngine() {
+    return this._engineStore.engines[this.selectedIndex];
+  },
+
+  // Helpers
+  rowCountChanged: function (index, count) {
+    this.tree.rowCountChanged(index, count);
+  },
+
+  invalidate: function () {
+    this.tree.invalidate();
+  },
+
+  ensureRowIsVisible: function (index) {
+    this.tree.ensureRowIsVisible(index);
+  },
+
+  getSourceIndexFromDrag: function (dataTransfer) {
+    return parseInt(dataTransfer.getData(ENGINE_FLAVOR));
+  },
+
+  // nsITreeView
+  get rowCount() {
+    return this._engineStore.engines.length;
+  },
+
+  getImageSrc: function(index, column) {
+    if (column.id == "engineName" && this._engineStore.engines[index].iconURI)
+      return this._engineStore.engines[index].iconURI.spec;
+    return "";
+  },
+
+  getCellText: function(index, column) {
+    if (column.id == "engineName")
+      return this._engineStore.engines[index].name;
+    else if (column.id == "engineKeyword")
+      return this._engineStore.engines[index].alias;
+    return "";
+  },
+
+  setTree: function(tree) {
+    this.tree = tree;
+  },
+
+  canDrop: function(targetIndex, orientation, dataTransfer) {
+    var sourceIndex = this.getSourceIndexFromDrag(dataTransfer);
+    return (sourceIndex != -1 &&
+            sourceIndex != targetIndex &&
+            sourceIndex != targetIndex + orientation);
+  },
+
+  drop: function(dropIndex, orientation, dataTransfer) {
+    var sourceIndex = this.getSourceIndexFromDrag(dataTransfer);
+    var sourceEngine = this._engineStore.engines[sourceIndex];
+
+    const nsITreeView = Components.interfaces.nsITreeView;
+    if (dropIndex > sourceIndex) {
+      if (orientation == nsITreeView.DROP_BEFORE)
+        dropIndex--;
+    } else {
+      if (orientation == nsITreeView.DROP_AFTER)
+        dropIndex++;
+    }
+
+    this._engineStore.moveEngine(sourceEngine, dropIndex);
+    gSearchPane.showRestoreDefaults(true);
+    gSearchPane.buildDefaultEngineDropDown();
+
+    // Redraw, and adjust selection
+    this.invalidate();
+    this.selection.select(dropIndex);
+  },
+
+  selection: null,
+  getRowProperties: function(index) { return ""; },
+  getCellProperties: function(index, column) { return ""; },
+  getColumnProperties: function(column) { return ""; },
+  isContainer: function(index) { return false; },
+  isContainerOpen: function(index) { return false; },
+  isContainerEmpty: function(index) { return false; },
+  isSeparator: function(index) { return false; },
+  isSorted: function(index) { return false; },
+  getParentIndex: function(index) { return -1; },
+  hasNextSibling: function(parentIndex, index) { return false; },
+  getLevel: function(index) { return 0; },
+  getProgressMode: function(index, column) { },
+  getCellValue: function(index, column) {
+    if (column.id == "engineShown")
+      return this._engineStore.engines[index].shown;
+    return undefined;
+  },
+  toggleOpenState: function(index) { },
+  cycleHeader: function(column) { },
+  selectionChanged: function() { },
+  cycleCell: function(row, column) { },
+  isEditable: function(index, column) { return column.id != "engineName"; },
+  isSelectable: function(index, column) { return false; },
+  setCellValue: function(index, column, value) {
+    if (column.id == "engineShown") {
+      this._engineStore.engines[index].shown = value == "true";
+      gEngineView.invalidate();
+      gSearchPane.saveOneClickEnginesList();
+    }
+  },
+  setCellText: function(index, column, value) {
+    if (column.id == "engineKeyword") {
+      if (!gSearchPane.editKeyword(this._engineStore.engines[index], value)) {
+        setTimeout(() => {
+          document.getElementById("engineList").startEditing(index, column);
+        }, 0);
+      }
+    }
+  },
+  performAction: function(action) { },
+  performActionOnRow: function(action, index) { },
+  performActionOnCell: function(action, index, column) { }
+};
--- a/browser/components/preferences/in-content/search.xul
+++ b/browser/components/preferences/in-content/search.xul
@@ -34,14 +34,40 @@
                 accesskey="&provideSearchSuggestions.accesskey;"
                 preference="browser.search.suggest.enabled"/>
     </groupbox>
 
     <groupbox id="oneClickSearchProvidersGroup" data-category="paneSearch">
       <caption label="&oneClickSearchEngines.label;"/>
       <label>&chooseWhichOneToDisplay.label;</label>
 
-      <richlistbox id="oneClickProvidersList"/>
-      <hbox pack="end">
+      <tree id="engineList" flex="1" rows="8" hidecolumnpicker="true" editable="true"
+            seltype="single" onselect="gSearchPane.onTreeSelect();"
+            onkeypress="gSearchPane.onTreeKeyPress(event);">
+        <treechildren id="engineChildren" flex="1"
+                      ondragstart="onDragEngineStart(event);"/>
+        <treecols>
+          <treecol id="engineShown" type="checkbox" style="min-width: 26px;" editable="true"/>
+          <treecol id="engineName" flex="4" label="&engineNameColumn.label;"/>
+          <treecol id="engineKeyword" flex="1" label="&engineKeywordColumn.label;" editable="true"/>
+        </treecols>
+      </tree>
+
+      <hbox>
+        <button id="restoreDefaultSearchEngines"
+                label="&restoreDefaultSearchEngines.label;"
+                accesskey="&restoreDefaultSearchEngines.accesskey;"
+                oncommand="gSearchPane.onRestoreDefaults();"/>
+        <spacer flex="1"/>
+        <button id="removeEngineButton"
+                label="&removeEngine.label;"
+                accesskey="&removeEngine.accesskey;"
+                disabled="true"
+                oncommand="gSearchPane.remove();"/>
+      </hbox>
+
+      <separator class="thin"/>
+
+      <hbox pack="start" style="margin-bottom: 1em">
         <label id="addEngines" class="text-link" value="&addMoreSearchEngines.label;"
                onclick="if (event.button == 0) { Services.wm.getMostRecentWindow('navigator:browser').BrowserSearch.loadAddEngines(); }"/>
       </hbox>
     </groupbox>
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -161,16 +161,19 @@ browser.jar:
   skin/classic/browser/preferences/Options-sync.png   (preferences/Options-sync.png)
 #endif
 * skin/classic/browser/preferences/preferences.css    (preferences/preferences.css)
 * skin/classic/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
 * skin/classic/browser/preferences/in-content/dialog.css      (preferences/in-content/dialog.css)
   skin/classic/browser/preferences/in-content/favicon.ico     (../shared/incontentprefs/favicon.ico)
   skin/classic/browser/preferences/in-content/icons.png       (../shared/incontentprefs/icons.png)
   skin/classic/browser/preferences/in-content/icons@2x.png    (../shared/incontentprefs/icons@2x.png)
+  skin/classic/browser/preferences/in-content/search.css      (../shared/incontentprefs/search.css)
+  skin/classic/browser/preferences/in-content/check.png       (../shared/incontentprefs/check.png)
+  skin/classic/browser/preferences/in-content/check@2x.png    (../shared/incontentprefs/check@2x.png)
   skin/classic/browser/preferences/applications.css   (preferences/applications.css)
   skin/classic/browser/preferences/aboutPermissions.css (preferences/aboutPermissions.css)
   skin/classic/browser/preferences/search.css         (preferences/search.css)
   skin/classic/browser/social/services-16.png         (social/services-16.png)
   skin/classic/browser/social/services-64.png         (social/services-64.png)
   skin/classic/browser/social/share-button.png        (social/share-button.png)
   skin/classic/browser/social/share-button-active.png (social/share-button-active.png)
   skin/classic/browser/social/chat-icons.svg          (../shared/social/chat-icons.svg)
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -266,16 +266,19 @@ browser.jar:
 #endif
   skin/classic/browser/preferences/saveFile.png             (preferences/saveFile.png)
 * skin/classic/browser/preferences/preferences.css          (preferences/preferences.css)
 * skin/classic/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
 * skin/classic/browser/preferences/in-content/dialog.css      (preferences/in-content/dialog.css)
   skin/classic/browser/preferences/in-content/favicon.ico     (../shared/incontentprefs/favicon.ico)
   skin/classic/browser/preferences/in-content/icons.png       (../shared/incontentprefs/icons.png)
   skin/classic/browser/preferences/in-content/icons@2x.png    (../shared/incontentprefs/icons@2x.png)
+  skin/classic/browser/preferences/in-content/search.css      (../shared/incontentprefs/search.css)
+  skin/classic/browser/preferences/in-content/check.png       (../shared/incontentprefs/check.png)
+  skin/classic/browser/preferences/in-content/check@2x.png    (../shared/incontentprefs/check@2x.png)
   skin/classic/browser/preferences/applications.css         (preferences/applications.css)
   skin/classic/browser/preferences/aboutPermissions.css     (preferences/aboutPermissions.css)
   skin/classic/browser/preferences/search.css               (preferences/search.css)
   skin/classic/browser/preferences/checkbox.png             (preferences/checkbox.png)
   skin/classic/browser/preferences/checkbox@2x.png          (preferences/checkbox@2x.png)
   skin/classic/browser/yosemite/preferences/checkbox.png    (preferences/checkbox-yosemite.png)
   skin/classic/browser/yosemite/preferences/checkbox@2x.png (preferences/checkbox-yosemite@2x.png)
   skin/classic/browser/social/services-16.png               (social/services-16.png)
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..98c89d66e628dcf852fbe1774363c0efff8876ff
GIT binary patch
literal 288
zc$@(s0pI?KP)<h;3K|Lk000e1NJLTq000aC000aK1^@s6R&`wG0002zNkl<Zc-muN
zAOfiMU659qd|{o`%rjiXsAo``d~vJNqznI*Cta9KP_go)i*X>uK>R~t@+A$tisk#y
zNCFkU18bOkp#m<UG3BH%HVulCE^dIDe%7I3KMTZt6VIyx1s(w<t5DoM>0%^Q@h`;*
zm-P^)OuWzpmH4kb@q9ek-P13M0S$Nq6>mhfchxFprHL1IK_!1HOupc*H0k_mDF3o{
z!&Np6x5`bsBnVV=2dd~Nl>VbM;k+r%5K)<OK^v&(Gu(NT&vz3J^U3D}fQtVC@hzFY
mW1Pgr>ZA*GKo8sEk_P~vZGl;O!Zy1A0000<MNUMnLSTYkmU^)O
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..3d97c886af7e2137f490f8a2588d402b725da51a
GIT binary patch
literal 471
zc$@*(0Vw{7P)<h;3K|Lk000e1NJLTq000;O000;W1^@s6;CDUv0004=Nkl<Zc-muN
zpeFeLpHX?zg_VjEE~+!o0w_<skgGK5!haxsrZn-qDpd`apL|gfXz&-P1^<;MUwEQC
z^{fijf(B^lUbx{9{150ON9qL4qzmb2hJ)}7>KH!tvMkWBPiTfeQkisuk6NzZf$n<c
zi5ESnVmL4&6VVM<oP2QsRScJ%d`=2z;CnO+p2<zSB#2X!;-rfqpcFxr>nB~<gzkEk
ziRb<CXt>x9RygrOJ7L4mM`NU#$rqO6PQw!~Oh?slF%zdRCtMZ>`t%K&1+Ub4&WK>w
zF!4edmNfhamv@1VS%VR=Kr;yC!cAB#`KB=Gq6La$CZ7+*5;W@wMi?kl02N=yV##Y*
zp#=&8P*9-Bzf+icRf4!QJ>`NLP{RjIOU~a>o_<jb6p0uaYSP6x(lXA(^IkxWf3f)F
zK4#Ed+)8fltiWOcI{v6K>7ooJ<;zNZ7AQ_SpGr|NAT{$07to*!I4syhm9j>m|GWY?
z6`>mb1td<*I@S?r=x;>OT*#q*MLy|b9@K(E#1@34r>sfmr;*!A005yCz>!?D9tr>e
N002ovPDHLkV1jR)%n1Mh
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/incontentprefs/search.css
@@ -0,0 +1,54 @@
+/* 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/. */
+
+#defaultEngine > .menulist-label-box > .menulist-icon {
+  height: 16px;
+}
+
+/* work around a display: none in Linux's menu.css, see bug 1112310 */
+.searchengine-menuitem > .menu-iconic-left {
+  display: -moz-box;
+}
+
+#engineList {
+  margin: .5em 6px;
+}
+
+#engineList treechildren::-moz-tree-image(engineShown, checked) {
+  /* Unfortunately check.svg can't be used in XUL trees. */
+  list-style-image: url("check.png");
+  -moz-margin-start: 5px;
+  width: 12px;
+  height: 12px;
+}
+
+@media (min-resolution: 2dppx) {
+  #engineList treechildren::-moz-tree-image(engineShown, checked) {
+    list-style-image: url("check@2x.png");
+  }
+}
+
+#engineList treechildren::-moz-tree-image(engineName) {
+  -moz-margin-end: 10px;
+  -moz-margin-start: 1px;
+  width: 16px;
+  height: 16px;
+}
+
+#engineList treechildren::-moz-tree-row {
+  min-height: 36px;
+}
+
+#engineList treechildren::-moz-tree-cell-text {
+  /* Override to avoid text in the selected row being white on light gray. */
+  color: -moz-FieldText;
+}
+
+#engineList treechildren::-moz-tree-drop-feedback {
+  background-color: Highlight;
+  width: 10000px; /* 100% doesn't work; 10k is hopefully larger than any window
+                     we may have, overflow isn't visible. */
+  height: 2px;
+  -moz-margin-start: 0;
+}
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -189,16 +189,19 @@ browser.jar:
 #endif
         skin/classic/browser/preferences/saveFile.png                (preferences/saveFile.png)
 *       skin/classic/browser/preferences/preferences.css             (preferences/preferences.css)
 *       skin/classic/browser/preferences/in-content/preferences.css  (preferences/in-content/preferences.css)
 *       skin/classic/browser/preferences/in-content/dialog.css       (preferences/in-content/dialog.css)
         skin/classic/browser/preferences/in-content/favicon.ico      (../shared/incontentprefs/favicon.ico)
         skin/classic/browser/preferences/in-content/icons.png        (../shared/incontentprefs/icons.png)
         skin/classic/browser/preferences/in-content/icons@2x.png     (../shared/incontentprefs/icons@2x.png)
+        skin/classic/browser/preferences/in-content/search.css       (../shared/incontentprefs/search.css)
+        skin/classic/browser/preferences/in-content/check.png        (../shared/incontentprefs/check.png)
+        skin/classic/browser/preferences/in-content/check@2x.png     (../shared/incontentprefs/check@2x.png)
         skin/classic/browser/preferences/applications.css            (preferences/applications.css)
         skin/classic/browser/preferences/aboutPermissions.css        (preferences/aboutPermissions.css)
         skin/classic/browser/preferences/search.css                  (preferences/search.css)
         skin/classic/browser/preferences/checkbox.png                (preferences/checkbox-xp.png)
         skin/classic/browser/preferences/checkbox-classic.png        (preferences/checkbox-classic.png)
         skin/classic/browser/social/services-16.png                  (social/services-16.png)
         skin/classic/browser/social/services-64.png                  (social/services-64.png)
         skin/classic/browser/social/chat-icons.svg                   (../shared/social/chat-icons.svg)
@@ -638,16 +641,19 @@ browser.jar:
 #endif
         skin/classic/aero/browser/preferences/saveFile.png           (preferences/saveFile-aero.png)
 *       skin/classic/aero/browser/preferences/preferences.css        (preferences/preferences.css)
 *       skin/classic/aero/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
 *       skin/classic/aero/browser/preferences/in-content/dialog.css      (preferences/in-content/dialog.css)
         skin/classic/aero/browser/preferences/in-content/favicon.ico     (../shared/incontentprefs/favicon.ico)
         skin/classic/aero/browser/preferences/in-content/icons.png       (../shared/incontentprefs/icons.png)
         skin/classic/aero/browser/preferences/in-content/icons@2x.png    (../shared/incontentprefs/icons@2x.png)
+        skin/classic/aero/browser/preferences/in-content/search.css      (../shared/incontentprefs/search.css)
+        skin/classic/aero/browser/preferences/in-content/check.png       (../shared/incontentprefs/check.png)
+        skin/classic/aero/browser/preferences/in-content/check@2x.png    (../shared/incontentprefs/check@2x.png)
         skin/classic/aero/browser/preferences/applications.css       (preferences/applications.css)
         skin/classic/aero/browser/preferences/aboutPermissions.css   (preferences/aboutPermissions.css)
         skin/classic/aero/browser/preferences/search.css             (preferences/search.css)
         skin/classic/aero/browser/preferences/checkbox.png           (preferences/checkbox-8.png)
         skin/classic/aero/browser/preferences/checkbox-aero.png      (preferences/checkbox-aero.png)
         skin/classic/aero/browser/preferences/checkbox-classic.png   (preferences/checkbox-classic.png)
         skin/classic/aero/browser/social/services-16.png             (social/services-16.png)
         skin/classic/aero/browser/social/services-64.png             (social/services-64.png)