Bug 910189: Part 3 - Restore default search engines (no dialog). r=margaret
☠☠ backed out by e04fb3e318fc ☠ ☠
authorChenxia Liu <liuche@mozilla.com>
Wed, 18 Dec 2013 15:21:14 -0800
changeset 161200 72a80fc289f2fd128132fdd799c3c36f85eeea1a
parent 161199 30258cd308b53567e9aa6b3c1050bfd06d61716b
child 161201 be2eafebfb4d2816c22c5b1c171f9c5b2e4a0456
push id25871
push usercbook@mozilla.com
push dateThu, 19 Dec 2013 09:34:02 +0000
treeherdermozilla-central@5c7fa2bfea8b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmargaret
bugs910189
milestone29.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 910189: Part 3 - Restore default search engines (no dialog). r=margaret
mobile/android/base/preferences/GeckoPreferenceFragment.java
mobile/android/base/preferences/GeckoPreferences.java
mobile/android/base/preferences/SearchPreferenceCategory.java
mobile/android/chrome/content/browser.js
--- a/mobile/android/base/preferences/GeckoPreferenceFragment.java
+++ b/mobile/android/base/preferences/GeckoPreferenceFragment.java
@@ -15,16 +15,17 @@ import android.preference.Preference;
 import android.preference.PreferenceActivity;
 import android.preference.PreferenceCategory;
 import android.preference.PreferenceFragment;
 import android.preference.PreferenceScreen;
 import android.os.Bundle;
 import android.util.Log;
 import android.view.Menu;
 import android.view.MenuInflater;
+import android.view.MenuItem;
 import android.view.ViewConfiguration;
 
 /* A simple implementation of PreferenceFragment for large screen devices
  * This will strip category headers (so that they aren't shown to the user twice)
  * as well as initializing Gecko prefs when a fragment is shown.
 */
 public class GeckoPreferenceFragment extends PreferenceFragment {
 
--- a/mobile/android/base/preferences/GeckoPreferences.java
+++ b/mobile/android/base/preferences/GeckoPreferences.java
@@ -72,17 +72,19 @@ public class GeckoPreferences
     private static final String NON_PREF_PREFIX = "android.not_a_preference.";
     public static final String INTENT_EXTRA_RESOURCES = "resource";
     public static String PREFS_HEALTHREPORT_UPLOAD_ENABLED = NON_PREF_PREFIX + "healthreport.uploadEnabled";
 
     private static boolean sIsCharEncodingEnabled = false;
     private boolean mInitialized = false;
     private int mPrefsRequestId = 0;
 
-    // These match keys in resources/xml/preferences.xml.in.
+    // These match keys in resources/xml*/preferences*.xml
+    private static String PREFS_SEARCH_RESTORE_DEFAULTS = NON_PREF_PREFIX + "search.restore_defaults";
+
     private static String PREFS_ANNOUNCEMENTS_ENABLED = NON_PREF_PREFIX + "privacy.announcements.enabled";
     private static String PREFS_DATA_REPORTING_PREFERENCES = NON_PREF_PREFIX + "datareporting.preferences";
     private static String PREFS_TELEMETRY_ENABLED = "datareporting.telemetry.enabled";
     private static String PREFS_CRASHREPORTER_ENABLED = "datareporting.crashreporter.submitEnabled";
     private static String PREFS_MENU_CHAR_ENCODING = "browser.menu.showCharacterEncoding";
     private static String PREFS_MP_ENABLED = "privacy.masterpassword.enabled";
     private static String PREFS_UPDATER_AUTODOWNLOAD = "app.update.autodownload";
     private static String PREFS_GEO_REPORTING = "app.geo.reportdata";
@@ -384,39 +386,64 @@ public class GeckoPreferences
                     CharSequence selectedEntry = listPref.getEntry();
                     listPref.setSummary(selectedEntry);
                     continue;
                 } else if (PREFS_SYNC.equals(key) && GeckoProfile.get(this).inGuestMode()) {
                     // Don't show sync prefs while in guest mode.
                     preferences.removePreference(pref);
                     i--;
                     continue;
+                } else if (PREFS_SEARCH_RESTORE_DEFAULTS.equals(key)) {
+                    pref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+                        @Override
+                        public boolean onPreferenceClick(Preference preference) {
+                            GeckoPreferences.this.restoreDefaultSearchEngines();
+                            return true;
+                        }
+                    });
                 }
 
                 // Some Preference UI elements are not actually preferences,
                 // but they require a key to work correctly. For example,
                 // "Clear private data" requires a key for its state to be
                 // saved when the orientation changes. It uses the
                 // "android.not_a_preference.privacy.clear" key - which doesn't
                 // exist in Gecko - to satisfy this requirement.
                 if (key != null && !key.startsWith(NON_PREF_PREFIX)) {
                     prefs.add(key);
                 }
             }
         }
     }
 
+    /**
+     * Restore default search engines in Gecko and retrigger a search engine refresh.
+     */
+    protected void restoreDefaultSearchEngines() {
+        GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("SearchEngines:RestoreDefaults", null));
+
+        // Send message to Gecko to get engines. SearchPreferenceCategory listens for the response.
+        GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("SearchEngines:GetVisible", null));
+    }
+
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
-        switch (item.getItemId()) {
+        int itemId = item.getItemId();
+        switch (itemId) {
             case android.R.id.home:
                 finish();
                 return true;
         }
 
+        // Generated R.id.* apparently aren't constant expressions, so they can't be switched.
+        if (itemId == R.id.restore_defaults) {
+            restoreDefaultSearchEngines();
+            return true;
+       }
+
         return super.onOptionsItemSelected(item);
     }
 
     final private int DIALOG_CREATE_MASTER_PASSWORD = 0;
     final private int DIALOG_REMOVE_MASTER_PASSWORD = 1;
 
     public static void setCharEncodingState(boolean enabled) {
         sIsCharEncodingEnabled = enabled;
--- a/mobile/android/base/preferences/SearchPreferenceCategory.java
+++ b/mobile/android/base/preferences/SearchPreferenceCategory.java
@@ -38,37 +38,41 @@ public class SearchPreferenceCategory ex
 
     @Override
     protected void onAttachedToActivity() {
         super.onAttachedToActivity();
 
         // Ensures default engine remains at top of list.
         setOrderingAsAdded(true);
 
-        // Request list of search engines from Gecko.
+        // Register for SearchEngines messages and request list of search engines from Gecko.
         GeckoAppShell.registerEventListener("SearchEngines:Data", this);
         GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("SearchEngines:GetVisible", null));
     }
 
     @Override
+    protected void onPrepareForRemoval() {
+        GeckoAppShell.unregisterEventListener("SearchEngines:Data", this);
+    }
+
+    @Override
     public void handleMessage(String event, final JSONObject data) {
         if (event.equals("SearchEngines:Data")) {
-            // We are no longer interested in this event from Gecko, as we do not request it again with
-            // this instance.
-            GeckoAppShell.unregisterEventListener("SearchEngines:Data", this);
-
             // Parse engines array from JSON.
             JSONArray engines;
             try {
                 engines = data.getJSONArray("searchEngines");
             } catch (JSONException e) {
                 Log.e(LOGTAG, "Unable to decode search engine data from Gecko.", e);
                 return;
             }
 
+            // Clear the preferences category from this thread.
+            this.removeAll();
+
             // Create an element in this PreferenceCategory for each engine.
             for (int i = 0; i < engines.length(); i++) {
                 try {
                     JSONObject engineJSON = engines.getJSONObject(i);
                     final String engineName = engineJSON.getString("name");
 
                     SearchEnginePreference enginePreference = new SearchEnginePreference(getContext(), this);
                     enginePreference.setSearchEngineFromJSON(engineJSON);
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -6604,18 +6604,19 @@ OverscrollController.prototype = {
 var SearchEngines = {
   _contextMenuId: null,
   PREF_SUGGEST_ENABLED: "browser.search.suggest.enabled",
   PREF_SUGGEST_PROMPTED: "browser.search.suggest.prompted",
 
   init: function init() {
     Services.obs.addObserver(this, "SearchEngines:Add", false);
     Services.obs.addObserver(this, "SearchEngines:GetVisible", false);
+    Services.obs.addObserver(this, "SearchEngines:Remove", false);
+    Services.obs.addObserver(this, "SearchEngines:RestoreDefaults", false);
     Services.obs.addObserver(this, "SearchEngines:SetDefault", false);
-    Services.obs.addObserver(this, "SearchEngines:Remove", false);
 
     let filter = {
       matches: function (aElement) {
         // Copied from body of isTargetAKeywordField function in nsContextMenu.js
         if(!(aElement instanceof HTMLInputElement))
           return false;
         let form = aElement.form;
         if (!form || aElement.type == "password")
@@ -6646,18 +6647,19 @@ var SearchEngines = {
         SearchEngines.addEngine(aElement);
       }
     }
   },
 
   uninit: function uninit() {
     Services.obs.removeObserver(this, "SearchEngines:Add");
     Services.obs.removeObserver(this, "SearchEngines:GetVisible");
+    Services.obs.removeObserver(this, "SearchEngines:Remove");
+    Services.obs.removeObserver(this, "SearchEngines:RestoreDefaults");
     Services.obs.removeObserver(this, "SearchEngines:SetDefault");
-    Services.obs.removeObserver(this, "SearchEngines:Remove");
     if (this._contextMenuId != null)
       NativeWindow.contextmenus.remove(this._contextMenuId);
   },
 
   // Fetch list of search engines. all ? All engines : Visible engines only.
   _handleSearchEnginesGetVisible: function _handleSearchEnginesGetVisible(rv) {
     if (!Components.isSuccessCode(rv)) {
       Cu.reportError("Could not initialize search service, bailing out.");
@@ -6717,29 +6719,34 @@ var SearchEngines = {
     let engine;
     switch(aTopic) {
       case "SearchEngines:Add":
         this.displaySearchEnginesList(aData);
         break;
       case "SearchEngines:GetVisible":
         Services.search.init(this._handleSearchEnginesGetVisible.bind(this));
         break;
-      case "SearchEngines:SetDefault":
-        engine = this._extractEngineFromJSON(aData);
-        // Move the new default search engine to the top of the search engine list.
-        Services.search.moveEngine(engine, 0);
-        Services.search.defaultEngine = engine;
-        break;
       case "SearchEngines:Remove":
         // Make sure the engine isn't hidden before removing it, to make sure it's
         // visible if the user later re-adds it (works around bug 341833)
         engine = this._extractEngineFromJSON(aData);
         engine.hidden = false;
         Services.search.removeEngine(engine);
         break;
+      case "SearchEngines:RestoreDefaults":
+        // Un-hides all default engines.
+        Services.search.restoreDefaultEngines();
+        break;
+      case "SearchEngines:SetDefault":
+        engine = this._extractEngineFromJSON(aData);
+        // Move the new default search engine to the top of the search engine list.
+        Services.search.moveEngine(engine, 0);
+        Services.search.defaultEngine = engine;
+        break;
+
       default:
         dump("Unexpected message type observed: " + aTopic);
         break;
     }
   },
 
   // Display context menu listing names of the search engines available to be added.
   displaySearchEnginesList: function displaySearchEnginesList(aData) {