Bug 474480: Update the Add-ons Manager UI, r=dtownsend, r=gavin

This commit is contained in:
Mark Finkle 2009-04-22 10:19:48 -04:00
parent d6f657b4e6
commit 257ff33541
14 changed files with 985 additions and 100 deletions

View File

@ -0,0 +1,135 @@
<?xml version="1.0"?>
<!DOCTYPE bindings [
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
%browserDTD;
]>
<bindings
xmlns="http://www.mozilla.org/xbl"
xmlns:xbl="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<binding id="extension-local" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
<content orient="vertical">
<xul:hbox align="start">
<xul:image xbl:inherits="src=iconURL"/>
<xul:vbox flex="1">
<xul:hbox align="center">
<xul:label class="title" xbl:inherits="value=name"/>
<xul:label class="normal" xbl:inherits="value=version"/>
</xul:hbox>
<xul:label class="normal hide-on-select" xbl:inherits="value=description" crop="end" flex="1"/>
<xul:description class="normal show-on-select" xbl:inherits="xbl:text=description" flex="1"/>
</xul:vbox>
</xul:hbox>
<xul:hbox class="show-on-select">
<xul:button anonid="options-button" class="addon-options" label="&addonOptions.label;"
oncommand="document.getBindingParent(this).toggleOptions();"/>
<xul:spacer flex="1"/>
<xul:button anonid="enable-button" class="show-on-disable hide-on-enable hide-on-uninstall addon-enable" label="&addonEnable.label;"
oncommand="ExtensionsView.enable(document.getBindingParent(this));"/>
<xul:button class="show-on-enable hide-on-disable hide-on-uninstall addon-disable" label="&addonDisable.label;"
oncommand="ExtensionsView.disable(document.getBindingParent(this));"/>
<xul:button class="hide-on-uninstall addon-uninstall" label="&addonUninstall.label;"
oncommand="ExtensionsView.uninstall(document.getBindingParent(this));"/>
<xul:button class="show-on-uninstall addon-cancel" label="&addonCancel.label;"
oncommand="ExtensionsView.cancelUninstall(document.getBindingParent(this));"/>
</xul:hbox>
<xul:vbox anonid="options-box" collapsed="true" flex="1">
<xul:iframe anonid="options-frame" src="" flex="1"/>
</xul:vbox>
</content>
<implementation>
<constructor>
<![CDATA[
let isDisabled = this.getAttribute("isDisabled");
let optionsURL = this.getAttribute("optionsURL");
if (optionsURL == "" || isDisabled == "true")
document.getAnonymousElementByAttribute(this, "anonid", "options-button").setAttribute("disabled", "true");
let appDisabled = this.getAttribute("appDisabled");
if (appDisabled == "true")
document.getAnonymousElementByAttribute(this, "anonid", "enable-button").setAttribute("disabled", "true");
this._optionsHandler.element = this;
this._optionsHandler.box = document.getAnonymousElementByAttribute(this, "anonid", "options-box");
this._optionsHandler.frame = document.getAnonymousElementByAttribute(this, "anonid", "options-frame");
]]>
</constructor>
<field name="_optionsHandler">
<![CDATA[ ({
element: null,
box: null,
frame: null,
handleEvent: function op_handleEvent(aEvent) {
if (aEvent.type == "load") {
// Try to determine the natural height of the iframe content
// so we can resize the iframe and all the content is visible
let height = 0;
let root = this.frame.contentDocument.documentElement;
for (let i = 0; i < root.children.length; ++i) {
height += root.children[i].clientHeight;
}
this.box.height = height;
}
}
}) ]]>
</field>
<method name="toggleOptions">
<body>
<![CDATA[
let optionsURL = this.getAttribute("optionsURL");
if (optionsURL == "")
return;
this._optionsHandler.box.collapsed = !box.collapsed;
if (this._optionsHandler.frame.getAttribute("src") == "") {
this._optionsHandler.frame.addEventListener("load", this._optionsHandler, true);
this._optionsHandler.frame.setAttribute("src", optionsURL);
}
]]>
</body>
</method>
</implementation>
</binding>
<binding id="extension-search" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
<content orient="horizontal" align="start">
<xul:image xbl:inherits="src=iconURL"/>
<xul:vbox flex="1">
<xul:hbox align="center">
<xul:label class="title" xbl:inherits="value=name"/>
<xul:label class="normal" xbl:inherits="value=version"/>
</xul:hbox>
<xul:label class="normal hide-on-select" xbl:inherits="value=description" crop="end" flex="1"/>
<xul:hbox class="show-on-select" align="start">
<xul:image xbl:inherits="src=thumbnailURL"/>
<xul:description class="normal show-on-select" xbl:inherits="xbl:text=description" flex="1"/>
</xul:hbox>
<xul:hbox class="show-on-select">
<xul:spacer flex="1"/>
<xul:button class="addon-install hide-on-install hide-on-restart" label="&addonInstall.label;"
oncommand="ExtensionsView.installFromRepo(document.getBindingParent(this));"/>
</xul:hbox>
<xul:progressmeter class="show-on-install" mode="normal" value="0" xbl:inherits="value=progress"/>
</xul:vbox>
</content>
</binding>
<binding id="extension-message" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
<content orient="vertical" align="center">
<xul:hbox>
<xul:image src="chrome://browser/skin/images/throbber.png" xbl:inherits="hidden=hidethrobber"/>
<xul:label class="normal" xbl:inherits="value=message"/>
</xul:hbox>
<xul:button xbl:inherits="label=button" oncommand="ExtensionsView.resetSearch();"/>
</content>
</binding>
</bindings>

View File

@ -454,9 +454,7 @@ var BrowserUI = {
panelUI.width = container.boxObject.width;
panelUI.height = container.boxObject.height;
let addons = document.getElementById("addons-container");
if (!addons.hasAttribute("src"))
addons.setAttribute("src", "chrome://mozapps/content/extensions/extensions.xul");
ExtensionsView.init();
let dloads = document.getElementById("downloads-container");
if (!dloads.hasAttribute("src"))
dloads.setAttribute("src", "chrome://mozapps/content/downloads/downloads.xul");

View File

@ -66,3 +66,87 @@ placelabel {
checkbox {
-moz-binding: url("chrome://browser/content/checkbox.xml#checkbox-radio");
}
richlistitem[typeName="local"] {
-moz-binding: url("chrome://browser/content/bindings/extensions.xml#extension-local");
}
richlistitem[typeName="search"] {
-moz-binding: url("chrome://browser/content/bindings/extensions.xml#extension-search");
}
richlistitem[typeName="message"] {
-moz-binding: url("chrome://browser/content/bindings/extensions.xml#extension-message");
}
/* richlist defaults ------------------------------------------------------- */
richlistitem {
padding: 5px;
border-bottom: 1px solid rgb(207,207,207);
}
richlistitem label.title,
richlistitem description.title {
font-size: 80% !important;
}
richlistitem label.normal,
richlistitem description.normal {
color: gray;
font-size: 60% !important;
white-space: pre-wrap;
}
richlistitem[selected="true"] {
background-color: -moz-Field;
color: -moz-FieldText;
}
richlistitem .show-on-select {
visibility: collapse;
}
richlistitem[selected="true"] .show-on-select {
visibility: visible;
}
richlistitem .hide-on-select {
visibility: visible;
}
richlistitem[selected="true"] .hide-on-select {
visibility: collapse;
}
richlistitem.section-header,
richlistitem[selected="true"].section-header {
font-weight: bold;
color: #000;
background-color: lightgray;
}
/* addons states ----------------------------------------------------------- */
.hide-on-enable,
.show-on-uninstall,
.show-on-install,
.show-on-restart,
richlistitem[isDisabled="true"] .hide-on-disable {
display: none;
}
richlistitem[opType="needs-restart"] .show-on-restart,
richlistitem[opType="needs-uninstall"] .show-on-uninstall,
richlistitem[opType="needs-install"] .show-on-install,
richlistitem[opType="needs-enable"] .show-on-enable,
richlistitem[opType="needs-disable"] .show-on-disable,
richlistitem[isDisabled="true"] .show-on-disable {
display: -moz-box;
}
richlistitem[opType="needs-restart"] .hide-on-restart,
richlistitem[opType="needs-uninstall"] .hide-on-uninstall,
richlistitem[opType="needs-install"] .hide-on-install,
richlistitem[opType="needs-enable"] .hide-on-enable,
richlistitem[opType="needs-disable"] .hide-on-disable {
display: none;
}

View File

@ -74,6 +74,7 @@
<script type="application/x-javascript" src="chrome://browser/content/browser.js"/>
<script type="application/x-javascript" src="chrome://browser/content/browser-ui.js"/>
<script type="application/x-javascript" src="chrome://browser/content/sanitize.js"/>
<script type="application/x-javascript" src="chrome://browser/content/extensions.js"/>
<script type="application/x-javascript" src="chrome://browser/content/WidgetStack.js"/>
<script type="application/x-javascript" src="chrome://browser/content/CanvasBrowser.js"/>
<script type="application/x-javascript" src="chrome://browser/content/InputHandler.js"/>
@ -282,13 +283,38 @@
<hbox id="panel-container" hidden="true" style="-moz-stack-sizing: ignore;" top="0" left="0">
<vbox id="panel-controls" oncommand="BrowserUI.switchPane(event.target.getAttribute('linkedpanel'));">
<toolbarspring/>
<toolbarbutton id="tool-addons" type="radio" group="1" class="panel-button" linkedpanel="addons-container"/>
<toolbarbutton id="tool-addons" type="radio" group="1" checked="1" class="panel-button" linkedpanel="addons-container"/>
<toolbarbutton id="tool-downloads" type="radio" group="1" class="panel-button" linkedpanel="downloads-container"/>
<toolbarbutton id="tool-preferences" type="radio" group="1" class="panel-button" linkedpanel="prefs-container"/>
<toolbarbutton id="tool-panel-close" class="page-button" command="cmd_panel"/>
</vbox>
<deck id="panel-items" flex="1">
<iframe id="addons-container" flex="1"/>
<vbox id="addons-container" flex="1">
<hbox id="addons-header">
<label value="&addonsHeader.label;"/>
</hbox>
<notificationbox id="addons-messages" flex="1">
<richlistbox id="addons-list" flex="1">
<richlistitem id="addons-local" class="section-header" align="center">
<label value="&addonsLocal.label;" flex="1"/>
</richlistitem>
<richlistitem id="addons-repo" class="section-header" orient="vertical" pack="center">
<hbox>
<label value="&addonsRepo.label;" flex="1"/>
<radiogroup id="addons-repo-mode" oncommand="ExtensionsView.toggleMode();">
<radio label="&addonsRecommended.label;" value="recommended" selected="true"/>
<radio label="&addonsSearch.label;" value="search"/>
</radiogroup>
</hbox>
<hbox id="addons-search-box" pack="end" collapsed="true">
<textbox id="addons-search-text" emptytext="&addonsSearch.emptytext;" type="search" searchbutton="false"
oncommand="ExtensionsView.getAddonsFromRepo(this.value);"/>
</hbox>
</richlistitem>
</richlistbox>
</notificationbox>
</vbox>
<iframe id="downloads-container" flex="1"/>
<vbox id="prefs-container" flex="1">

View File

@ -0,0 +1,610 @@
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Mobile Browser.
*
* The Initial Developer of the Original Code is Mozilla Corporation.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Mark Finkle <mfinkle@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
const PREFIX_ITEM_URI = "urn:mozilla:item:";
const PREFIX_NS_EM = "http://www.mozilla.org/2004/em-rdf#";
const PREF_GETADDONS_REPOSITORY = "extensions.getAddons.repository";
const PREF_GETADDONS_MAXRESULTS = "extensions.getAddons.maxResults";
const URI_GENERIC_ICON_XPINSTALL = "chrome://mozapps/skin/xpinstall/xpinstallItemGeneric.png";
var ExtensionsView = {
_extmgr: null,
_pref: null,
_rdf: null,
_ios: null,
_repo: null,
_list: null,
_localItem: null,
_repoItem: null,
_msg: null,
_dloadmgr: null,
_restartCount: 0,
_isXPInstallEnabled: function isXPInstallEnabled() {
let enabled = false;
let locked = false;
try {
enabled = this._pref.getBoolPref("xpinstall.enabled");
if (enabled)
return true;
locked = this._pref.prefIsLocked("xpinstall.enabled");
}
catch (e) { }
/*
var msgText = getExtensionString(locked ? "xpinstallDisabledMsgLocked" :
"xpinstallDisabledMsg");
var buttonLabel = locked ? null : getExtensionString("enableButtonLabel");
var buttonAccesskey = locked ? null : getExtensionString("enableButtonAccesskey");
var notifyData = locked ? null : "addons-enable-xpinstall";
showMessage(URI_NOTIFICATION_ICON_WARNING,
msgText, buttonLabel, buttonAccesskey,
!locked, notifyData);
*/
return false;
},
_getIDFromURI: function ev__getIDFromURI(aURI) {
if (aURI.substring(0, PREFIX_ITEM_URI.length) == PREFIX_ITEM_URI)
return aURI.substring(PREFIX_ITEM_URI.length);
return aURI;
},
_getRDFProperty: function ev__getRDFProperty(aID, aName) {
let resource = this._rdf.GetResource(PREFIX_ITEM_URI + aID);
if (resource) {
let ds = this._extmgr.datasource;
let target = ds.GetTarget(resource, this._rdf.GetResource(PREFIX_NS_EM + aName), true);
if (target && target instanceof Ci.nsIRDFLiteral)
return target.Value;
}
return null;
},
_createItem: function ev__createItem(aAddon, aTypeName) {
let item = document.createElement("richlistitem");
item.setAttribute("id", PREFIX_ITEM_URI + aAddon.id);
item.setAttribute("addonID", aAddon.id);
item.setAttribute("typeName", aTypeName);
item.setAttribute("type", aAddon.type);
item.setAttribute("name", aAddon.name);
item.setAttribute("version", aAddon.version);
item.setAttribute("iconURL", aAddon.iconURL);
return item;
},
_clearSection: function ev_clearSection(aSection) {
let start = end = null;
if (aSection == "local") {
start = this._localItem;
end = this._repoItem;
}
else {
start = this._repoItem;
}
while (start.nextSibling != end)
this._list.removeChild(start.nextSibling);
},
_messageActions: function ev__messageActions(aData) {
if (aData == "addons-restart-app") {
// Notify all windows that an application quit has been requested
var os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
var cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
os.notifyObservers(cancelQuit, "quit-application-requested", "restart");
// If nothing aborted, quit the app
if (cancelQuit.data == false) {
let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup);
appStartup.quit(Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eAttemptQuit);
}
}
},
getElementForAddon: function ev_getElementForAddon(aID) {
let element = document.getElementById(PREFIX_ITEM_URI + aID);
if (!element)
element = this._list.getElementsByAttribute("xpiURL", aID)[0];
return element;
},
showMessage: function ev_showMessage(aMsg, aValue, aButtonLabel, aShowCloseButton, aNotifyData) {
let notification = this._msg.getNotificationWithValue(aValue);
if (notification)
return;
let self = this;
let buttons = null;
if (aButtonLabel) {
buttons = [ {
label: aButtonLabel,
accesskey: "",
data: aNotifyData,
callback: function(aNotification, aButton) {
self._messageActions(aButton.data);
return true;
}
} ];
}
this._msg.appendNotification(aMsg, aValue, "", this._msg.PRIORITY_WARNING_LOW, buttons).hideclose = !aShowCloseButton;
},
showRestart: function ev_showRestart() {
this._restartCount++;
let strings = document.getElementById("bundle_browser");
this.showMessage(strings.getString("addonsRestart"), "restart-app",
strings.getString("addonsRestartButton.label"), false, "addons-restart-app");
},
hideRestart: function ev_hideRestart() {
this._restartCount--;
if (this._restartCount == 0) {
let notification = this._msg.getNotificationWithValue("restart-app");
if (notification)
notification.close();
}
},
get visible() {
let panel = document.getElementById("panel-container");
let items = document.getElementById("panel-items");
if (panel.hidden == false && items.selectedPanel.id == "addons-container")
return true;
return false;
},
init: function ev_init() {
if (this._extmgr)
return;
this._extmgr = Cc["@mozilla.org/extensions/manager;1"].getService(Ci.nsIExtensionManager);
this._pref = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch2);
this._rdf = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService);
this._ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
let repository = "@mozilla.org/extensions/addon-repository;1";
try {
var repo = pref.getCharPref(PREF_GETADDONS_REPOSITORY);
if (repo in Components.classes)
repository = repo;
} catch (e) { }
this._repo = Cc[repository].createInstance(Ci.nsIAddonRepository);
this._dloadmgr = new XPInstallDownloadManager();
this._extmgr.addInstallListener(this._dloadmgr);
// Now look and see if we're being opened by XPInstall
var os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
os.addObserver(this._dloadmgr, "xpinstall-download-started", false);
let self = this;
let panels = document.getElementById("panel-items");
panels.addEventListener("select",
function(aEvent) {
if (panels.selectedPanel.id == "addons-container")
self.show();
},
false);
},
show: function ev_show() {
if (this._list)
return;
this._list = document.getElementById("addons-list");
this._localItem = document.getElementById("addons-local");
this._repoItem = document.getElementById("addons-repo");
this._msg = document.getElementById("addons-messages");
let self = this;
setTimeout(function() {
self.getAddonsFromLocal();
self.getAddonsFromRepo("");
}, 0);
},
getAddonsFromLocal: function ev_getAddonsFromLocal() {
this._clearSection("local");
let items = this._extmgr.getItemList(Ci.nsIUpdateItem.TYPE_ANY, {});
for (let i = 0; i < items.length; i++) {
let addon = items[i];
// Some information is not directly accessible from the extmgr
let isDisabled = this._getRDFProperty(addon.id, "isDisabled") == "true";
let appDisabled = this._getRDFProperty(addon.id, "appDisabled");
let desc = this._getRDFProperty(addon.id, "description");
let optionsURL = this._getRDFProperty(addon.id, "optionsURL");
let opType = this._getRDFProperty(addon.id, "opType");
let listitem = this._createItem(addon, "local");
listitem.setAttribute("isDisabled", isDisabled);
listitem.setAttribute("appDisabled", appDisabled);
listitem.setAttribute("description", desc);
listitem.setAttribute("optionsURL", optionsURL);
listitem.setAttribute("opType", opType);
this._list.insertBefore(listitem, this._repoItem);
}
},
enable: function ev_enable(aItem) {
let id = this._getIDFromURI(aItem.id);
this._extmgr.enableItem(id);
let opType = this._getRDFProperty(id, "opType");
if (opType == "needs-enable")
this.showRestart();
else
this.hideRestart();
aItem.setAttribute("opType", opType);
},
disable: function ev_disable(aItem) {
let id = this._getIDFromURI(aItem.id);
this._extmgr.disableItem(id);
let opType = this._getRDFProperty(id, "opType");
if (opType == "needs-disable")
this.showRestart();
else
this.hideRestart();
aItem.setAttribute("opType", opType);
},
uninstall: function ev_uninstall(aItem) {
let id = this._getIDFromURI(aItem.id);
this._extmgr.uninstallItem(id);
let opType = this._getRDFProperty(id, "opType");
if (opType == "needs-uninstall")
this.showRestart();
aItem.setAttribute("opType", opType);
},
cancelUninstall: function ev_cancelUninstall(aItem) {
let id = this._getIDFromURI(aItem.id);
this._extmgr.cancelUninstallItem(id);
this.hideRestart();
let opType = this._getRDFProperty(id, "opType");
aItem.setAttribute("opType", opType);
},
_installCallback: function ev__installCallback(aItem, aStatus) {
if (aStatus == -210) {
// TODO: User cancelled
}
else if (aStatus < 0) {
// TODO: Some other error
}
else {
// Success
aItem.setAttribute("opType", "needs-restart");
}
},
installFromRepo: function ev_installFromRepo(aItem) {
if (!this._isXPInstallEnabled())
return;
aItem.setAttribute("opType", "needs-install");
if (aItem.hasAttribute("eula")) {
var eula = {
name: aSelectedItem.getAttribute("name"),
text: aSelectedItem.getAttribute("eula"),
accepted: false
};
// TODO: eula
//window.openDialog("chrome://mozapps/content/extensions/eula.xul", "_blank",
// "chrome,dialog,modal,centerscreen,resizable=no", eula);
//if (!eula.accepted)
// return;
}
var details = {
URL: aItem.getAttribute("xpiURL"),
Hash: aItem.getAttribute("xpiHash"),
IconURL: aItem.getAttribute("iconURL"),
toString: function () { return this.URL; }
};
var params = [];
params[aItem.getAttribute("name")] = details;
let self = this;
InstallTrigger.install(params, function(aURL, aStatus) { self._installCallback(aItem, aStatus); });
},
installFromXPI: function ev_installAddons(aItems, aManager) {
this._extmgr.addDownloads(aItems, aItems.length, aManager);
},
_isSafeURI: function ev_isSafeURI(aURL) {
try {
var uri = this._ios.newURI(aURL, null, null);
var scheme = uri.scheme;
} catch (ex) {}
return (uri && (scheme == "http" || scheme == "https" || scheme == "ftp"));
},
toggleMode: function ev_toggleMode() {
let mode = document.getElementById("addons-repo-mode");
if (mode.value == "search") {
document.getElementById("addons-search-box").collapsed = false;
document.getElementById("addons-search-text").value = "";
}
else {
document.getElementById("addons-search-box").collapsed = true;
this.getAddonsFromRepo("");
}
},
displaySearchMessage: function ev_displaySearchMessage(aMessage, aButtonLabel, aHideThrobber) {
let item = document.createElement("richlistitem");
item.setAttribute("typeName", "message");
item.setAttribute("message", aMessage);
item.setAttribute("button", aButtonLabel);
item.setAttribute("hidethrobber", aHideThrobber);
this._list.appendChild(item);
},
getAddonsFromRepo: function ev_getAddonsFromRepo(aTerms) {
this._clearSection("repo");
if (this._repo.isSearching)
this._repo.cancelSearch();
let strings = document.getElementById("bundle_browser");
if (aTerms) {
this.displaySearchMessage(strings.getString("addonsSearchStart.label"),
strings.getString("addonsSearchStart.button"), false);
this._repo.searchAddons(aTerms, this._pref.getIntPref(PREF_GETADDONS_MAXRESULTS), AddonSearchResults);
}
else {
if (RecommendedSearchResults.cache) {
this.displaySearchResults(RecommendedSearchResults.cache, -1, true);
}
else {
this.displaySearchMessage(strings.getString("addonsSearchStart.label"),
strings.getString("addonsSearchStart.button"), false);
this._repo.retrieveRecommendedAddons(this._pref.getIntPref(PREF_GETADDONS_MAXRESULTS), RecommendedSearchResults);
}
}
},
displaySearchResults: function ev_displaySearchResults(aAddons, aTotalResults, aIsRecommended) {
this._clearSection("repo");
if (aAddons.length == 0) {
let strings = document.getElementById("bundle_browser");
this.displaySearchMessage(strings.getString("addonsSearchNone.label"),
strings.getString("addonsSearchNone.button"), true);
return;
}
if (aIsRecommended) {
// Locale sensitive sort
function compare(a, b) {
return String.localeCompare(a.name, b.name);
}
aAddons.sort(compare);
}
var urlproperties = [ "iconURL", "homepageURL", "thumbnailURL", "xpiURL" ];
var properties = [ "name", "eula", "iconURL", "homepageURL", "thumbnailURL", "xpiURL", "xpiHash" ];
for (let i = 0; i < aAddons.length; i++) {
let addon = aAddons[i];
// Check for any items with potentially unsafe urls
if (urlproperties.some(function (p) !this._isSafeURI(addon[p]), this))
continue;
let listitem = this._createItem(addon, "search");
listitem.setAttribute("description", addon.description);
listitem.setAttribute("thumbnailURL", addon.thumbnailURL);
listitem.setAttribute("xpiURL", addon.xpiURL);
listitem.setAttribute("xpiHash", addon.xpiHash);
this._list.appendChild(listitem);
}
},
resetSearch: function ev_resetSearch() {
document.getElementById("addons-repo-mode").value = "recommended";
this.toggleMode();
}
};
///////////////////////////////////////////////////////////////////////////////
// nsIAddonSearchResultsCallback for the recommended search
var RecommendedSearchResults = {
cache: null,
searchSucceeded: function(aAddons, aAddonCount, aTotalResults) {
this.cache = aAddons;
ExtensionsView.displaySearchResults(aAddons, aTotalResults, true);
},
searchFailed: function() {
let strings = document.getElementById("bundle_browser");
let brand = document.getElementById("bundle_brand");
ExtensionsView.displaySearchMessage(strings.getFormattedString("addonsSearchFail.label", [brand.getString("brandShortName")]),
strings.getString("addonsSearchFail.button"), true);
}
}
///////////////////////////////////////////////////////////////////////////////
// nsIAddonSearchResultsCallback for a standard search
var AddonSearchResults = {
searchSucceeded: function(aAddons, aAddonCount, aTotalResults) {
ExtensionsView.displaySearchResults(aAddons, aTotalResults, false);
},
searchFailed: function() {
let strings = document.getElementById("bundle_browser");
let brand = document.getElementById("bundle_brand");
ExtensionsView.displaySearchMessage(strings.getFormattedString("addonsSearchFail.label", [brand.getString("brandShortName")]),
strings.getString("addonsSearchFail.button"), true);
}
}
///////////////////////////////////////////////////////////////////////////////
// XPInstall download helper
function XPInstallDownloadManager() {
}
XPInstallDownloadManager.prototype = {
observe: function (aSubject, aTopic, aData) {
switch (aTopic) {
case "xpinstall-download-started":
var params = aSubject.QueryInterface(Components.interfaces.nsISupportsArray);
var paramBlock = params.GetElementAt(0).QueryInterface(Components.interfaces.nsISupportsInterfacePointer);
paramBlock = paramBlock.data.QueryInterface(Components.interfaces.nsIDialogParamBlock);
var manager = params.GetElementAt(1).QueryInterface(Components.interfaces.nsISupportsInterfacePointer);
manager = manager.data.QueryInterface(Components.interfaces.nsIObserver);
this.addDownloads(paramBlock, manager);
break;
}
},
addDownloads: function (aParams, aManager) {
let count = aParams.GetInt(1);
let items = [];
for (var i = 0; i < count;) {
let displayName = aParams.GetString(i++);
let url = aParams.GetString(i++);
let iconURL = aParams.GetString(i++);
let uri = ExtensionsView._ios.newURI(url, null, null);
let isTheme = uri.QueryInterface(Ci.nsIURL).fileExtension.toLowerCase() == "jar";
let type = isTheme ? Ci.nsIUpdateItem.TYPE_THEME : Ci.nsIUpdateItem.TYPE_EXTENSION;
if (!iconURL) {
iconURL = isTheme ? "chrome://mozapps/skin/extensions/themeGeneric.png" :
"chrome://mozapps/skin/xpinstall/xpinstallItemGeneric.png";
}
let item = Cc["@mozilla.org/updates/item;1"].createInstance(Ci.nsIUpdateItem);
item.init(url, " ", "app-profile", "", "", displayName, url, "", iconURL, "", "", type, "");
items.push(item);
// Advance the enumerator
let certName = aParams.GetString(i++);
}
this._failed = [];
this._succeeded = [];
ExtensionsView.installFromXPI(items, aManager);
if (ExtensionsView.visible)
return;
let strings = document.getElementById("bundle_browser");
var alerts = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
alerts.showAlertNotification(URI_GENERIC_ICON_XPINSTALL, strings.getString("alertAddons"),
strings.getString("alertAddonsStart"), true, "", this);
},
/////////////////////////////////////////////////////////////////////////////
// nsIAddonInstallListener
onDownloadStarted: function(aAddon) { },
onDownloadEnded: function(aAddon) { },
onInstallStarted: function(aAddon) { },
onCompatibilityCheckStarted: function(aAddon) { },
onCompatibilityCheckEnded: function(aAddon, aStatus) { },
_failed: [],
_succeeded: [],
onInstallEnded: function(aAddon, aStatus) {
// Track success/failure for each addon
if (Components.isSuccessCode(aStatus))
this._succeeded.push(aAddon.id);
else
this._failed.push(aAddon.id);
var element = ExtensionsView.getElementForAddon(aAddon.id);
if (!element)
return;
element.setAttribute("status", (Components.isSuccessCode(aStatus) ? "success" : "fail"));
},
onInstallsCompleted: function() {
let strings = document.getElementById("bundle_browser");
// If even one add-on succeeded, display the restart notif
if (this._succeeded.length > 0)
ExtensionsView.showRestart();
if (ExtensionsView.visible)
return;
let message = strings.getString("alertAddonsDone");
if (this._succeeded.length == 0 && this._failed.length > 0)
message = strings.getString("alertAddonsFail");
var alerts = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
alerts.showAlertNotification(URI_GENERIC_ICON_XPINSTALL, strings.getString("alertAddons"),
message, true, "", this);
},
onDownloadProgress: function xpidm_onDownloadProgress(aAddon, aValue, aMaxValue) {
var element = ExtensionsView.getElementForAddon(aAddon.id);
if (!element)
return;
var progress = Math.round((aValue / aMaxValue) * 100);
element.setAttribute("progress", progress);
},
/////////////////////////////////////////////////////////////////////////////
// nsISupports
QueryInterface: function (aIID) {
if (!aIID.equals(Ci.nsIAddonInstallListener) &&
!aIID.equals(Ci.nsISupports))
throw Components.results.NS_ERROR_NO_INTERFACE;
return this;
}
};

View File

@ -10,15 +10,16 @@ browser.jar:
content/tabs.xml (content/tabs.xml)
content/checkbox.xml (content/checkbox.xml)
content/notification.xml (content/notification.xml)
content/bindings/extensions.xml (content/bindings/extensions.xml)
content/browser.css (content/browser.css)
content/scrollbars.css (content/scrollbars.css)
content/content.css (content/content.css)
content/checkerboard.png (content/checkerboard.png)
% content branding %content/branding/
% style chrome://mozapps/content/extensions/extensions.xul chrome://browser/skin/extensions.css
content/preferences/richpref.xml (content/preferences/richpref.xml)
* content/sanitize.xul (content/sanitize.xul)
* content/sanitize.js (content/sanitize.js)
content/WidgetStack.js (content/WidgetStack.js)
content/CanvasBrowser.js (content/CanvasBrowser.js)
content/InputHandler.js (content/InputHandler.js)
content/extensions.js (content/extensions.js)

View File

@ -53,6 +53,7 @@ EXTRA_COMPONENTS = \
aboutFirstrun.js \
geolocationPrompt.js \
alertsService.js \
xpiDialogService.js \
$(NULL)
DIRS = protocols \

View File

@ -0,0 +1,93 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is XPI Dialog Service.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Mark Finkle <mfinkle@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
const Cc = Components.classes;
const Ci = Components.interfaces;
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
// -----------------------------------------------------------------------
// XPInstall Dialog Service
// -----------------------------------------------------------------------
function XPInstallDialogService() { }
XPInstallDialogService.prototype = {
classDescription: "XPInstall Dialog Service",
contractID: "@mozilla.org/embedui/xpinstall-dialog-service;1",
classID: Components.ID("{c1242012-27d8-477e-a0f1-0b098ffc329b}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIXPIDialogService, Ci.nsIXPIProgressDialog]),
confirmInstall: function(aParent, aPackages, aCount) {
// Return true to get the install started
return true;
},
openProgressDialog: function(aPackages, aCount, aManager) {
// Create a param block with the packages
let dpb = Cc["@mozilla.org/embedcomp/dialogparam;1"].createInstance(Ci.nsIDialogParamBlock);
dpb.SetInt(0, 2); // OK and Cancel buttons
dpb.SetInt(1, aPackages.length); // Number of strings
dpb.SetNumberStrings(aPackages.length); // Add strings
for (let i = 0; i < aPackages.length; ++i)
dpb.SetString(i, aPackages[i]);
let dpbWrap = Cc["@mozilla.org/supports-interface-pointer;1"].createInstance(Ci.nsISupportsInterfacePointer);
dpbWrap.data = dpb;
dpbWrap.dataIID = Ci.nsIDialogParamBlock;
let obsWrap = Cc["@mozilla.org/supports-interface-pointer;1"].createInstance(Ci.nsISupportsInterfacePointer);
obsWrap.data = aManager;
obsWrap.dataIID = Ci.nsIObserver;
let params = Cc["@mozilla.org/supports-array;1"].createInstance(Ci.nsISupportsArray);
params.AppendElement(dpbWrap);
params.AppendElement(obsWrap);
let os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
os.notifyObservers(params, "xpinstall-download-started", null);
// Give the nsXPInstallManager a progress dialog
aManager.observe(this, "xpinstall-progress", "open");
},
onStateChange: function(aIndex, aState, aError) { },
onProgress: function(aIndex, aValue, aMax) { }
};
function NSGetModule(aCompMgr, aFileSpec) {
return XPCOMUtils.generateModule([XPInstallDialogService]);
}

View File

@ -49,6 +49,20 @@
<!ENTITY findAgainCmd.commandkey "g">
<!ENTITY findAgainCmd.commandkey2 "VK_F3">
<!ENTITY addonsHeader.label "Add-ons">
<!ENTITY addonsLocal.label "Your Add-ons">
<!ENTITY addonsRepo.label "Get Add-ons">
<!ENTITY addonsRecommended.label "See Recommended">
<!ENTITY addonsSearch.label "Search Catalog">
<!ENTITY addonsSearch.emptytext "Search for an add-ons">
<!ENTITY addonOptions.label "Options">
<!ENTITY addonEnable.label "Enable">
<!ENTITY addonDisable.label "Disable">
<!ENTITY addonInstall.label "Install">
<!ENTITY addonUninstall.label "Uninstall">
<!ENTITY addonCancel.label "Cancel">
<!ENTITY identity.unverifiedsite2 "This web site does not supply identity information.">
<!ENTITY identity.connectedTo "You are connected to">
<!-- Localization note (identity.runBy) : This string appears between a

View File

@ -2,6 +2,23 @@
editBookmarkAddFolder=Add a new folder
editBookmarkNewFolder=New folder
# Add-on Manager
addonsRestart=Restart to complete changes
addonsRestartButton.label=Restart
addonsSearchStart.label=Searching for add-ons...
addonsSearchStart.button=Cancel
addonsSearchNone.label=No matches found
addonsSearchNone.button=OK
addonsSearchFail.label=%S couldn't retrieve add-ons
addonsSearchFail.button=OK
# Alerts
alertAddons=Add-ons
alertAddonsStart=Installing addons
alertAddonsDone=Installation complete
alertAddonsFail=Installation failed
# Popup Blocker
popupWarning=%S prevented this site from opening a pop-up window.
popupWarningMultiple=%S prevented this site from opening %S pop-up windows.

View File

@ -1,46 +0,0 @@
label,
description,
textbox {
font-family: "Nokia Sans", sans-serif !important;
font-size: 12pt !important;
}
richlistitem {
font-family: "Nokia Sans", sans-serif !important;
font-size: 12pt !important;
}
#extensionsManager {
-moz-appearance: none;
background-color: rgba(123,125,123,0.9);
}
#extensionsView {
-moz-appearance: none !important;
background-color: rgba(207,207,207,0.9);
border: 2px solid #fff !important;
-moz-border-top-colors: none;
-moz-border-right-colors: none;
-moz-border-bottom-colors: none;
-moz-border-left-colors: none;
-moz-border-radius: 10px;
}
#themeSplitter {
background-color: rgba(123,125,123,0.9);
}
#themePreviewArea {
-moz-appearance: none !important;
background-color: rgba(207,207,207,0.9);
border: 2px solid #fff !important;
-moz-border-top-colors: none;
-moz-border-right-colors: none;
-moz-border-bottom-colors: none;
-moz-border-left-colors: none;
-moz-border-radius: 10px;
}
#resizerBox {
display: none;
}

View File

@ -10,7 +10,6 @@ classic.jar:
% skin browser classic/1.0 %
platform.css (platform.css)
browser.css (browser.css)
extensions.css (extensions.css)
notification.css (notification.css)
images/tab_close.png (images/tab_close.png)
images/default-favicon.png (images/default-favicon.png)

View File

@ -1,46 +0,0 @@
label,
description,
textbox {
font-family: "Nokia Sans", sans-serif !important;
font-size: 12pt !important;
}
richlistitem {
font-family: "Nokia Sans", sans-serif !important;
font-size: 12pt !important;
}
#extensionsManager {
-moz-appearance: none;
background-color: rgba(123,125,123,0.9);
}
#extensionsView {
-moz-appearance: none !important;
background-color: rgba(207,207,207,0.9);
border: 2px solid #fff !important;
-moz-border-top-colors: none;
-moz-border-right-colors: none;
-moz-border-bottom-colors: none;
-moz-border-left-colors: none;
-moz-border-radius: 10px;
}
#themeSplitter {
background-color: rgba(123,125,123,0.9);
}
#themePreviewArea {
-moz-appearance: none !important;
background-color: rgba(207,207,207,0.9);
border: 2px solid #fff !important;
-moz-border-top-colors: none;
-moz-border-right-colors: none;
-moz-border-bottom-colors: none;
-moz-border-left-colors: none;
-moz-border-radius: 10px;
}
#resizerBox {
display: none;
}

View File

@ -10,7 +10,6 @@ classic.jar:
% skin browser classic/1.0 %
platform.css (platform.css)
browser.css (browser.css)
extensions.css (extensions.css)
notification.css (notification.css)
images/tab_close.png (images/tab_close.png)
images/default-favicon.png (images/default-favicon.png)