gecko-dev/toolkit/mozapps/extensions/content/extensions.js

1184 lines
43 KiB
JavaScript
Raw Normal View History

# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
# ***** 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 The Extension Manager.
#
# The Initial Developer of the Original Code is Ben Goodger.
# Portions created by the Initial Developer are Copyright (C) 2004
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Ben Goodger <ben@mozilla.org>
#
# 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 *****
///////////////////////////////////////////////////////////////////////////////
// Globals
const kObserverServiceProgID = "@mozilla.org/observer-service;1";
const nsIUpdateItem = Components.interfaces.nsIUpdateItem;
var gExtensionManager = null;
var gExtensionsView = null;
var gWindowState = "";
var gGetMoreURL = "";
var gCurrentTheme = "classic/1.0";
var gDefaultTheme = "classic/1.0";
var gDownloadManager = null;
var gObserverIndex = -1;
var gItemType = -1;
var gApp = null;
const PREF_EXTENSIONS_GETMORETHEMESURL = "extensions.getMoreThemesURL";
const PREF_EXTENSIONS_GETMOREEXTENSIONSURL = "extensions.getMoreExtensionsURL";
const PREF_EXTENSIONS_DSS_ENABLED = "extensions.dss.enabled";
const PREF_EXTENSIONS_DSS_SWITCHPENDING = "extensions.dss.switchPending";
const PREF_EM_LAST_SELECTED_SKIN = "extensions.lastSelectedSkin";
const PREF_GENERAL_SKINS_SELECTEDSKIN = "general.skins.selectedSkin";
const RDFURI_ITEM_ROOT = "urn:mozilla:item:root";
const PREFIX_ITEM_URI = "urn:mozilla:item:";
const OP_NONE = "";
const OP_NEEDS_INSTALL = "needs-install";
const OP_NEEDS_UPGRADE = "needs-upgrade";
const OP_NEEDS_UNINSTALL = "needs-uninstall";
const OP_NEEDS_ENABLE = "needs-enable";
const OP_NEEDS_DISABLE = "needs-disable";
///////////////////////////////////////////////////////////////////////////////
// Utility Functions
function LOG(msg) {
dump("*** " + msg + "\n");
}
function getIDFromResourceURI(aURI)
{
if (aURI.substring(0, PREFIX_ITEM_URI.length) == PREFIX_ITEM_URI)
return aURI.substring(PREFIX_ITEM_URI.length);
return aURI;
}
function openURL(aURL)
{
# If we're not a browser, use the external protocol service to load the URI.
#ifndef MOZ_PHOENIX
var uri = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService)
.newURI(aURL, null, null);
var protocolSvc = Components.classes["@mozilla.org/uriloader/external-protocol-service;1"]
.getService(Components.interfaces.nsIExternalProtocolService);
protocolSvc.loadUrl(uri);
# If we're a browser, open a new browser window instead.
#else
openDialog("chrome://browser/content/browser.xul", "_blank", "chrome,all,dialog=no", aURL, null, null);
#endif
}
function flushDataSource()
{
var rds = gExtensionManager.datasource.QueryInterface(Components.interfaces.nsIRDFRemoteDataSource);
if (rds)
rds.Flush();
}
function setRestartMessage(aItem)
{
var sbs = Components.classes["@mozilla.org/intl/stringbundle;1"]
.getService(Components.interfaces.nsIStringBundleService);
var extensionStrings = sbs.createBundle("chrome://mozapps/locale/extensions/extensions.properties");
var brandStrings = sbs.createBundle("chrome://branding/locale/brand.properties");
var brandShortName = brandStrings.GetStringFromName("brandShortName");
var themeName = aItem.getAttribute("name");
var restartMessage = extensionStrings.formatStringFromName("dssSwitchAfterRestart",
[brandShortName], 1);
for (var i = 0; i < gExtensionsView.childNodes.length; ++i) {
var item = gExtensionsView.childNodes[i];
if (item.hasAttribute("oldDescription")) {
item.setAttribute("description", item.getAttribute("oldDescription"));
item.removeAttribute("oldDescription");
}
}
aItem.setAttribute("oldDescription", aItem.getAttribute("description"));
aItem.setAttribute("description", restartMessage);
}
///////////////////////////////////////////////////////////////////////////////
// Event Handlers
function onExtensionUpdateNow(aEvent)
{
var item = gExtensionManager.getItemForID(getIDFromResourceURI(
aEvent.target.id));
gExtensionManager.addDownloads([item], 1, true);
}
///////////////////////////////////////////////////////////////////////////////
// Startup, Shutdown
function Startup()
{
gWindowState = window.location.search.substr("?type=".length, window.location.search.length);
var isExtensions = gWindowState == "extensions";
gItemType = isExtensions ? nsIUpdateItem.TYPE_EXTENSION : nsIUpdateItem.TYPE_THEME;
var typeCondition = document.getElementById("typeCondition");
typeCondition.setAttribute("object", gItemType);
document.documentElement.setAttribute("windowtype", document.documentElement.getAttribute("windowtype") + "-" + gWindowState);
gExtensionsView = document.getElementById("extensionsView");
gExtensionsView.setAttribute("state", gWindowState);
gExtensionManager = Components.classes["@mozilla.org/extensions/manager;1"]
.getService(Components.interfaces.nsIExtensionManager);
gApp = Components.classes["@mozilla.org/xre/app-info;1"].getService(Components.interfaces.nsIXULAppInfo)
.QueryInterface(Components.interfaces.nsIXULRuntime);
// Extension Command Updating is handled by a command controller.
gExtensionsView.controllers.appendController(gExtensionsViewController);
gExtensionsView.addEventListener("extension-updatenow", onExtensionUpdateNow, false);
var pref = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
var defaultPref = pref.QueryInterface(Components.interfaces.nsIPrefService)
.getDefaultBranch(null);
if (!isExtensions) {
gExtensionsView.addEventListener("select", onThemeSelect, false);
try {
gCurrentTheme = pref.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN);
gDefaultTheme = defaultPref.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN);
}
catch (e) { }
var useThemeButton = document.getElementById("useThemeButton");
useThemeButton.hidden = false;
var optionsButton = document.getElementById("optionsButton");
optionsButton.hidden = true;
}
// Finally, update the UI.
gExtensionsView.database.AddDataSource(gExtensionManager.datasource);
gExtensionsView.setAttribute("ref", RDFURI_ITEM_ROOT);
gExtensionsView.focus();
var extensionsStrings = document.getElementById("extensionsStrings");
document.title = extensionsStrings.getString(gWindowState + "Title");
gExtensionsViewController.onCommandUpdate();
gGetMoreURL = pref.getComplexValue(isExtensions ? PREF_EXTENSIONS_GETMOREEXTENSIONSURL
: PREF_EXTENSIONS_GETMORETHEMESURL,
Components.interfaces.nsIPrefLocalizedString).data;
var app = Components.classes["@mozilla.org/xre/app-info;1"]
.getService(Components.interfaces.nsIXULAppInfo);
gGetMoreURL = gGetMoreURL.replace(/%APPID%/g, app.ID);
// Update various pieces of state-dependant UI
var getMore = document.getElementById("getMore");
getMore.setAttribute("value", getMore.getAttribute(isExtensions ? "valueextensions" : "valuethemes"));
getMore.setAttribute("tooltiptext", getMore.getAttribute(isExtensions ? "tooltiptextextensions" : "tooltiptextthemes"));
getMore.setAttribute('href', gGetMoreURL);
if (!isExtensions) {
var themePreviewArea = document.getElementById("themePreviewArea");
themePreviewArea.hidden = false;
gExtensionsView.removeAttribute("flex");
gExtensionsView.style.width = extensionsStrings.getString("themesManagerLeftColumn");
}
// Set Initial Size
var win = document.documentElement;
if (!win.hasAttribute("width") || !win.hasAttribute("height")) {
win.setAttribute("width", isExtensions ? (extensionsStrings.getString("extensionsManagerWidth")) : (extensionsStrings.getString("themesManagerWidth")));
win.setAttribute("height", isExtensions ? (extensionsStrings.getString("extensionsManagerHeight")) : (extensionsStrings.getString("themesManagerHeight")));
}
// Now look and see if we're being opened by XPInstall
gDownloadManager = new XPInstallDownloadManager();
var os = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
os.addObserver(gDownloadManager, "xpinstall-download-started", false);
gObserverIndex = gExtensionManager.addUpdateListener(gDownloadManager);
if ("arguments" in window) {
try {
var params = window.arguments[0].QueryInterface(Components.interfaces.nsIDialogParamBlock);
gDownloadManager.addDownloads(params);
}
catch (e) {
if (window.arguments[0] == "updatecheck")
performUpdate();
}
}
// Set the tooltips
if (!isExtensions) {
#ifndef MOZ_PHOENIX
document.getElementById("installButton").setAttribute("tooltiptext", extensionsStrings.getString("cmdInstallTooltipTheme"));
#endif
document.getElementById("uninstallButton").setAttribute("tooltiptext", extensionsStrings.getString("cmdUninstallTooltipTheme"));
document.getElementById("updateButton").setAttribute("tooltiptext", extensionsStrings.getString("cmdUpdateTooltipTheme"));
}
}
function Shutdown()
{
if (gWindowState != "extensions")
gExtensionsView.removeEventListener("select", onThemeSelect, false);
gExtensionsView.removeEventListener("extension-updatenow", onExtensionUpdateNow, false);
gExtensionsView.database.RemoveDataSource(gExtensionManager.datasource);
gExtensionManager.removeUpdateListenerAt(gObserverIndex);
var os = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
if (gDownloadManager)
os.removeObserver(gDownloadManager, "xpinstall-download-started");
}
///////////////////////////////////////////////////////////////////////////////
//
// XPInstall
//
function getURLSpecFromFile(aFile)
{
var ioServ = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
var fph = ioServ.getProtocolHandler("file").QueryInterface(Components.interfaces.nsIFileProtocolHandler);
return fph.getURLSpecFromFile(aFile);
}
function XPInstallDownloadManager()
{
var extensionsStrings = document.getElementById("extensionsStrings");
this._statusFormatKBMB = extensionsStrings.getString("statusFormatKBMB");
this._statusFormatKBKB = extensionsStrings.getString("statusFormatKBKB");
this._statusFormatMBMB = extensionsStrings.getString("statusFormatMBMB");
}
XPInstallDownloadManager.prototype = {
_statusFormat : null,
_statusFormatKBMB : null,
_statusFormatKBKB : null,
_statusFormatMBMB : null,
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);
this.addDownloads(paramBlock);
break;
}
},
addDownloads: function (aParams)
{
var numXPInstallItems = aParams.GetInt(1);
var isExtensions = gWindowState == "extensions";
var items = [];
for (var i = 0; i < numXPInstallItems;) {
var displayName = aParams.GetString(i++);
var url = aParams.GetString(i++);
var iconURL = aParams.GetString(i++);
if (!iconURL) {
iconURL = isExtensions ? "chrome://mozapps/skin/xpinstall/xpinstallItemGeneric.png" :
"chrome://mozapps/skin/extensions/themeGeneric.png";
}
var type = isExtensions ? nsIUpdateItem.TYPE_EXTENSION : nsIUpdateItem.TYPE_THEME;
var item = Components.classes["@mozilla.org/updates/item;1"]
.createInstance(Components.interfaces.nsIUpdateItem);
item.init(url, " ", "app-profile", "", "", displayName, url, "", iconURL, "", type);
items.push(item);
// Advance the enumerator
var certName = aParams.GetString(i++);
}
gExtensionManager.addDownloads(items, items.length, false);
gExtensionsView.scrollBoxObject.ensureElementIsVisible(gExtensionsView.lastChild);
},
getElementForAddon: function(aAddon)
{
var element = document.getElementById(PREFIX_ITEM_URI + aAddon.id);
if (aAddon.id == aAddon.xpiURL)
element = document.getElementById(aAddon.xpiURL);
return element;
},
/////////////////////////////////////////////////////////////////////////////
// nsIAddonUpdateListener
onStateChange: function (aAddon, aState, aValue)
{
const nsIXPIProgressDialog = Components.interfaces.nsIXPIProgressDialog;
var element = this.getElementForAddon(aAddon);
if (!element) return;
switch (aState) {
case nsIXPIProgressDialog.DOWNLOAD_START:
element.setAttribute("state", "waiting");
var extensionsStrings = document.getElementById("extensionsStrings");
element.setAttribute("description",
extensionsStrings.getString("installWaiting"));
element.setAttribute("progress", "0");
break;
case nsIXPIProgressDialog.DOWNLOAD_DONE:
element.setAttribute("progress", "100");
break;
case nsIXPIProgressDialog.INSTALL_START:
element.setAttribute("state", "installing");
extensionsStrings = document.getElementById("extensionsStrings");
element.setAttribute("description",
extensionsStrings.getString("installInstalling"));
break;
case nsIXPIProgressDialog.INSTALL_DONE:
dump("*** state change = " + aAddon.xpiURL + ", state = " + aState + ", value = " + aValue + "\n");
element.setAttribute("state", "done");
extensionsStrings = document.getElementById("extensionsStrings");
element.setAttribute("description",
extensionsStrings.getString("installSuccess"));
var msg;
if (aValue != 0 && aValue != 999) {
var xpinstallStrings = document.getElementById("xpinstallStrings");
try {
msg = xpinstallStrings.getString("error" + aValue);
}
catch (e) {
msg = xpinstallStrings.getFormattedString("unknown.error", [aValue]);
}
var sbs = Components.classes["@mozilla.org/intl/stringbundle;1"]
.getService(Components.interfaces.nsIStringBundleService);
var extensionStrings = sbs.createBundle("chrome://mozapps/locale/extensions/extensions.properties");
var title = extensionStrings.GetStringFromName("errorInstallTitle");
var brandStrings = sbs.createBundle("chrome://branding/locale/brand.properties");
var brandShortName = brandStrings.GetStringFromName("brandShortName");
var params = [brandShortName, aAddon.xpiURL, msg];
var message = extensionStrings.formatStringFromName("errorInstallMsg", params, params.length);
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService);
ps.alert(window, title, message);
element.setAttribute("status", msg);
}
// Remove the dummy, since we installed successfully (only if we're a URL,
// not a download operation on an existing item.
var type = gWindowState == "extensions" ? nsIUpdateItem.TYPE_EXTENSION
: nsIUpdateItem.TYPE_THEME;
gExtensionManager.removeDownload(aAddon.xpiURL);
break;
case nsIXPIProgressDialog.DIALOG_CLOSE:
break;
}
},
_urls: { },
onProgress: function (aAddon, aValue, aMaxValue)
{
var element = this.getElementForAddon(aAddon);
if (!element) return;
var percent = Math.round((aValue / aMaxValue) * 100);
if (percent > 1 && !(aAddon.xpiURL in this._urls)) {
this._urls[aAddon.xpiURL] = true;
element.setAttribute("state", "downloading");
}
element.setAttribute("progress", percent);
var KBProgress = parseInt(aValue/1024 + .5);
var KBTotal = parseInt(aMaxValue/1024 + .5);
element.setAttribute("status", this._formatKBytes(KBProgress, KBTotal));
},
_replaceInsert: function ( text, index, value )
{
var result = text;
var regExp = new RegExp( "#"+index );
result = result.replace( regExp, value );
return result;
},
// aBytes aTotalKBytes returns:
// x, < 1MB y < 1MB x of y KB
// x, < 1MB y >= 1MB x KB of y MB
// x, >= 1MB y >= 1MB x of y MB
_formatKBytes: function (aKBytes, aTotalKBytes)
{
var progressHasMB = parseInt(aKBytes/1000) > 0;
var totalHasMB = parseInt(aTotalKBytes/1000) > 0;
var format = "";
if (!progressHasMB && !totalHasMB) {
format = this._statusFormatKBKB;
format = this._replaceInsert(format, 1, aKBytes);
format = this._replaceInsert(format, 2, aTotalKBytes);
}
else if (progressHasMB && totalHasMB) {
format = this._statusFormatMBMB;
format = this._replaceInsert(format, 1, (aKBytes / 1000).toFixed(1));
format = this._replaceInsert(format, 2, (aTotalKBytes / 1000).toFixed(1));
}
else if (totalHasMB && !progressHasMB) {
format = this._statusFormatKBMB;
format = this._replaceInsert(format, 1, aKBytes);
format = this._replaceInsert(format, 2, (aTotalKBytes / 1000).toFixed(1));
}
else {
// This is an undefined state!
dump("*** huh?!\n");
}
return format;
},
/////////////////////////////////////////////////////////////////////////////
// nsISupports
QueryInterface: function (aIID)
{
if (!aIID.equals(Components.interfaces.nsIAddonUpdateListener) &&
!aIID.equals(Components.interfaces.nsISupports))
throw Components.results.NS_ERROR_NO_INTERFACE;
return this;
}
};
///////////////////////////////////////////////////////////////////////////////
//
// Update Listener
//
function UpdateCheckListener() {
this._addons = [];
}
UpdateCheckListener.prototype = {
/**
* A list of addons we've found updates to in this update check transaction.
*/
_addons: [],
/**
* See nsIExtensionManager.idl
*/
onUpdateStarted: function() {
gExtensionsView.setAttribute("update-check", "true");
var command = document.getElementById("cmd_update");
command.setAttribute("disabled", "true");
command = document.getElementById("cmd_update_all");
command.setAttribute("disabled", "true");
},
/**
* See nsIExtensionManager.idl
*/
onUpdateEnded: function() {
gExtensionsView.removeAttribute("update-check");
var command = document.getElementById("cmd_update");
gExtensionsViewController.updateCommand(command);
command = document.getElementById("cmd_update_all");
command.removeAttribute("disabled");
// Show message to user listing extensions for which updates are available
// and prompt to install now.
if (this._addons.length == 1) {
var element = document.getElementById(PREFIX_ITEM_URI + this._addons[0].id);
gExtensionsView.scrollBoxObject.scrollToElement(element);
}
else if (this._addons.length > 1) {
var strings = document.getElementById("extensionsStrings");
var brandName = document.getElementById("brandStrings").getString("brandShortName");
var params = {
message1: strings.getFormattedString("updatesAvailableMessage1",
[brandName]),
message2: strings.getString("updatesAvailableMessage2"),
title: strings.getString("updatesAvailableTitle"),
buttons: {
accept: { label: strings.getString("updatesAvailableAccept"),
focused: true },
cancel: { label: strings.getString("updatesAvailableCancel") }
}
}
var names = [];
for (var i = 0; i < this._addons.length; ++i) {
var addon = this._addons[i];
var existingItem = gExtensionManager.getItemForID(addon.id);
var name = strings.getFormattedString("itemFormat",
[this._addons[i].name,
existingItem.version,
this._addons[i].version]);
names.push(name);
}
openDialog("chrome://mozapps/content/extensions/list.xul", "",
"titlebar,modal", names, params);
if (params.result == "accept")
gExtensionManager.addDownloads(this._addons, this._addons.length, true);
}
},
/**
* See nsIExtensionManager.idl
*/
onAddonUpdateStarted: function(addon) {
var element = document.getElementById(PREFIX_ITEM_URI + addon.id);
element.setAttribute("loading", "true");
},
/**
* See nsIExtensionManager.idl
*/
onAddonUpdateEnded: function(addon, status) {
var element = document.getElementById(PREFIX_ITEM_URI + addon.id);
element.removeAttribute("loading");
const nsIAUCL = Components.interfaces.nsIAddonUpdateCheckListener;
var strings = document.getElementById("extensionsStrings");
switch (status) {
case nsIAUCL.STATUS_UPDATE:
this._addons.push(addon);
break;
case nsIAUCL.STATUS_FAILURE:
element.setAttribute("description", strings.getString("updateFailedMsg"));
break;
case nsIAUCL.STATUS_DISABLED:
element.setAttribute("description", strings.getString("updateDisabledMsg"));
break;
}
},
/**
* See nsISupports.idl
*/
QueryInterface: function(iid) {
if (!iid.equals(Components.interfaces.nsIAddonUpdateCheckListener) &&
!iid.equals(Components.interfaces.nsISupports))
throw Components.results.NS_ERROR_NO_INTERFACE;
return this;
}
};
///////////////////////////////////////////////////////////////////////////////
//
// View Event Handlers
//
function onViewDoubleClick(aEvent)
{
if (aEvent.button != 0)
return;
switch (gWindowState) {
case "extensions":
gExtensionsViewController.doCommand('cmd_options');
break;
case "themes":
gExtensionsViewController.doCommand('cmd_useTheme');
break;
}
}
function onThemeSelect(aEvent)
{
if (gWindowState != "themes")
return;
var previewImageDeck = document.getElementById("previewImageDeck");
if (!gExtensionsView.selectedItem) {
previewImageDeck.setAttribute("selectedIndex", "0");
return;
}
var url = gExtensionsView.selectedItem.getAttribute("previewImage");
if (url) {
previewImageDeck.setAttribute("selectedIndex", "2");
var previewImage = document.getElementById("previewImage");
previewImage.setAttribute("src", url);
}
else
previewImageDeck.setAttribute("selectedIndex", "1");
}
///////////////////////////////////////////////////////////////////////////////
// View Context Menus
var gExtensionContextMenus = ["menuitem_options", "menuitem_homepage", "menuitem_about",
"menuseparator_1", "menuitem_uninstall", "menuitem_update",
"menuitem_enable", "menuitem_disable", "menuseparator_2",
"menuitem_moveTop", "menuitem_moveUp", "menuitem_moveDn"];
var gThemeContextMenus = ["menuitem_useTheme", "menuitem_homepage", "menuitem_about",
"menuseparator_1", "menuitem_uninstall", "menuitem_update",
"menuitem_enable"];
function buildContextMenu(aEvent)
{
if (aEvent.target.id != "extensionContextMenu")
return false;
var popup = document.getElementById("extensionContextMenu");
while (popup.hasChildNodes())
popup.removeChild(popup.firstChild);
var isExtensions = gWindowState == "extensions";
var menus = isExtensions ? gExtensionContextMenus : gThemeContextMenus;
for (var i = 0; i < menus.length; ++i) {
var clonedMenu = document.getElementById(menus[i]).cloneNode(true);
clonedMenu.id = clonedMenu.id + "_clone";
popup.appendChild(clonedMenu);
}
var extensionsStrings = document.getElementById("extensionsStrings");
var menuitem_about = document.getElementById("menuitem_about_clone");
var selectedItem = gExtensionsView.selectedItem;
var name = selectedItem ? selectedItem.getAttribute("name") : "";
menuitem_about.setAttribute("label", extensionsStrings.getFormattedString("aboutExtension", [name]));
if (isExtensions) {
var canEnable = gExtensionsViewController.isCommandEnabled("cmd_reallyEnable");
var menuitemToShow, menuitemToHide;
if (canEnable) {
menuitemToShow = document.getElementById("menuitem_enable_clone");
menuitemToHide = document.getElementById("menuitem_disable_clone");
}
else {
menuitemToShow = document.getElementById("menuitem_disable_clone");
menuitemToHide = document.getElementById("menuitem_enable_clone");
}
menuitemToShow.hidden = false;
menuitemToHide.hidden = true;
}
else {
var enableMenu = document.getElementById("menuitem_enable_clone");
if (gExtensionsView.selectedItem &&
(gExtensionsView.selectedItem.getAttribute("compatible") == "false" ||
gExtensionsView.selectedItem.disabled))
// don't let the user activate incompatible themes, but show a (disabled) Enable
// menuitem to give visual feedback; it's disabled because cmd_enable returns false
enableMenu.hidden = false;
else {
enableMenu.hidden = true;
}
}
return true;
}
///////////////////////////////////////////////////////////////////////////////
// Drag and Drop
var gExtensionsDNDObserver =
{
_ioServ: null,
_canDrop: false,
_ensureServices: function ()
{
if (!this._ioServ)
this._ioServ = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
},
// returns a JS object whose properties are used by xpinstall
_getDataFromDragSession: function (aDragSession, aPosition)
{
var fileData = { };
// if this fails we do not have valid data to drop
try {
var xfer = Components.classes["@mozilla.org/widget/transferable;1"]
.createInstance(Components.interfaces.nsITransferable);
xfer.addDataFlavor("text/x-moz-url");
xfer.addDataFlavor("application/x-moz-file", "nsIFile");
aDragSession.getData(xfer, aPosition);
var flavour = { }, data = { }, length = { };
xfer.getAnyTransferData(flavour, data, length);
var selectedFlavour = this.getSupportedFlavours().flavourTable[flavour.value];
var xferData = new FlavourData(data.value, length.value, selectedFlavour);
var fileURL = transferUtils.retrieveURLFromData(xferData.data,
xferData.flavour.contentType);
fileData.fileURL = fileURL;
var uri = this._ioServ.newURI(fileURL, null, null);
var url = uri.QueryInterface(Components.interfaces.nsIURL);
fileData.fileName = url.fileName;
switch (url.fileExtension) {
case "xpi":
fileData.type = nsIUpdateItem.TYPE_EXTENSION;
break;
case "jar":
fileData.type = nsIUpdateItem.TYPE_THEME;
break;
default:
return null;
}
}
catch (e) {
return null;
}
return fileData;
},
canDrop: function (aEvent, aDragSession) { return this._canDrop; },
onDragEnter: function (aEvent, aDragSession)
{
// XXXrstrong - bug 269568, GTK2 drag and drop is returning invalid data for
// dragenter and dragover. To workaround this we always set canDrop to true
// and just use the xfer data returned in ondrop which is valid.
#ifndef MOZ_WIDGET_GTK2
this._ensureServices();
var count = aDragSession.numDropItems;
for (var i = 0; i < count; ++i) {
var fileData = this._getDataFromDragSession(aDragSession, i);
if (!fileData) {
this._canDrop = false;
return;
}
}
#endif
this._canDrop = true;
},
onDragOver: function (aEvent, aFlavor, aDragSession) { },
onDrop: function(aEvent, aXferData, aDragSession)
{
this._ensureServices();
var xpinstallObj = { };
var themes = { };
var xpiCount = 0;
var themeCount = 0;
var count = aDragSession.numDropItems;
for (var i = 0; i < count; ++i) {
var fileData = this._getDataFromDragSession(aDragSession, i);
if (!fileData)
continue;
if (fileData.type == nsIUpdateItem.TYPE_EXTENSION) {
xpinstallObj[fileData.fileName] = fileData.fileURL;
++xpiCount;
}
else if (fileData.type == nsIUpdateItem.TYPE_THEME) {
themes[fileData.fileName] = fileData.fileURL;
++themeCount;
}
}
if (xpiCount > 0)
InstallTrigger.install(xpinstallObj);
if (themeCount > 0) {
// XXXrstrong Only allow the install of one theme due to bug 257992
for (var fileName in themes) {
InstallTrigger.installChrome(InstallTrigger.SKIN, themes[fileName], fileName);
break;
}
}
},
_flavourSet: null,
getSupportedFlavours: function ()
{
if (!this._flavourSet) {
this._flavourSet = new FlavourSet();
this._flavourSet.appendFlavour("text/x-moz-url");
this._flavourSet.appendFlavour("application/x-moz-file", "nsIFile");
}
return this._flavourSet;
}
};
///////////////////////////////////////////////////////////////////////////////
// Command Updating and Command Handlers
function canWriteToLocation(element)
{
var installLocation = null;
if (element) {
var id = getIDFromResourceURI(element.id)
installLocation = gExtensionManager.getInstallLocation(id);
}
return installLocation ? installLocation.canAccess : false;
}
function performUpdate()
{
gExtensionsViewController.commands.cmd_update(null);
}
var gExtensionsViewController = {
supportsCommand: function (aCommand)
{
var commandNode = document.getElementById(aCommand);
return commandNode && (commandNode.parentNode == document.getElementById("extensionsCommands"));
},
isCommandEnabled: function (aCommand)
{
var selectedItem = gExtensionsView.selectedItem;
if (selectedItem) {
if (selectedItem.getAttribute("downloadURL") != "")
return false;
var opType = selectedItem.getAttribute("opType");
}
switch (aCommand) {
case "cmd_close":
return true;
case "cmd_useTheme":
return selectedItem &&
!selectedItem.disabled &&
opType != OP_NEEDS_UNINSTALL &&
gCurrentTheme != selectedItem.getAttribute("internalName");
case "cmd_options":
return selectedItem &&
!selectedItem.disabled &&
!gApp.inSafeMode &&
opType == OP_NONE &&
selectedItem.getAttribute("optionsURL") != "";
case "cmd_about":
return selectedItem &&
opType != OP_NEEDS_INSTALL;
case "cmd_homepage":
return selectedItem && selectedItem.getAttribute("homepageURL") != "";
case "cmd_uninstall":
if (gWindowState != "extensions") {
// uninstall is never available for the default theme
return (selectedItem &&
selectedItem.getAttribute("internalName") != gDefaultTheme &&
opType != OP_NEEDS_UNINSTALL &&
selectedItem.getAttribute("locked") != "true" &&
canWriteToLocation(selectedItem));
}
return selectedItem &&
opType != OP_NEEDS_UNINSTALL &&
selectedItem.getAttribute("locked") != "true" &&
canWriteToLocation(selectedItem);
case "cmd_update":
return selectedItem &&
selectedItem.getAttribute("updateable") != "false" &&
!gExtensionsView.hasAttribute("update-check");
case "cmd_update_all":
return gExtensionsView.children.length > 0 &&
!gExtensionsView.hasAttribute("update-check");
case "cmd_reallyEnable":
// controls whether to show Enable or Disable in extensions' context menu
return selectedItem &&
(selectedItem.disabled &&
opType != OP_NEEDS_ENABLE ||
opType == OP_NEEDS_DISABLE ||
opType == OP_NEEDS_UNINSTALL);
case "cmd_enable":
//controls wheter the Enable/Disable menuitem is enabled
return selectedItem &&
opType != OP_NEEDS_ENABLE &&
opType != OP_NEEDS_INSTALL &&
opType != OP_NEEDS_UPGRADE &&
selectedItem.getAttribute("compatible") != "false";
case "cmd_disable":
return selectedItem &&
(opType == OP_NONE ||
opType == OP_NEEDS_ENABLE);
case "cmd_movetop":
return selectedItem && (gExtensionsView.children[0] != selectedItem);
case "cmd_moveup":
return selectedItem && (gExtensionsView.children[0] != selectedItem);
case "cmd_movedn":
var children = gExtensionsView.children;
return selectedItem && (children[children.length-1] != selectedItem);
#ifndef MOZ_PHOENIX
case "cmd_install":
return true;
#endif
}
return false;
},
doCommand: function (aCommand)
{
if (this.isCommandEnabled(aCommand))
this.commands[aCommand](gExtensionsView.selectedItem);
},
onCommandUpdate: function ()
{
var extensionsCommands = document.getElementById("extensionsCommands");
for (var i = 0; i < extensionsCommands.childNodes.length; ++i)
this.updateCommand(extensionsCommands.childNodes[i]);
},
updateCommand: function (command)
{
if (this.isCommandEnabled(command.id))
command.removeAttribute("disabled");
else
command.setAttribute("disabled", "true");
},
commands: {
cmd_close: function (aSelectedItem)
{
closeWindow(true);
},
cmd_useTheme: function (aSelectedItem)
{
var pref = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
gCurrentTheme = aSelectedItem.getAttribute("internalName");
// Set this pref so the user can reset the theme in safe mode
pref.setCharPref(PREF_EM_LAST_SELECTED_SKIN, gCurrentTheme);
if (pref.getBoolPref(PREF_EXTENSIONS_DSS_ENABLED)) {
pref.setCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN, gCurrentTheme);
}
else {
// Theme change will happen on next startup, this flag tells
// the Theme Manager that it needs to show "This theme will
// be selected after a restart" text in the selected theme
// item.
pref.setBoolPref(PREF_EXTENSIONS_DSS_SWITCHPENDING, true);
// Update the view
setRestartMessage(aSelectedItem);
}
// disable the useThemeButton
gExtensionsViewController.onCommandUpdate();
},
cmd_options: function (aSelectedItem)
{
if (!aSelectedItem) return;
var optionsURL = aSelectedItem.getAttribute("optionsURL");
if (optionsURL != "") {
var features;
try {
var prefs = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
var instantApply = prefs.getBoolPref("browser.preferences.instantApply");
features = "chrome,titlebar,toolbar,centerscreen" + (instantApply ? ",dialog=no" : ",modal");
}
catch (e) {
features = "chrome,titlebar,toolbar,centerscreen,modal";
}
openDialog(optionsURL, "", features);
}
},
cmd_homepage: function (aSelectedItem)
{
if (!aSelectedItem) return;
var homepageURL = aSelectedItem.getAttribute("homepageURL");
2005-11-07 04:49:19 +00:00
// only allow http(s) homepages
var scheme = "";
var uri = null;
try {
uri = makeURI(homepageURL);
scheme = uri.scheme;
} catch (ex) {}
if (uri && (scheme == "http" || scheme == "https"))
openURL(uri.spec);
},
cmd_about: function (aSelectedItem)
{
if (!aSelectedItem) return;
var aboutURL = aSelectedItem.getAttribute("aboutURL");
if (aboutURL != "")
openDialog(aboutURL, "", "chrome,modal");
else
openDialog("chrome://mozapps/content/extensions/about.xul", "", "chrome,modal", aSelectedItem.id, gExtensionsView.database);
},
cmd_movetop: function (aSelectedItem)
{
var movingID = aSelectedItem.id;
var moveTopID = gExtensionsView.children[0].id;
gExtensionManager.moveToIndexOf(movingID, moveTopID);
},
cmd_moveup: function (aSelectedItem)
{
var movingID = aSelectedItem.id;
var moveAboveID = aSelectedItem.previousSibling.id;
gExtensionManager.moveToIndexOf(movingID, moveAboveID);
},
cmd_movedn: function (aSelectedItem)
{
var movingID = aSelectedItem.id;
var moveBelowID = aSelectedItem.nextSibling.id;
gExtensionManager.moveToIndexOf(movingID, moveBelowID);
},
cmd_update: function (aSelectedItem)
{
var items = [];
if (!aSelectedItem)
items = gExtensionManager.getItemList(gItemType, { });
else {
var id = getIDFromResourceURI(aSelectedItem.id);
items = [gExtensionManager.getItemForID(id)];
}
var listener = new UpdateCheckListener();
gExtensionManager.update(items, items.length, false, listener);
},
cmd_update_all: function (aSelectedItem)
{
var items = gExtensionManager.getItemList(gItemType, { });
var listener = new UpdateCheckListener();
gExtensionManager.update(items, items.length, false, listener);
},
cmd_uninstall: function (aSelectedItem)
{
// Confirm the uninstall
var extensionsStrings = document.getElementById("extensionsStrings");
var brandStrings = document.getElementById("brandStrings");
var name = aSelectedItem.getAttribute("name");
var title = extensionsStrings.getFormattedString("queryUninstallTitle", [name]);
if (gWindowState == "extensions")
var message = extensionsStrings.getFormattedString("queryUninstallExtensionMessage", [name, name]);
else if (gWindowState == "themes")
message = extensionsStrings.getFormattedString("queryUninstallThemeMessage", [name]);
// XXXben - improve the wording on the buttons here!
var promptSvc = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
.getService(Components.interfaces.nsIPromptService);
if (!promptSvc.confirm(window, title, message))
return;
var selectedID = aSelectedItem.id;
// if no next item, go to the previous one
if (!gExtensionsView.goDown())
gExtensionsView.goUp();
if (gWindowState == "themes") {
// If the theme being uninstalled is the current theme, we need to reselect
// the default.
var pref = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
var currentTheme = pref.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN);
if (aSelectedItem.getAttribute("internalName") == currentTheme)
this.cmd_useTheme(document.getElementById(PREFIX_ITEM_URI + "{972ce4c6-7e08-4474-a285-3208198ce6fd}"));
}
gExtensionManager.uninstallItem(getIDFromResourceURI(selectedID));
},
cmd_disable: function (aSelectedItem)
{
gExtensionManager.disableItem(getIDFromResourceURI(aSelectedItem.id));
},
cmd_enable: function (aSelectedItem)
{
gExtensionManager.enableItem(getIDFromResourceURI(aSelectedItem.id));
#ifdef MOZ_PHOENIX
}
}
};
#else
},
cmd_install: function(aSelectedItem)
{
if (gWindowState == "extensions")
installExtension();
else
installSkin();
}
}
};
//////////////////////////////////////////////////////////////////////////
// functions to support installing of themes in apps other than firefox //
//////////////////////////////////////////////////////////////////////////
const nsIFilePicker = Components.interfaces.nsIFilePicker;
const nsIIOService = Components.interfaces.nsIIOService;
const nsIFileProtocolHandler = Components.interfaces.nsIFileProtocolHandler;
const nsIURL = Components.interfaces.nsIURL;
function installSkin()
{
// 1) Prompt the user for the location of the theme to install.
var extensionsStrings = document.getElementById("extensionsStrings");
var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
fp.init(window, extensionsStrings.getString("installThemePickerTitle"), nsIFilePicker.modeOpen);
fp.appendFilter(extensionsStrings.getString("themesFilter"), "*.jar");
fp.appendFilters(nsIFilePicker.filterAll);
var ret = fp.show();
if (ret == nsIFilePicker.returnOK)
{
var ioService = Components.classes['@mozilla.org/network/io-service;1'].getService(nsIIOService);
var fileProtocolHandler =
ioService.getProtocolHandler("file").QueryInterface(nsIFileProtocolHandler);
var url = fileProtocolHandler.newFileURI(fp.file).QueryInterface(nsIURL);
InstallTrigger.installChrome(InstallTrigger.SKIN, url.spec, decodeURIComponent(url.fileBaseName));
}
}
function installExtension()
{
var extensionsStrings = document.getElementById("extensionsStrings");
var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
fp.init(window, extensionsStrings.getString("installExtensionPickerTitle"), nsIFilePicker.modeOpen);
fp.appendFilter(extensionsStrings.getString("extensionFilter"), "*.xpi");
fp.appendFilters(nsIFilePicker.filterAll);
var ret = fp.show();
if (ret == nsIFilePicker.returnOK)
{
var ioService = Components.classes['@mozilla.org/network/io-service;1'].getService(nsIIOService);
var fileProtocolHandler =
ioService.getProtocolHandler("file").QueryInterface(nsIFileProtocolHandler);
var url = fileProtocolHandler.newFileURI(fp.file).QueryInterface(nsIURL);
var xpi = {};
xpi[decodeURIComponent(url.fileBaseName)] = url.spec;
InstallTrigger.install(xpi);
}
}
#endif