Bug 892113 - Dialog to manage installed search providers. r=liuche

This commit is contained in:
Chris Kitching 2013-08-02 22:00:13 -07:00
parent c5f0278790
commit 9fcfd19453
11 changed files with 419 additions and 115 deletions

View File

@ -234,6 +234,7 @@ FENNEC_JAVA_FILES = \
menu/MenuPanel.java \
menu/MenuPopup.java \
preferences/SearchPreferenceCategory.java \
preferences/SearchEnginePreference.java \
widget/AboutHome.java \
widget/AboutHomeView.java \
widget/AboutHomeSection.java \

View File

@ -90,7 +90,7 @@ public class AllPagesTab extends AwesomeBarTab implements GeckoEventListener {
mSearchEngines = new ArrayList<SearchEngine>();
registerEventListener("SearchEngines:Data");
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("SearchEngines:Get", null));
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("SearchEngines:GetVisible", null));
mHandler = new AllPagesHandler();
}

View File

@ -144,6 +144,10 @@ size. -->
<!ENTITY pref_about_firefox "About &brandShortName;">
<!ENTITY pref_vendor_faqs "FAQs">
<!ENTITY pref_vendor_feedback "Give feedback">
<!ENTITY pref_search_set_default "Set as default">
<!ENTITY pref_search_default "Default">
<!ENTITY pref_search_remove "Remove">
<!ENTITY pref_search_last_toast "You can\'t remove or disable your last search engine.">
<!ENTITY datareporting_notification_title "&brandShortName; stats &amp; data">
<!ENTITY datareporting_notification_action_long "Choose what information to share">

View File

@ -0,0 +1,229 @@
/* 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/. */
package org.mozilla.gecko.preferences;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.os.Build;
import android.preference.Preference;
import android.text.SpannableString;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;
import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.R;
import org.mozilla.gecko.gfx.BitmapUtils;
import org.mozilla.gecko.util.ThreadUtils;
/**
* Represents an element in the list of search engines on the preferences menu.
*/
public class SearchEnginePreference extends Preference {
private static final String LOGTAG = "SearchEnginePreference";
// Dimensions, in dp, of the icon to display for this engine.
public static int sIconSize;
// Indices in button array of the AlertDialog of the three buttons.
public static final int INDEX_SET_DEFAULT_BUTTON = 0;
public static final int INDEX_REMOVE_BUTTON = 1;
// Cache label to avoid repeated use of the resource system.
public final String LABEL_IS_DEFAULT;
// Specifies if this engine is configured as the default search engine.
private boolean mIsDefaultEngine;
// Specifies if this engine is one of the ones bundled with the app, which cannot be deleted.
private boolean mIsImmutableEngine;
// Dialog element labels.
private String[] mDialogItems;
// The popup displayed when this element is tapped.
private AlertDialog mDialog;
private final SearchPreferenceCategory mParentCategory;
/**
* Create a preference object to represent a search engine that is attached to category
* containingCategory.
* @param context The activity context we operate under.
* @param parentCategory The PreferenceCategory this object exists within.
* @see this.setSearchEngine
*/
public SearchEnginePreference(Context context, SearchPreferenceCategory parentCategory) {
super(context);
mParentCategory = parentCategory;
Resources res = getContext().getResources();
// Fetch the icon dimensions from the resource file.
sIconSize = res.getDimensionPixelSize(R.dimen.searchpreferences_icon_size);
setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
SearchEnginePreference sPref = (SearchEnginePreference) preference;
sPref.showDialog();
return true;
}
});
// Fetch this resource now, instead of every time we ever want to relabel a button.
LABEL_IS_DEFAULT = res.getString(R.string.pref_search_default);
// Set up default dialog items.
mDialogItems = new String[] { res.getString(R.string.pref_search_set_default),
res.getString(R.string.pref_search_remove) };
}
/**
* Configure this Preference object from the Gecko search engine JSON object.
* @param geckoEngineJSON The Gecko-formatted JSON object representing the search engine.
* @throws JSONException If the JSONObject is invalid.
*/
public void setSearchEngineFromJSON(JSONObject geckoEngineJSON) throws JSONException {
final String engineName = geckoEngineJSON.getString("name");
SpannableString titleSpannable = new SpannableString(engineName);
mIsImmutableEngine = geckoEngineJSON.getBoolean("immutable");
if (mIsImmutableEngine) {
// Delete the "Remove" option from the menu.
mDialogItems = new String[] { getContext().getResources().getString(R.string.pref_search_set_default) };
}
setTitle(titleSpannable);
// setIcon is only available on Honeycomb and up.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
// Create a drawable from the iconURI and assign it to this Preference for display.
String iconURI = geckoEngineJSON.getString("iconURI");
Bitmap iconBitmap = BitmapUtils.getBitmapFromDataURI(iconURI);
Bitmap scaledIconBitmap = Bitmap.createScaledBitmap(iconBitmap, sIconSize, sIconSize, false);
BitmapDrawable drawable = new BitmapDrawable(scaledIconBitmap);
setIcon(drawable);
}
}
/**
* Set if this object's UI should show that this is the default engine.
* @param isDefault Flag indicating if this represents the default engine.
*/
public void setIsDefaultEngine(boolean isDefault) {
mIsDefaultEngine = isDefault;
if (isDefault) {
setOrder(0);
setSummary(LABEL_IS_DEFAULT);
} else {
setOrder(1);
setSummary("");
}
}
/**
* Display the AlertDialog providing options to reconfigure this search engine. Sets an event
* listener to disable buttons in the dialog as appropriate after they have been constructed by
* Android.
* @see this.configureShownDialog
* @see this.hideDialog
*/
public void showDialog() {
// If we are the only engine left, then we are the default engine, and none of the options
// on this menu can do anything.
if (mParentCategory.getPreferenceCount() == 1) {
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getContext(), R.string.pref_search_last_toast, Toast.LENGTH_SHORT).show();
}
});
return;
}
// If we are both default and immutable, we have no enabled items to show on the menu - abort.
if (mIsDefaultEngine && mIsImmutableEngine) {
return;
}
final AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
builder.setTitle(getTitle().toString());
builder.setItems(mDialogItems, new DialogInterface.OnClickListener() {
// Forward the various events that we care about to the container class for handling.
@Override
public void onClick(DialogInterface dialog, int indexClicked) {
hideDialog();
switch (indexClicked) {
case INDEX_SET_DEFAULT_BUTTON:
mParentCategory.setDefault(SearchEnginePreference.this);
break;
case INDEX_REMOVE_BUTTON:
mParentCategory.uninstall(SearchEnginePreference.this);
break;
default:
Log.w(LOGTAG, "Selected index out of range.");
break;
}
}
});
// Copy the icon, if any, from this object to the prompt we produce.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
builder.setIcon(getIcon());
}
// We have to construct the dialog itself on the UI thread.
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
mDialog = builder.create();
mDialog.setOnShowListener(new DialogInterface.OnShowListener() {
// Called when the dialog is shown (so we're finally able to manipulate button enabledness).
@Override
public void onShow(DialogInterface dialog) {
configureShownDialog();
}
});
mDialog.show();
}
});
}
/**
* Hide the dialog we previously created, if any.
*/
public void hideDialog() {
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
// Null check so we can chain engine-mutating methods up in SearchPreferenceCategory
// without consequence.
if (mDialog != null && mDialog.isShowing()) {
mDialog.dismiss();
}
}
});
}
/**
* Disables buttons in the shown AlertDialog as required. The button elements are not created
* until after we call show, so this method has to be called from the onShowListener above.
* @see this.showDialog
*/
private void configureShownDialog() {
// If we are the default engine, disable the "Set as default" button.
TextView defaultButton = (TextView) mDialog.getListView().getChildAt(INDEX_SET_DEFAULT_BUTTON);
// Disable "Set as default" button if we are already the default.
if (mIsDefaultEngine) {
defaultButton.setEnabled(false);
// Failure to unregister this listener leads to tapping the button dismissing the dialog
// without doing anything.
defaultButton.setOnClickListener(null);
}
}
}

View File

@ -1,9 +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/. */
package org.mozilla.gecko.preferences;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.os.Build;
import android.preference.Preference;
import android.preference.PreferenceCategory;
import android.util.AttributeSet;
@ -13,38 +14,36 @@ import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoEvent;
import org.mozilla.gecko.R;
import org.mozilla.gecko.gfx.BitmapUtils;
import org.mozilla.gecko.util.GeckoEventListener;
public class SearchPreferenceCategory extends PreferenceCategory implements GeckoEventListener {
public static final String LOGTAG = "SearchPrefCategory";
private static int sIconSize;
private SearchEnginePreference mDefaultEngineReference;
// These seemingly redundant constructors are mandated by the Android system, else it fails to
// inflate this object.
public SearchPreferenceCategory(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public SearchPreferenceCategory(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public SearchPreferenceCategory(Context context) {
super(context);
init();
}
private void init() {
sIconSize = getContext().getResources().getDimensionPixelSize(R.dimen.searchpreferences_icon_size);
}
@Override
protected void onAttachedToActivity() {
super.onAttachedToActivity();
// Request list of search engines from Gecko
// Ensures default engine remains at top of list.
setOrderingAsAdded(false);
// Request list of search engines from Gecko.
GeckoAppShell.registerEventListener("SearchEngines:Data", this);
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("SearchEngines:Get", null));
}
@ -52,9 +51,17 @@ public class SearchPreferenceCategory extends PreferenceCategory implements Geck
@Override
public void handleMessage(String event, final JSONObject data) {
if (event.equals("SearchEngines:Data")) {
// Parse engines array from JSON. The first element in the array is the default engine.
JSONArray engines;
JSONObject defaultEngine;
final String defaultEngineName;
try {
engines = data.getJSONArray("searchEngines");
if (engines.length() == 0) {
return;
}
defaultEngine = engines.getJSONObject(0);
defaultEngineName = defaultEngine.getString("name");
} catch (JSONException e) {
Log.e(LOGTAG, "Unable to decode search engine data from Gecko.", e);
return;
@ -66,24 +73,90 @@ public class SearchPreferenceCategory extends PreferenceCategory implements Geck
JSONObject engineJSON = engines.getJSONObject(i);
final String engineName = engineJSON.getString("name");
Preference engine = new Preference(getContext());
engine.setTitle(engineName);
engine.setKey(engineName);
// The setIcon feature is not available prior to API 11.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
String iconURI = engineJSON.getString("iconURI");
Bitmap iconBitmap = BitmapUtils.getBitmapFromDataURI(iconURI);
Bitmap scaledIconBitmap = Bitmap.createScaledBitmap(iconBitmap, sIconSize, sIconSize, false);
BitmapDrawable drawable = new BitmapDrawable(scaledIconBitmap);
engine.setIcon(drawable);
SearchEnginePreference enginePreference = new SearchEnginePreference(getContext(), this);
enginePreference.setSearchEngineFromJSON(engineJSON);
if (engineName.equals(defaultEngineName)) {
// We set this here, not in setSearchEngineFromJSON, because it allows us to
// keep a reference to the default engine to use when the AlertDialog
// callbacks are used.
enginePreference.setIsDefaultEngine(true);
mDefaultEngineReference = enginePreference;
}
addPreference(engine);
// TODO: Bug 892113 - Add event listener here for tapping on each element. Produce a dialog to provide options.
enginePreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
SearchEnginePreference sPref = (SearchEnginePreference) preference;
// Display the configuration dialog associated with the tapped engine.
sPref.showDialog();
return true;
}
});
addPreference(enginePreference);
} catch (JSONException e) {
Log.e(LOGTAG, "JSONException parsing engine at index " + i, e);
}
}
}
// 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);
}
/**
* Set the default engine to any available engine. Used if the current default is removed or
* disabled.
*/
private void setFallbackDefaultEngine() {
if (getPreferenceCount() > 0) {
SearchEnginePreference aEngine = (SearchEnginePreference) getPreference(0);
setDefault(aEngine);
}
}
/**
* Helper method to send a particular event string to Gecko with an associated engine name.
* @param event The type of event to send.
* @param engine The engine to which the event relates.
*/
private void sendGeckoEngineEvent(String event, SearchEnginePreference engine) {
JSONObject json = new JSONObject();
try {
json.put("engine", engine.getTitle());
} catch (JSONException e) {
Log.e(LOGTAG, "JSONException creating search engine configuration change message for Gecko.", e);
return;
}
GeckoAppShell.notifyGeckoOfEvent(GeckoEvent.createBroadcastEvent(event, json.toString()));
}
// Methods called by tapping items on the submenus for each search engine are below.
/**
* Removes the given engine from the set of available engines.
* @param engine The engine to remove.
*/
public void uninstall(SearchEnginePreference engine) {
removePreference(engine);
if (engine == mDefaultEngineReference) {
// If they're deleting their default engine, get them a new default engine.
setFallbackDefaultEngine();
}
sendGeckoEngineEvent("SearchEngines:Remove", engine);
}
/**
* Sets the given engine as the current default engine.
* @param engine The intended new default engine.
*/
public void setDefault(SearchEnginePreference engine) {
engine.setIsDefaultEngine(true);
mDefaultEngineReference.setIsDefaultEngine(false);
mDefaultEngineReference = engine;
sendGeckoEngineEvent("SearchEngines:SetDefault", engine);
}
}

View File

@ -151,6 +151,12 @@
<string name="pref_vendor_faqs">&pref_vendor_faqs;</string>
<string name="pref_vendor_feedback">&pref_vendor_feedback;</string>
<!-- Strings used in default search provider config preferences menu -->
<string name="pref_search_set_default">&pref_search_set_default;</string>
<string name="pref_search_default">&pref_search_default;</string>
<string name="pref_search_remove">&pref_search_remove;</string>
<string name="pref_search_last_toast">&pref_search_last_toast;</string>
<string name="datareporting_notification_title">&datareporting_notification_title;</string>
<string name="datareporting_notification_action_long">&datareporting_notification_action_long;</string>
<string name="datareporting_notification_action">&datareporting_notification_action;</string>

View File

@ -61,12 +61,7 @@ var ContextMenus = {
document.getElementById("contextmenu-disable").setAttribute("hidden", "true");
}
// Only show the "Set as Default" menuitem for enabled non-default search engines.
if (addon.type == "search" && enabled && addon.id != Services.search.defaultEngine.name) {
document.getElementById("contextmenu-default").removeAttribute("hidden");
} else {
document.getElementById("contextmenu-default").setAttribute("hidden", "true");
}
document.getElementById("contextmenu-default").setAttribute("hidden", "true");
},
enable: function(event) {
@ -82,17 +77,11 @@ var ContextMenus = {
uninstall: function (event) {
Addons.uninstall(this.target.addon);
this.target = null;
},
setDefaultSearch: function(event) {
Addons.setDefaultSearch(this.target.addon);
this.target = null;
}
}
function init() {
window.addEventListener("popstate", onPopState, false);
Services.obs.addObserver(Addons, "browser-search-engine-modified", false);
AddonManager.addInstallListener(Addons);
AddonManager.addAddonListener(Addons);
@ -102,7 +91,6 @@ function init() {
}
function uninit() {
Services.obs.removeObserver(Addons, "browser-search-engine-modified");
AddonManager.removeInstallListener(Addons);
AddonManager.removeAddonListener(Addons);
}
@ -361,16 +349,7 @@ var Addons = {
uninstallBtn.removeAttribute("disabled");
let defaultButton = document.getElementById("default-btn");
if (addon.type == "search") {
if (addon.id == Services.search.defaultEngine.name)
defaultButton.setAttribute("disabled", "true");
else
defaultButton.removeAttribute("disabled");
defaultButton.removeAttribute("hidden");
} else {
defaultButton.setAttribute("hidden", "true");
}
defaultButton.setAttribute("hidden", "true");
let box = document.querySelector("#addons-details > .addon-item .options-box");
box.innerHTML = "";
@ -424,10 +403,7 @@ var Addons = {
let listItem = this._getElementForAddon(addon.id);
let opType;
if (addon.type == "search") {
addon.engine.hidden = !aValue;
opType = aValue ? "needs-enable" : "needs-disable";
} else if (addon.type == "theme") {
if (addon.type == "theme") {
if (aValue) {
// We can have only one theme enabled, so disable the current one if any
let list = document.getElementById("addons-list");
@ -492,30 +468,21 @@ var Addons = {
let listItem = this._getElementForAddon(addon.id);
if (addon.type == "search") {
// 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)
addon.engine.hidden = false;
Services.search.removeEngine(addon.engine);
// the search-engine-modified observer will take care of updating the list
history.back();
addon.uninstall();
if (addon.pendingOperations & AddonManager.PENDING_UNINSTALL) {
this.showRestart();
// A disabled addon doesn't need a restart so it has no pending ops and
// can't be cancelled
let opType = this._getOpTypeForOperations(addon.pendingOperations);
if (!addon.isActive && opType == "")
opType = "needs-uninstall";
detailItem.setAttribute("opType", opType);
listItem.setAttribute("opType", opType);
} else {
addon.uninstall();
if (addon.pendingOperations & AddonManager.PENDING_UNINSTALL) {
this.showRestart();
// A disabled addon doesn't need a restart so it has no pending ops and
// can't be cancelled
let opType = this._getOpTypeForOperations(addon.pendingOperations);
if (!addon.isActive && opType == "")
opType = "needs-uninstall";
detailItem.setAttribute("opType", opType);
listItem.setAttribute("opType", opType);
} else {
list.removeChild(listItem);
history.back();
}
list.removeChild(listItem);
history.back();
}
},
@ -535,20 +502,6 @@ var Addons = {
listItem.setAttribute("opType", opType);
},
setDefaultSearch: function setDefaultSearch(aAddon) {
let addon = aAddon || document.querySelector("#addons-details > .addon-item").addon;
if (addon.type != "search")
return;
let engine = Services.search.getEngineByName(addon.id);
// Move the new default search engine to the top of the search engine list.
Services.search.moveEngine(engine, 0);
Services.search.defaultEngine = engine;
document.getElementById("default-btn").setAttribute("disabled", "true");
},
showRestart: function showRestart() {
this._restartCount++;
gChromeWin.XPInstallObserver.showRestartPrompt();
@ -590,18 +543,6 @@ var Addons = {
element.setAttribute("opType", "needs-restart");
},
observe: function observe(aSubject, aTopic, aData) {
if (aTopic == "browser-search-engine-modified") {
switch (aData) {
case "engine-added":
case "engine-removed":
case "engine-changed":
this.getAddons();
break;
}
}
},
onInstallFailed: function(aInstall) {
},

View File

@ -57,7 +57,6 @@
<menuitem id="contextmenu-enable" label="&addonAction.enable;" onclick="ContextMenus.enable();"></menuitem>
<menuitem id="contextmenu-disable" label="&addonAction.disable;" onclick="ContextMenus.disable();"></menuitem>
<menuitem id="contextmenu-uninstall" label="&addonAction.uninstall;" onclick="ContextMenus.uninstall();"></menuitem>
<menuitem id="contextmenu-default" label="&addonAction.setDefault;" onclick="ContextMenus.setDefaultSearch();"></menuitem>
</menu>
<div id="addons-header" class="header">
@ -84,7 +83,6 @@
<button id="disable-btn" class="show-on-enable hide-on-disable hide-on-uninstall" onclick="Addons.disable();">&addonAction.disable;</button>
<button id="uninstall-btn" class="hide-on-uninstall" onclick="Addons.uninstall();">&addonAction.uninstall;</button>
<button id="cancel-btn" class="show-on-uninstall" onclick="Addons.cancelUninstall();">&addonAction.undo;</button>
<button id="default-btn" class="show-on-enable hide-on-disable hide-on-uninstall" onclick="Addons.setDefaultSearch();">&addonAction.setDefault;</button>
</div>
<div class="options-header">&aboutAddons.options;</div>
<div class="options-box"></div>

View File

@ -6421,6 +6421,9 @@ var SearchEngines = {
init: function init() {
Services.obs.addObserver(this, "SearchEngines:Get", false);
Services.obs.addObserver(this, "SearchEngines:GetVisible", false);
Services.obs.addObserver(this, "SearchEngines:SetDefault", false);
Services.obs.addObserver(this, "SearchEngines:Remove", false);
let contextName = Strings.browser.GetStringFromName("contextmenu.addSearchEngine");
let filter = {
matches: function (aElement) {
@ -6432,21 +6435,36 @@ var SearchEngines = {
uninit: function uninit() {
Services.obs.removeObserver(this, "SearchEngines:Get");
Services.obs.removeObserver(this, "SearchEngines:GetVisible");
Services.obs.removeObserver(this, "SearchEngines:SetDefault");
Services.obs.removeObserver(this, "SearchEngines:Remove");
if (this._contextMenuId != null)
NativeWindow.contextmenus.remove(this._contextMenuId);
},
_handleSearchEnginesGet: function _handleSearchEnginesGet(rv) {
// Fetch list of search engines. all ? All engines : Visible engines only.
_handleSearchEnginesGet: function _handleSearchEnginesGet(rv, all) {
if (!Components.isSuccessCode(rv)) {
Cu.reportError("Could not initialize search service, bailing out.");
return;
}
let engineData = Services.search.getVisibleEngines({});
let engineData;
if (all) {
engineData = Services.search.getEngines({});
} else {
engineData = Services.search.getVisibleEngines({});
}
// These engines are the bundled ones - they may not be uninstalled.
let immutableEngines = Services.search.getDefaultEngines();
let searchEngines = engineData.map(function (engine) {
return {
name: engine.name,
identifier: engine.identifier,
iconURI: (engine.iconURI ? engine.iconURI.spec : null)
iconURI: (engine.iconURI ? engine.iconURI.spec : null),
hidden: engine.hidden,
immutable: immutableEngines.indexOf(engine) != -1
};
});
@ -6458,6 +6476,8 @@ var SearchEngines = {
suggestTemplate = engine.getSubmission("__searchTerms__", "application/x-suggestions+json").uri.spec;
}
// By convention, the currently configured default engine is at position zero in searchEngines.
sendMessageToJava({
type: "SearchEngines:Data",
searchEngines: searchEngines,
@ -6470,9 +6490,45 @@ var SearchEngines = {
});
},
_handleSearchEnginesGetAll: function _handleSearchEnginesGetAll(rv) {
this._handleSearchEnginesGet(rv, true);
},
_handleSearchEnginesGetVisible: function _handleSearchEnginesGetVisible(rv) {
this._handleSearchEnginesGet(rv, false)
},
// Helper method to extract the engine name from a JSON. Simplifies the observe function.
_extractEngineFromJSON: function _extractEngineFromJSON(aData) {
let data = JSON.parse(aData);
return Services.search.getEngineByName(data.engine);
},
observe: function observe(aSubject, aTopic, aData) {
if (aTopic == "SearchEngines:Get") {
Services.search.init(this._handleSearchEnginesGet.bind(this));
let engine;
switch(aTopic) {
case "SearchEngines:GetVisible":
Services.search.init(this._handleSearchEnginesGetVisible.bind(this));
break;
case "SearchEngines:Get":
// Return a list of all engines, including "Hidden" ones.
Services.search.init(this._handleSearchEnginesGetAll.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;
default:
dump("Unexpected message type observed: " + aTopic);
break;
}
},

View File

@ -10,4 +10,3 @@
<!ENTITY addonAction.disable "Disable">
<!ENTITY addonAction.uninstall "Uninstall">
<!ENTITY addonAction.undo "Undo">
<!ENTITY addonAction.setDefault "Set as Default">

View File

@ -2,12 +2,9 @@
# 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/.
addonsSearchEngine.description=Integrated Search
addonType.extension=Extension
addonType.theme=Theme
addonType.locale=Locale
addonType.search=Search
addonStatus.uninstalled=%S will be uninstalled after restart.