Bug 271166 - Should be able to blacklist extensions centrally, in update.mozilla.org. ui-r=beltzner, r=darin, sr=mscott

This commit is contained in:
rob_strong%exchangecode.com 2006-03-08 21:54:27 +00:00
Родитель 16bb5b7281
Коммит 5d79d38268
13 изменённых файлов: 819 добавлений и 97 удалений

Просмотреть файл

@ -67,6 +67,12 @@ pref("extensions.ignoreMTimeChanges", false);
// Enables some extra Extension System Logging (can reduce performance)
pref("extensions.logging.enabled", false);
// Blocklist preferences
pref("extensions.blocklist.enabled", true);
pref("extensions.blocklist.interval", 86400);
pref("extensions.blocklist.url", "https://addons.mozilla.org/blocklist/1/%APP_ID%/%APP_VERSION%/");
pref("extensions.blocklist.detailsURL", "http://www.mozilla.com/blocklist/");
// App-specific update preferences
// Whether or not app updates are enabled

Просмотреть файл

@ -105,6 +105,12 @@ pref("app.update.timer", 600000);
// which tells users what's new in this new update.
pref("app.update.showInstalledUI", false);
// Blocklist preferences
pref("extensions.blocklist.enabled", true);
pref("extensions.blocklist.interval", 86400);
pref("extensions.blocklist.url", "https://addons.mozilla.org/blocklist/1/%APP_ID%/%APP_VERSION%/");
pref("extensions.blocklist.detailsURL", "http://www.mozilla.com/blocklist/");
// Developers can set this to |true| if they are constantly changing files in their
// extensions directory so that the extension system does not constantly think that
// their extensions are being updated and thus reregistered every time the app is started

Просмотреть файл

@ -50,7 +50,7 @@
<!ENTITY optionsUnix.tooltip "Preferences">
<!ENTITY about.tooltip "About">
<!ENTITY homepage.tooltip "Home Page">
<!ENTITY moreInfo.label "More Information">
<!ENTITY installNow.label "Update Now">
<!ENTITY getMoreExtensions.label "Get More Extensions">

Просмотреть файл

@ -15,6 +15,7 @@ restartBeforeEnableTitle=Enable Extension
restartBeforeDisableTitle=Disable Extension
restartBeforeEnableMessage=%S will be enabled when %S is restarted.
restartBeforeDisableMessage=%S will be disabled when %S is restarted.
restartBeforeDisableBlocklisted=%S will be disabled when %S is restarted for your protection.
restartBeforeUninstallTitle=Uninstall
restartBeforeUninstallMessage=%S will be uninstalled when %S is restarted.
restartBeforeInstallMessage=%S will be installed when %S is restarted.
@ -29,6 +30,9 @@ disabledBySafeMode=%S is disabled by safe mode.
uninstallButton=Uninstall
disableButton=Disable
cancelButton=Cancel
restartButton=Restart %S
laterButton=Later
moreInfoText=More information
uninstallTitle=Uninstall %S
uninstallWarningDependMessage=If you uninstall %S, the functionality it offers will no longer be available and the following items that require this extension will be disabled:
uninstallWarningMessage=If you uninstall %S, the functionality it offers will no longer be available.
@ -67,11 +71,18 @@ incompatibleOlder=versions 0.8 or older.
incompatibleThemeName=this Theme
incompatibleExtension=Disabled - not compatible with %S %S
needsDependenciesDisabled=Disabled - requires additional items to operate.
blocklistedDisabled=Disabled for your protection
invalidGUIDMessage="%S" could not be installed because of an error in its Install Manifest ("%S" is not a valid GUID). Please contact the author of this item about the problem.
invalidVersionMessage="%S" could not be installed because of an error in its Install Manifest ("%S" is not a valid Version String). Please contact the author of this item about the problem.
incompatiblePlatformMessage="%S" could not be installed because it is not compatible with your %S build type (%S). Please contact the author of this item about the problem.
blocklistedInstallTitle=This extension is not secure
blocklistedInstallMsg=The extension %S is known to be dangerous, and can't be installed.
blocklistNotifyTitle=Some of your extensions aren't secure
blocklistNotifyMsg=A security update to %S has indicated that one or more of your extensions are no longer considered safe.
blocklistRestartMsg=You should restart %S so that these extensions can be disabled.
missingFileTitle=Missing File
missingFileMessage=%S could not load this item because the file %S was missing.
missingFileConsoleMessage=Failed to install from %S because %S was not provided at the top level of the jar/xpi file.

Просмотреть файл

@ -153,7 +153,7 @@ function Startup()
gExtensionsView.setAttribute("state", gWindowState);
gExtensionManager = Components.classes["@mozilla.org/extensions/manager;1"]
.getService(Components.interfaces.nsIExtensionManager)
.QueryInterface(Components.interfaces.nsIExtensionManager2);
.QueryInterface(Components.interfaces.nsIExtensionManager_MOZILLA_1_8_BRANCH);
gApp = Components.classes["@mozilla.org/xre/app-info;1"].getService(Components.interfaces.nsIXULAppInfo)
.QueryInterface(Components.interfaces.nsIXULRuntime);
@ -218,10 +218,12 @@ function Startup()
// 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")));
}
if (!win.hasAttribute("width"))
win.setAttribute("width", extensionsStrings.getString(
isExtensions ? "extensionsManagerWidth" : "themesManagerWidth"));
if (!win.hasAttribute("height"))
win.setAttribute("height", extensionsStrings.getString(
isExtensions ? "extensionsManagerHeight" : "themesManagerHeight"));
// Now look and see if we're being opened by XPInstall
gDownloadManager = new XPInstallDownloadManager();
@ -520,6 +522,7 @@ UpdateCheckListener.prototype = {
[brandName]),
message2: strings.getString("updatesAvailableMessage2"),
title: strings.getString("updatesAvailableTitle"),
iconClass: "question-icon",
buttons: {
accept: { label: strings.getString("updatesAvailableAccept"),
focused: true },
@ -536,8 +539,8 @@ UpdateCheckListener.prototype = {
this._addons[i].version]);
names.push(name);
}
openDialog("chrome://mozapps/content/extensions/list.xul", "",
"titlebar,modal", names, params);
window.openDialog("chrome://mozapps/content/extensions/list.xul", "",
"titlebar,centerscreen,modal", names, params);
if (params.result == "accept")
gExtensionManager.addDownloads(this._addons, this._addons.length, true);
}
@ -672,6 +675,7 @@ function buildContextMenu(aEvent)
var enableMenu = document.getElementById("menuitem_enable_clone");
if (gExtensionsView.selectedItem &&
(gExtensionsView.selectedItem.getAttribute("compatible") == "false" ||
gExtensionsView.selectedItem.getAttribute("blocklisted") == "true" ||
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
@ -881,7 +885,7 @@ var gExtensionsViewController = {
canWriteToLocation(selectedItem);
case "cmd_cancelUninstall":
return selectedItem &&
opType == OP_NEEDS_UNINSTALL;
opType == OP_NEEDS_UNINSTALL;
case "cmd_update":
return selectedItem &&
selectedItem.getAttribute("updateable") != "false" &&
@ -901,10 +905,12 @@ var gExtensionsViewController = {
(opType == OP_NONE ||
opType == OP_NEEDS_DISABLE) &&
selectedItem.getAttribute("satisfiesDependencies") != "false" &&
selectedItem.getAttribute("blocklisted") != "true" &&
selectedItem.getAttribute("compatible") != "false";
case "cmd_disable":
return selectedItem &&
selectedItem.getAttribute("satisfiesDependencies") != "false" &&
selectedItem.getAttribute("blocklisted") != "true" &&
(opType == OP_NONE ||
opType == OP_NEEDS_ENABLE);
case "cmd_movetop":
@ -1144,12 +1150,13 @@ var gExtensionsViewController = {
return;
}
gExtensionManager.disableItem(id);
gExtensionsView.selectedItem = document.getElementById(aSelectedItem.id);
gExtensionsViewController.onCommandUpdate();
},
cmd_enable: function (aSelectedItem)
{
gExtensionManager.enableItem(getIDFromResourceURI(aSelectedItem.id));
gExtensionsViewController.onCommandUpdate();
#ifdef MOZ_PHOENIX
}
}

Просмотреть файл

@ -89,7 +89,9 @@
</xul:hbox>
<xul:hbox>
<xul:image class=""/>
<xul:label class="extension-item-description" xbl:inherits="value=description" crop="right" flex="1"/>
<xul:label class="extension-item-description" xbl:inherits="value=description" crop="right"/>
<xul:label class="text-link extension-details-link"
xbl:inherits="href=blocklistDetailsURL" value="&moreInfo.label;"/>
</xul:hbox>
</xul:vbox>
<xul:vbox class="extension-install-button-box" pack="end">

Просмотреть файл

@ -184,6 +184,12 @@
<binding subject="?extension"
predicate="http://www.mozilla.org/2004/em-rdf#compatible"
object="?compatible"/>
<binding subject="?extension"
predicate="http://www.mozilla.org/2004/em-rdf#blocklisted"
object="?blocklisted"/>
<binding subject="?extension"
predicate="http://www.mozilla.org/2004/em-rdf#blocklistDetailsURL"
object="?blocklistDetailsURL"/>
<binding subject="?extension"
predicate="http://www.mozilla.org/2004/em-rdf#homepageURL"
object="?homepage-url"/>
@ -246,6 +252,8 @@
state="?state" progress="?progress" status="?status"
incompatibleUpdate="?incompatibleUpdate"
satisfiesDependencies="?satisfiesDependencies"
blocklisted="?blocklisted"
blocklistDetailsURL="?blocklistDetailsURL"
updateable="?updateable"/>
</action>
</rule>

Просмотреть файл

@ -21,6 +21,7 @@
#
# Contributor(s):
# Ben Goodger <ben@mozilla.org>
# Robert Strong <robert.bugzilla@gmail.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
@ -40,21 +41,30 @@ const kXULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
const kDialog = "dialog";
/**
* Initialize the dialog from the parameters supplied via window.arguments
* This dialog can be initialized from parameters supplied via window.arguments
* or it can be used to display blocklist notification and blocklist blocked
* installs via nsIDialogParamBlock as is done by nsIExtensionManager.
*
* When using this dialog with window.arguments it must be opened modally, the
* caller can inspect the user action after the dialog closes by inspecting the
* value of the |result| parameter on this object which is set to the dlgtype
* of the button used to close the dialog.
*
* This dialog must be opened modally, the caller can inspect the user action
* after the dialog closes by inspecting the value of the |result| parameter
* on this object which is set to the dlgtype of the button used to close
* the dialog.
*
* window.arguments[0] is an array of strings to display
* window.arguments[0] is an array of strings to display in the tree. If the
* array is empty the tree will not be displayed.
* window.arguments[1] a JS Object with the following properties:
*
* title: A title string, to be displayed in the title bar of the dialog.
* message1: A message string, displayed above the addon list
* message2: A message string, displayed below the addon list
* message3: A bolded message string, displayed below the addon list
* If no value is supplied for the message, it is not displayed.
* moreInfoURL: An url for displaying more information
* iconClass : Can be one of the following values (default is alert-icon)
* alert-icon, error-icon, or question-icon
*
* If no value is supplied for message1, message2, message3, or moreInfoURL,
* the element is not displayed.
*
* buttons: {
* accept: { label: "A Label for the Accept button",
* focused: true },
@ -64,14 +74,76 @@ const kDialog = "dialog";
*
* result: The dlgtype of button that was used to dismiss the dialog.
*/
var gButtons = { };
function init() {
// Fill the addons list
var items = window.arguments[0];
var de = document.documentElement;
var items = [];
if (window.arguments[0] instanceof Components.interfaces.nsIDialogParamBlock) {
var args = window.arguments[0];
var fromInstall = args.GetInt(0) == 1 ? true : false;
var numberOfItems = args.GetInt(1);
for (var i = 0; i < numberOfItems; ++i)
items.push(args.GetString(i));
var extensionsBundle = document.getElementById("extensionsBundle");
var pref = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
try {
var url = pref.getCharPref("extensions.blocklist.detailsURL");
}
catch (e) { }
if (fromInstall) { // Blocklist blocked install
var params = {
message1: extensionsBundle.getFormattedString("blocklistedInstallMsg",
[items[0]]),
moreInfoURL: url,
title: extensionsBundle.getString("blocklistedInstallTitle")
};
items = [];
var button = document.getElementById("centeredButton");
button.setAttribute("dlgtype", "accept");
de.buttons = "accept";
de.getButton("accept").focus();
}
else { // Blocklist background notification
// only hide when not used due to focus issues
document.getElementById("buttonCenteredBox").hidden = true;
var brandBundle = document.getElementById("brandBundle");
var brandShortName = brandBundle.getString("brandShortName");
params = {
message1: extensionsBundle.getFormattedString("blocklistNotifyMsg",
[brandShortName]),
message2: extensionsBundle.getFormattedString("blocklistRestartMsg",
[brandShortName]),
moreInfoURL: url,
title: extensionsBundle.getString("blocklistNotifyTitle")
};
de.buttons = "extra1,cancel";
button = de.getButton("cancel");
button.label = extensionsBundle.getString("laterButton");
de.setAttribute("ondialogextra1", "restartApp();");
button.focus();
button = de.getButton("extra1");
button.label = extensionsBundle.getFormattedString("restartButton",
[brandShortName]);
}
}
else {
// only hide when not used due to focus issues
document.getElementById("buttonCenteredBox").hidden = true;
items = window.arguments[0];
params = window.arguments[1];
}
var addons = document.getElementById("addonsChildren");
if (items.length > 0)
document.getElementById("addonsTree").hidden = false;
for (var i = 0; i < items.length; ++i) {
// Fill the addons list
for (i = 0; i < items.length; ++i) {
var treeitem = document.createElementNS(kXULNS, "treeitem");
var treerow = document.createElementNS(kXULNS, "treerow");
var treecell = document.createElementNS(kXULNS, "treecell");
@ -80,13 +152,10 @@ function init() {
treeitem.appendChild(treerow);
addons.appendChild(treeitem);
}
var de = document.documentElement;
var params = window.arguments[1];
// Set the messages
var messages = ["message1", "message2", "message3"];
for (var i = 0; i < messages.length; ++i) {
for (i = 0; i < messages.length; ++i) {
if (messages[i] in params) {
var message = document.getElementById(messages[i]);
message.hidden = false;
@ -94,28 +163,50 @@ function init() {
}
}
document.getElementById("infoIcon").className =
params["iconClass"] ? "spaced " + params["iconClass"] : "spaced alert-icon";
if ("moreInfoURL" in params && params["moreInfoURL"]) {
message = document.getElementById("moreInfo");
message.value = extensionsBundle.getString("moreInfoText");
message.setAttribute("href", params["moreInfoURL"]);
document.getElementById("moreInfoBox").hidden = false;
}
// Set the window title
if ("title" in params)
document.title = params.title;
// Set up the buttons
if ("buttons" in params) {
var buttons = params.buttons;
gButtons = params.buttons;
var buttonString = "";
for (var buttonType in buttons)
for (var buttonType in gButtons)
buttonString += "," + buttonType;
dump("*** de.toSource = " + de.localName + "\n");
de.buttons = buttonString.substr(1);
for (buttonType in buttons) {
var button = de.getButton(buttonType);
button.label = buttons[buttonType].label;
if (buttons[buttonType].focused)
for (buttonType in gButtons) {
button = de.getButton(buttonType);
button.label = gButtons[buttonType].label;
if (gButtons[buttonType].focused)
button.focus();
document.addEventListener(kDialog + buttonType, handleButtonCommand, true);
}
}
}
function shutdown() {
for (buttonType in gButtons)
document.removeEventListener(kDialog + buttonType, handleButtonCommand, true);
}
//XXXrstrong this should use session restore - bug 328154
function restartApp() {
Components.classes["@mozilla.org/toolkit/app-startup;1"]
.getService(Components.interfaces.nsIAppStartup)
.quit(Components.interfaces.nsIAppStartup.eRestart |
Components.interfaces.nsIAppStartup.eAttemptQuit);
}
/**
* Watch for the user hitting one of the buttons to dismiss the dialog
* and report the result back to the caller through the |result| property on

Просмотреть файл

@ -22,6 +22,7 @@
#
# Contributor(s):
# Ben Goodger <ben@mozilla.org>
# Robert Strong <robert.bugzilla@gmail.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
@ -41,7 +42,7 @@
<dialog id="addonList" windowtype="Addons:List"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
style="width: 35em;"
onunload="shutdown();"
buttons="accept,cancel" onload="init();">
<script type="application/x-javascript"
@ -49,18 +50,33 @@
<stringbundle id="extensionsBundle"
src="chrome://mozapps/locale/extensions/extensions.properties"/>
<label id="message1" hidden="true"/>
<separator class="thin"/>
<tree id="addonsTree" rows="10" hidecolumnpicker="true" hidden="true">
<treecols>
<treecol flex="1" id="nameColumn" hideheader="true"/>
</treecols>
<treechildren id="addonsChildren"/>
</tree>
<separator class="thin"/>
<label id="message2" hidden="true"/>
<separator class="thin"/>
<label class="bold" id="message3" hidden="true"/>
</dialog>
<stringbundle id="brandBundle"
src="chrome://branding/locale/brand.properties"/>
<hbox align="start">
<vbox>
<image id="infoIcon"/>
</vbox>
<vbox class="spaced" style="width: 30em;">
<label id="message1" class="spaced" hidden="true"/>
<separator class="thin"/>
<tree id="addonsTree" rows="8" hidecolumnpicker="true" hidden="true" class="spaced">
<treecols>
<treecol flex="1" id="nameColumn" hideheader="true"/>
</treecols>
<treechildren id="addonsChildren"/>
</tree>
<label id="message2" class="spaced" hidden="true"/>
<label class="bold spaced" id="message3" hidden="true"/>
<hbox id="moreInfoBox" hidden="true">
<label id="moreInfo" class="text-link spaced"/>
<spacer flex="1"/>
</hbox>
</vbox>
</hbox>
<hbox id="buttonCenteredBox">
<spacer flex="1"/>
<button id="centeredButton"/>
<spacer flex="1"/>
</hbox>
</dialog>

Просмотреть файл

@ -372,8 +372,8 @@ interface nsIExtensionManager : nsISupports
* interfaces for Gecko 1.8.1. After the 1.8.1 release this interface should
* not be used.
*/
[scriptable, uuid(8058922f-9367-45d1-878e-7c5ca27eddf4)]
interface nsIExtensionManager2 : nsIExtensionManager
[scriptable, uuid(126d544a-5f34-4277-a3bc-2785ee85a046)]
interface nsIExtensionManager_MOZILLA_1_8_BRANCH : nsIExtensionManager
{
/**
* Cancels a pending uninstall of an item
@ -398,6 +398,17 @@ interface nsIExtensionManager2 : nsIExtensionManager
in boolean includeDisabled,
out unsigned long itemCount,
[retval, array, size_is(itemCount)] out nsIUpdateItem items);
/**
* Checks for changes to the blocklist using the local blocklist file,
* application disables / enables items that have been added / removed from
* the blocklist, and if there are additions to the blocklist this will
* inform the user by displaying a list of the items added.
*
* XXXrstrong - this method is not terribly useful and was added so we can
* trigger this check from the additional timer used by blocklisting.
*/
void checkForBlocklistChanges();
};
/**

Просмотреть файл

@ -72,6 +72,10 @@ const PREF_GENERAL_SKINS_SELECTEDSKIN = "general.skins.selectedSkin";
const PREF_EM_LOGGING_ENABLED = "extensions.logging.enabled";
const PREF_EM_UPDATE_INTERVAL = "extensions.update.interval";
const PREF_XPINSTALL_STATUS_DLG_SKIN = "xpinstall.dialog.progress.skin";
const PREF_BLOCKLIST_URL = "extensions.blocklist.url";
const PREF_BLOCKLIST_DETAILS_URL = "extensions.blocklist.detailsURL";
const PREF_BLOCKLIST_ENABLED = "extensions.blocklist.enabled";
const PREF_BLOCKLIST_INTERVAL = "extensions.blocklist.interval";
const DIR_EXTENSIONS = "extensions";
const DIR_CHROME = "chrome";
@ -83,12 +87,14 @@ const FILE_AUTOREG = ".autoreg";
const FILE_INSTALL_MANIFEST = "install.rdf";
const FILE_CONTENTS_MANIFEST = "contents.rdf";
const FILE_CHROME_MANIFEST = "chrome.manifest";
const FILE_BLOCKLIST = "blocklist.xml";
const UNKNOWN_XPCOM_ABI = "unknownABI";
const FILE_LOGFILE = "extensionmanager.log";
const FILE_DEFAULT_THEME_JAR = "classic.jar";
const TOOLKIT_ID = "toolkit@mozilla.org"
const KEY_PROFILEDIR = "ProfD";
const KEY_PROFILEDS = "ProfDS";
@ -124,6 +130,7 @@ const RDFURI_INSTALL_MANIFEST_ROOT = "urn:mozilla:install-manifest";
const RDFURI_ITEM_ROOT = "urn:mozilla:item:root"
const RDFURI_DEFAULT_THEME = "urn:mozilla:item:{972ce4c6-7e08-4474-a285-3208198ce6fd}";
const XMLURI_PARSE_ERROR = "http://www.mozilla.org/newlayout/xml/parsererror.xml"
const XMLURI_BLOCKLIST = "http://www.mozilla.org/2006/addons-blocklist";
const URI_GENERIC_ICON_XPINSTALL = "chrome://mozapps/skin/xpinstall/xpinstallItemGeneric.png";
const URI_GENERIC_ICON_THEME = "chrome://mozapps/skin/extensions/themeGeneric.png";
@ -133,6 +140,7 @@ const URI_EXTENSIONS_PROPERTIES = "chrome://mozapps/locale/extensions/exte
const URI_BRAND_PROPERTIES = "chrome://branding/locale/brand.properties";
const URI_DOWNLOADS_PROPERTIES = "chrome://mozapps/locale/downloads/downloads.properties";
const URI_EXTENSION_UPDATE_DIALOG = "chrome://mozapps/content/extensions/update.xul";
const URI_EXTENSION_LIST_DIALOG = "chrome://mozapps/content/extensions/list.xul";
const INSTALLERROR_SUCCESS = 0;
const INSTALLERROR_INVALID_VERSION = -1;
@ -140,7 +148,9 @@ const INSTALLERROR_INVALID_GUID = -2;
const INSTALLERROR_INCOMPATIBLE_VERSION = -3;
const INSTALLERROR_PHONED_HOME = -4;
const INSTALLERROR_INCOMPATIBLE_PLATFORM = -5;
const INSTALLERROR_BLOCKLISTED = -6;
const MODE_RDONLY = 0x01;
const MODE_WRONLY = 0x02;
const MODE_CREATE = 0x08;
const MODE_APPEND = 0x10;
@ -744,6 +754,37 @@ function showMessage(titleKey, titleParams, messageKey, messageParams) {
ps.alert(null, title, message);
}
/**
* Shows a dialog for blocklisted items.
* @param items
* An array of nsIUpdateItems.
* @param fromInstall
* Whether this is called from an install or from the blocklist
* background check.
*/
function showBlocklistMessage(items, fromInstall) {
var win = null;
var params = Components.classes["@mozilla.org/embedcomp/dialogparam;1"]
.createInstance(Components.interfaces.nsIDialogParamBlock);
params.SetInt(0, (fromInstall ? 1 : 0));
params.SetInt(1, items.length);
params.SetNumberStrings(items.length * 2);
for (var i = 0; i < items.length; ++i)
params.SetString(i, items[i].name + " " + items[i].version);
// if this was initiated from an install try to find the appropriate manager
if (fromInstall) {
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
.getService(Components.interfaces.nsIWindowMediator);
win = wm.getMostRecentWindow(nsIUpdateItem.TYPE_THEME ? "Extension:Manager-themes" :
"Extension:Manager-extensions");
}
var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
.getService(Components.interfaces.nsIWindowWatcher);
ww.openWindow(win, URI_EXTENSION_LIST_DIALOG, "",
"chrome,centerscreen,modal,dialog,titlebar", params);
}
/**
* Gets a zip reader for the file specified.
* @param zipFile
@ -1988,7 +2029,7 @@ var PendingOperations = {
}
},
/**
/**
* Remove all Pending Operations of a certain type
* @param opType
* The type of Operation to remove all entries for
@ -2255,6 +2296,306 @@ var StartupCache = {
}
};
/**
* Manages the Blocklist. The Blocklist is a representation of the contents of
* blocklist.xml and allows us to remotely disable / re-enable blocklisted
* items managed by the Extension Manager with an item's appDisabled property.
*/
var Blocklist = {
/**
* Extension ID -> array of Version Ranges
* Each value in the version range array is a JS Object that has the
* following properties:
* "minVersion" The minimum version in a version range (default = 0)
* "maxVersion" The maximum version in a version range (default = *)
* "targetApps" Application ID -> array of Version Ranges
* (default = current application ID)
* Each value in the version range array is a JS Object that
* has the following properties:
* "minVersion" The minimum version in a version range
* (default = 0)
* "maxVersion" The maximum version in a version range
* (default = *)
*/
entries: null,
notify: function() {
if (getPref("getBoolPref", PREF_BLOCKLIST_ENABLED, true) == false)
return;
try {
var dsURI = gPref.getCharPref(PREF_BLOCKLIST_URL);
}
catch (e) {
LOG("Blocklist::notify: The " + PREF_BLOCKLIST_URL + " preference" +
" is missing!");
return;
}
dsURI = dsURI.replace(/%APP_ID%/g, gApp.ID);
dsURI = dsURI.replace(/%APP_VERSION%/g, gApp.version);
// Verify that the URI is valid
try {
var uri = newURI(dsURI);
}
catch (e) {
LOG("Blocklist::notify: There was an error creating the blocklist URI\r\n" +
"for: " + dsURI + ", error: " + e);
return;
}
var request = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
.createInstance(Components.interfaces.nsIXMLHttpRequest);
request.open("GET", uri.spec, true);
request.overrideMimeType("text/xml");
request.setRequestHeader("Cache-Control", "no-cache");
var self = this;
request.onerror = function(event) { self.onXMLError(event); };
request.onload = function(event) { self.onXMLLoad(event); };
request.send(null);
},
onXMLLoad: function(aEvent) {
var request = aEvent.target;
var responseXML = request.responseXML;
if (!responseXML || responseXML.documentElement.namespaceURI == XMLURI_PARSE_ERROR ||
(request.status != 200 && request.status != 0)) {
LOG("Blocklist::onXMLLoad: there was an error during load");
return;
}
var blocklistFile = getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]);
if (blocklistFile.exists())
blocklistFile.remove(false);
var fos = openSafeFileOutputStream(blocklistFile);
fos.write(request.responseText, request.responseText.length);
closeSafeFileOutputStream(fos);
this.entries = this._loadBlocklistFromFile(getFile(KEY_PROFILEDIR,
[FILE_BLOCKLIST]));
var em = Components.classes["@mozilla.org/extensions/manager;1"]
.getService(Components.interfaces.nsIExtensionManager)
.QueryInterface(Components.interfaces.nsIExtensionManager_MOZILLA_1_8_BRANCH);
em.checkForBlocklistChanges();
},
onXMLError: function(aEvent) {
try {
var request = aEvent.target;
// the following may throw (e.g. a local file or timeout)
var status = request.status;
}
catch (e) {
request = aEvent.target.channel.QueryInterface(Components.interfaces.nsIRequest);
status = request.status;
}
var statusText = request.statusText;
// When status is 0 we don't have a valid channel.
if (status == 0)
statusText = "nsIXMLHttpRequest channel unavailable";
LOG("Blocklist:onError: There was an error loading the blocklist file\r\n" +
statusText);
},
/**
* The blocklist XML file looks something like this:
*
* <blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
* <emItems>
* <emItem id="item_1@domain">
* <versionRange minVersion="1.0" maxVersion="2.0.*">
* <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
* <versionRange minVersion="1.5" maxVersion="1.5.*"/>
* <versionRange minVersion="1.7" maxVersion="1.7.*"/>
* </targetApplication>
* <targetApplication id="toolkit@mozilla.org">
* <versionRange minVersion="1.8" maxVersion="1.8.*"/>
* </targetApplication>
* </versionRange>
* <versionRange minVersion="3.0" maxVersion="3.0.*">
* <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
* <versionRange minVersion="1.5" maxVersion="1.5.*"/>
* </targetApplication>
* <targetApplication id="toolkit@mozilla.org">
* <versionRange minVersion="1.8" maxVersion="1.8.*"/>
* </targetApplication>
* </versionRange>
* </emItem>
* <emItem id="item_2@domain">
* <versionRange minVersion="3.1" maxVersion="4.*"/>
* </emItem>
* <emItem id="item_3@domain">
* <versionRange>
* <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
* <versionRange minVersion="1.5" maxVersion="1.5.*"/>
* </targetApplication>
* </versionRange>
* </emItem>
* <emItem id="item_4@domain">
* <versionRange>
* <targetApplication>
* <versionRange minVersion="1.5" maxVersion="1.5.*"/>
* </targetApplication>
* </versionRange>
* <emItem id="item_5@domain"/>
* </emItems>
* </blocklist>
*/
_loadBlocklistFromFile: function(file) {
if (getPref("getBoolPref", PREF_BLOCKLIST_ENABLED, true) == false) {
LOG("Blocklist::_loadBlocklistFromFile: blocklist is disabled");
return { };
}
if (!file.exists()) {
LOG("Blocklist::_loadBlocklistFromFile: XML File does not exist");
return { };
}
var result = { };
var fileStream = Components.classes["@mozilla.org/network/file-input-stream;1"]
.createInstance(Components.interfaces.nsIFileInputStream);
fileStream.init(file, MODE_RDONLY, PERMS_FILE, 0);
try {
var parser = Components.classes["@mozilla.org/xmlextras/domparser;1"]
.createInstance(Components.interfaces.nsIDOMParser);
var doc = parser.parseFromStream(fileStream, "UTF-8", file.fileSize, "text/xml");
if (doc.documentElement.namespaceURI != XMLURI_BLOCKLIST) {
LOG("Blocklist::_loadBlocklistFromFile: aborting due to incorrect " +
"XML Namespace.\r\nExpected: " + XMLURI_BLOCKLIST + "\r\n" +
"Received: " + doc.documentElement.namespaceURI);
return { };
}
const kELEMENT_NODE = Components.interfaces.nsIDOMNode.ELEMENT_NODE;
var itemNodes = this._getItemNodes(doc.documentElement.childNodes);
for (var i = 0; i < itemNodes.length; ++i) {
var blocklistElement = itemNodes.item(i);
if (blocklistElement.nodeType != kELEMENT_NODE ||
blocklistElement.localName != "emItem")
continue;
blocklistElement.QueryInterface(Components.interfaces.nsIDOMElement);
var versionNodes = blocklistElement.childNodes;
var id = blocklistElement.getAttribute("id");
result[id] = [];
for (var x = 0; x < versionNodes.length; ++x) {
var versionRangeElement = versionNodes.item(x);
if (versionRangeElement.nodeType != kELEMENT_NODE ||
versionRangeElement.localName != "versionRange")
continue;
result[id].push(new BlocklistItemData(versionRangeElement));
}
// if only the extension ID is specified block all versions of the
// extension for the current application.
if (result[id].length == 0)
result[id].push(new BlocklistItemData(null));
}
}
catch (e) {
LOG("Blocklist::_loadBlocklistFromFile: Error constructing blocklist " + e);
return { };
}
fileStream.close();
return result;
},
_getItemNodes: function(deChildNodes) {
const kELEMENT_NODE = Components.interfaces.nsIDOMNode.ELEMENT_NODE;
for (var i = 0; i < deChildNodes.length; ++i) {
var emItemsElement = deChildNodes.item(i);
if (emItemsElement.nodeType == kELEMENT_NODE ||
emItemsElement.localName == "emItems")
return emItemsElement.childNodes;
}
return [ ];
},
_ensureBlocklist: function() {
if (!this.entries)
this.entries = this._loadBlocklistFromFile(getFile(KEY_PROFILEDIR,
[FILE_BLOCKLIST]));
}
};
/**
* Helper for constructing a blocklist.
*/
function BlocklistItemData(versionRangeElement) {
var versionRange = this.getBlocklistVersionRange(versionRangeElement);
this.minVersion = versionRange.minVersion;
this.maxVersion = versionRange.maxVersion;
this.targetApps = { };
// Default to all versions of the extension and the current application when
// versionRange is not defined.
if (!versionRangeElement || versionRangeElement.childNodes.length == 0) {
this.targetApps[gApp.ID] = this.getBlocklistAppVersions(null);
return;
}
for (var i = 0; i < versionRangeElement.childNodes.length; ++i) {
var targetAppElement = versionRangeElement.childNodes.item(i);
if (targetAppElement.nodeType != Components.interfaces.nsIDOMNode.ELEMENT_NODE ||
targetAppElement.localName != "targetApplication")
continue;
// default to the current application if id is not provided.
var appID = targetAppElement.hasAttribute("id") ? targetAppElement.getAttribute("id") : gApp.ID;
this.targetApps[appID] = this.getBlocklistAppVersions(targetAppElement);
}
}
BlocklistItemData.prototype = {
/**
* Retrieves a version range (e.g. minVersion and maxVersion) for a
* blocklist item's targetApplication element.
* @param targetAppElement
* A targetApplication blocklist element.
* @returns An array of JS objects with the following properties:
* "minVersion" The minimum version in a version range (default = 0).
* "maxVersion" The maximum version in a version range (default = *).
*/
getBlocklistAppVersions: function(targetAppElement) {
var appVersions = [ ];
// return minVersion = 0 and maxVersion = * if not available
if (!targetAppElement || targetAppElement.childNodes.length == 0)
return [ this.getBlocklistVersionRange(null) ];
for (var i = 0; i < targetAppElement.childNodes.length; ++i) {
var versionRangeElement = targetAppElement.childNodes.item(i);
if (versionRangeElement.nodeType != Components.interfaces.nsIDOMNode.ELEMENT_NODE ||
versionRangeElement.localName != "versionRange")
continue;
appVersions.push(this.getBlocklistVersionRange(versionRangeElement));
}
return appVersions;
},
/**
* Retrieves a version range (e.g. minVersion and maxVersion) for a blocklist
* versionRange element.
* @param versionRangeElement
* The versionRange blocklist element.
* @returns A JS object with the following properties:
* "minVersion" The minimum version in a version range (default = 0).
* "maxVersion" The maximum version in a version range (default = *).
*/
getBlocklistVersionRange: function(versionRangeElement) {
var minVersion = "0";
var maxVersion = "*";
if (!versionRangeElement)
return { minVersion: minVersion, maxVersion: maxVersion };
if (versionRangeElement.hasAttribute("minVersion"))
minVersion = versionRangeElement.getAttribute("minVersion");
if (versionRangeElement.hasAttribute("maxVersion"))
maxVersion = versionRangeElement.getAttribute("maxVersion");
return { minVersion: minVersion, maxVersion: maxVersion };
}
};
/**
* Installs, manages and tracks compatibility for Extensions and Themes
* @constructor
@ -2493,6 +2834,9 @@ ExtensionManager.prototype = {
.getService(Components.interfaces.nsIUpdateTimerManager);
var interval = getPref("getIntPref", PREF_EM_UPDATE_INTERVAL, 86400);
tm.registerTimer("addon-background-update-timer", this, interval);
interval = getPref("getIntPref", PREF_BLOCKLIST_INTERVAL, 86400);
tm.registerTimer("blocklist-background-update-timer", Blocklist, interval);
},
/**
@ -2643,6 +2987,11 @@ ExtensionManager.prototype = {
callback(installManifest, installData.id, location, installData.type);
em._appDisableItem(id);
}
else if (installData.error == INSTALLERROR_BLOCKLISTED) {
LOG("... success, item installed but is blocklisted");
callback(installManifest, installData.id, location, installData.type);
em._appDisableItem(id);
}
else {
/**
* Turns an error code into a message for logging
@ -3190,21 +3539,20 @@ ExtensionManager.prototype = {
while (elements.hasMoreElements()) {
var itemResource = elements.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
// The value of appDisabled is determined by an item being
// compatible and satisfying its dependencies.
// appDisabled is determined by an item being compatible,
// satisfying its dependencies, and not being blocklisted
id = stripPrefix(itemResource.Value, PREFIX_ITEM_URI);
if (ds.getItemProperty(id, "compatible") == "true" &&
ds.getItemProperty(id, "blocklisted") == "false" &&
ds.getItemProperty(id, "satisfiesDependencies") == "true")
ds.setItemProperty(id, EM_R("appDisabled"), null);
else
ds.setItemProperty(id, EM_R("appDisabled"), EM_L("true"));
// The value of userDisabled is set based on the value being
// OP_NEEDS_ENABLE or OP_NEEDS_DISABLE. This allows us to have an
// item to be enabled by the app and disabled by the user during
// one restart.
var target = ds.GetTarget(itemResource, EM_R("userDisabled"), true);
var value = stringData(target);
// userDisabled is set based on its value being OP_NEEDS_ENABLE or
// OP_NEEDS_DISABLE. This allows us to have an item to be enabled
// by the app and disabled by the user during a single restart.
var value = stringData(ds.GetTarget(itemResource, EM_R("userDisabled"), true));
if (value == OP_NEEDS_ENABLE)
ds.setItemProperty(id, EM_R("userDisabled"), null);
else if (value == OP_NEEDS_DISABLE)
@ -3290,13 +3638,21 @@ ExtensionManager.prototype = {
}
ds.endUpdateBatch();
// Disable all incompatible items and let update enable them if appropriate.
var currAppID = gApp.ID;
var items = ds.getIncompatibleItemList(currAppID, currAppVersion,
nsIUpdateItem.TYPE_ADDON, true);
for (var i = 0; i < items.length; ++i)
ds.setItemProperty(items[i].id, EM_R("appDisabled"), EM_L("true"));
// Update the manifests to reflect the items that were disabled.
var ctr = getContainer(ds, ds._itemRoot);
var elements = ctr.GetElements();
while (elements.hasMoreElements()) {
var itemResource = elements.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
// appDisabled is determined by an item being compatible,
// satisfying its dependencies, and not being blocklisted
var id = stripPrefix(itemResource.Value, PREFIX_ITEM_URI);
if (ds.getItemProperty(id, "compatible") == "true" &&
ds.getItemProperty(id, "blocklisted") == "false" &&
ds.getItemProperty(id, "satisfiesDependencies") == "true")
ds.setItemProperty(id, EM_R("appDisabled"), null);
else
ds.setItemProperty(id, EM_R("appDisabled"), EM_L("true"));
}
// Update the manifests to reflect the items that were disabled / enabled.
this._updateManifests(true);
// Always check for compatibility updates when upgrading
@ -3563,7 +3919,7 @@ ExtensionManager.prototype = {
var extensionSectionHeader = "[ExtensionDirs]\r\n";
fos.write(extensionSectionHeader, extensionSectionHeader.length);
for (i = 0; i < validExtensions.length; ++i) {
for (var i = 0; i < validExtensions.length; ++i) {
var e = validExtensions[i];
var itemLocation = e.location.getItemLocation(e.id).QueryInterface(nsILocalFile);
var descriptor = getAbsoluteDescriptor(itemLocation);
@ -3639,6 +3995,8 @@ ExtensionManager.prototype = {
* INSTALLERROR_INCOMPATIBLE_PLATFORM
* error, item is not compatible with the operating
* system or ABI the application was built for.
* INSTALLERROR_BLOCKLISTED
* error, item is blocklisted
*/
_getInstallData: function(installManifest) {
var installData = { id : "",
@ -3736,6 +4094,11 @@ ExtensionManager.prototype = {
if (!this.datasource.isCompatible(installManifest, gInstallManifestRoot, undefined))
installData.error = INSTALLERROR_INCOMPATIBLE_VERSION;
// Check if the item is blocklisted.
if (this.datasource.isBlocklisted(installData.id, installData.version,
undefined, undefined))
installData.error = INSTALLERROR_BLOCKLISTED;
return installData;
},
@ -4199,6 +4562,11 @@ ExtensionManager.prototype = {
"incompatiblePlatformMessage",
[installData.name, BundleManager.appName, osABI]);
break;
case INSTALLERROR_BLOCKLISTED:
LOG("Blocklisted Item: Item: \"" + installData.id + "\" version " +
installData.version + " was not installed.");
showBlocklistMessage([installData], true);
break;
default:
break;
}
@ -4556,7 +4924,7 @@ ExtensionManager.prototype = {
},
/**
* Note on appDisabled and userDisable property arcs.
* Note on appDisabled and userDisabled property arcs.
* The appDisabled and userDisabled RDF property arcs are used to store
* the pending operation for app disabling and user disabling for an item as
* well as the user and app disabled status after the pending operation has
@ -4573,7 +4941,7 @@ ExtensionManager.prototype = {
* property value will be set to OP_NEEDS_ENABLE. The item's pending
* operations are then evaluated in order to set the operation to perform
* and notify the observers if the operation has been changed.
* See "Note on appDisabled and userDisable property arcs" above.
* See "Note on appDisabled and userDisabled property arcs" above.
* @param id
* The ID of the item to be enabled by the application.
*/
@ -4640,7 +5008,7 @@ ExtensionManager.prototype = {
* property value will be set to OP_NEEDS_DISABLE. The item's pending
* operations are then evaluated in order to set the operation to perform
* and notify the observers if the operation has been changed.
* See "Note on appDisabled and userDisable property arcs" above.
* See "Note on appDisabled and userDisabled property arcs" above.
* @param id
* The ID of the item to be disabled by the application.
*/
@ -4660,7 +5028,8 @@ ExtensionManager.prototype = {
else if (userDisabled == OP_NEEDS_ENABLE)
ds.setItemProperty(id, EM_R("userDisabled"), EM_L("true"));
if (userDisabled == OP_NEEDS_ENABLE || ds.getItemProperty(id, "userDisabled") == "true")
if (appDisabled == OP_NEEDS_ENABLE || userDisabled == OP_NEEDS_ENABLE ||
ds.getItemProperty(id, "userDisabled") == "true")
ds.setItemProperty(id, EM_R("appDisabled"), EM_L("true"));
else if (appDisabled == OP_NONE)
ds.setItemProperty(id, EM_R("appDisabled"), EM_L(OP_NEEDS_DISABLE));
@ -4702,7 +5071,7 @@ ExtensionManager.prototype = {
/**
* Sets an item to be enabled by the user. If the item is already enabled this
* clears the needs-enable operation for the next restart.
* See "Note on appDisabled and userDisable property arcs" above.
* See "Note on appDisabled and userDisabled property arcs" above.
* @param id
* The ID of the item to be enabled by the user.
*/
@ -4747,7 +5116,7 @@ ExtensionManager.prototype = {
/**
* Sets an item to be disabled by the user. If the item is already disabled
* this clears the needs-disable operation for the next restart.
* See "Note on appDisabled and userDisable property arcs" above.
* See "Note on appDisabled and userDisabled property arcs" above.
* @param id
* The ID of the item to be disabled by the user.
*/
@ -4802,6 +5171,7 @@ ExtensionManager.prototype = {
var dependentID = dependentItems[i].id;
ds.updateProperty(dependentID, "satisfiesDependencies");
if (ds.getItemProperty(dependentID, "satisfiesDependencies") == "true" &&
ds.getItemProperty(dependentID, "blocklisted") == "false" &&
ds.getItemProperty(dependentID, "compatible") == "true")
this._appEnableItem(dependentID);
else
@ -4833,6 +5203,42 @@ ExtensionManager.prototype = {
updater.checkForUpdates(items, items.length, versionUpdateOnly, listener);
},
/**
* Checks for changes to the blocklist using the local blocklist file,
* application disables / enables items that have been added / removed from
* the blocklist, and if there are additions to the blocklist this will
* inform the user by displaying a list of the items added.
*
* XXXrstrong - this method is not terribly useful and was added so we can
* trigger this check from the additional timer used by blocklisting.
*/
checkForBlocklistChanges: function() {
var ds = this.datasource;
var items = this.getItemList(nsIUpdateItem.TYPE_ADDON, { });
for (var i = 0; i < items.length; ++i) {
var id = items[i].id;
ds.updateProperty(id, "blocklisted");
ds.updateProperty(id, "blocklistDetailsURL");
var appDisabled = ds.getItemProperty(id, "appDisabled");
if ((appDisabled == "true" || appDisabled == OP_NEEDS_DISABLE) &&
ds.getItemProperty(id, "compatible") == "true" &&
ds.getItemProperty(id, "satisfiesDependencies") == "true" &&
ds.getItemProperty(id, "blocklisted") == "false" &&
ds.getItemProperty(id, "appDisabled") == "true")
this._appEnableItem(id);
}
items = ds.getBlocklistedItemList(null, null, nsIUpdateItem.TYPE_ADDON,
false);
for (i = 0; i < items.length; ++i)
this._appDisableItem(items[i].id);
// show the blocklist notification window if there are new blocklist items.
if (items.length > 0)
showBlocklistMessage(items, false);
},
/**
* @returns An enumeration of all registered Install Locations.
*/
@ -5233,7 +5639,7 @@ ExtensionManager.prototype = {
*/
QueryInterface: function(iid) {
if (!iid.equals(Components.interfaces.nsIExtensionManager) &&
!iid.equals(Components.interfaces.nsIExtensionManager2) &&
!iid.equals(Components.interfaces.nsIExtensionManager_MOZILLA_1_8_BRANCH) &&
!iid.equals(Components.interfaces.nsITimerCallback) &&
!iid.equals(Components.interfaces.nsIObserver) &&
!iid.equals(Components.interfaces.nsISupports))
@ -5493,6 +5899,10 @@ ExtensionItemUpdater.prototype = {
if (aRemoteItem.maxAppVersion &&
gVersionChecker.compare(appExtensionsVersion, aRemoteItem.maxAppVersion) > 0)
return false;
if (this._emDS.isBlocklisted(aRemoteItem.id, aRemoteItem.version,
undefined, undefined))
return false;
return true;
},
@ -5631,24 +6041,14 @@ RDFItemUpdater.prototype = {
onXMLLoad: function(aEvent, aItem) {
var request = aEvent.target;
var responseXML = request.responseXML;
if (responseXML)
var parseError = (responseXML.documentElement.namespaceURI == XMLURI_PARSE_ERROR);
// If AMO does not return responseXML it is not treated as a failure since
// items without an updateURL are checked on AMO. If there is an XML parse
// error, responseXML is null, status does NOT equal 200 or 0 (e.g. 200 is
// HTTP OK and 0 is returned for a local file) then we don't have valid data.
if (!responseXML || parseError || (request.status != 200 && request.status != 0)) {
// If the item does not have an updateRDF then the error is from UMO.
if (!aItem.updateRDF) {
this._updater.checkForDone(aItem,
nsIAddonUpdateCheckListener.STATUS_NONE);
}
else {
this._updater.checkForDone(aItem,
nsIAddonUpdateCheckListener.STATUS_FAILURE);
}
// If the item does not have an update RDF and returns an error it is not
// treated as a failure since all items without an updateURL are checked
// for updates on AMO even if they are not hosted there.
if (!responseXML || responseXML.documentElement.namespaceURI == XMLURI_PARSE_ERROR ||
(request.status != 200 && request.status != 0)) {
this._updater.checkForDone(aItem, (aItem.updateRDF ? nsIAddonUpdateCheckListener.STATUS_FAILURE :
nsIAddonUpdateCheckListener.STATUS_NONE));
return;
}
@ -5661,9 +6061,6 @@ RDFItemUpdater.prototype = {
this.onDatasourceLoaded(ds, aItem);
},
/**
*
*/
onXMLError: function(aEvent, aItem) {
try {
var request = aEvent.target;
@ -6037,7 +6434,64 @@ ExtensionsDataSource.prototype = {
}
return false;
},
/**
* Determine if an item is blocklisted
* @param id
* The id of the item to check.
* @param extVersion
* The item's version.
* @param appVersion
* The version of the application we are checking in the blocklist.
* If this parameter is undefined, the version of the running
* application is used.
* @param toolkitVersion
* The version of the toolkit we are checking in the blocklist.
* If this parameter is undefined, the version of the running
* toolkit is used.
* @returns true if the item is compatible with this version of the
* application, false, otherwise.
*/
isBlocklisted: function(id, extVersion, appVersion, toolkitVersion) {
if (appVersion === undefined)
appVersion = getPref("getCharPref", PREF_EM_APP_EXTENSIONS_VERSION,
gApp.version);
if (toolkitVersion === undefined)
toolkitVersion = gApp.platformVersion;
var blItem = Blocklist.entries[id];
if (!blItem)
return false;
var versionChecker = getVersionChecker();
for (var i = 0; i < blItem.length; ++i) {
if (versionChecker.compare(extVersion, blItem[i].minVersion) < 0 ||
versionChecker.compare(extVersion, blItem[i].maxVersion) > 0)
continue;
var blTargetApp = blItem[i].targetApps[gApp.ID];
if (blTargetApp) {
for (var x = 0; x < blTargetApp.length; ++x) {
if (versionChecker.compare(appVersion, blTargetApp[x].minVersion) < 0 ||
versionChecker.compare(appVersion, blTargetApp[x].maxVersion) > 0)
continue;
return true;
}
}
blTargetApp = blItem[i].targetApps[TOOLKIT_ID];
if (!blTargetApp)
return false;
for (x = 0; x < blTargetApp.length; ++x) {
if (versionChecker.compare(toolkitVersion, blTargetApp[x].minVersion) < 0 ||
versionChecker.compare(toolkitVersion, blTargetApp[x].maxVersion) > 0)
continue;
return true;
}
}
return false;
},
/**
* Gets a list of items that are incompatible with a specific application version.
* @param appID
@ -6084,6 +6538,44 @@ ExtensionsDataSource.prototype = {
return items;
},
/**
* Retrieves a list of items that will be blocklisted by the application for
* a specific application or toolkit version.
* @param appVersion
* The Version of the application to check the blocklist against.
* @param toolkitVersion
* The Version of the toolkit to check the blocklist against.
* @param desiredType
* The nsIUpdateItem type of items to look for
* @param includeAppDisabled
* Whether or not items that are or are already set to be disabled
* by the app on next restart should be included in the set returned
* @returns An array of nsIUpdateItems that are blocklisted with the application
* or toolkit version supplied.
*/
getBlocklistedItemList: function(appVersion, toolkitVersion, desiredType,
includeAppDisabled) {
var items = [];
var ctr = getContainer(this._inner, this._itemRoot);
var elements = ctr.GetElements();
while (elements.hasMoreElements()) {
var item = elements.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
var id = stripPrefix(item.Value, PREFIX_ITEM_URI);
var type = this.getItemProperty(id, "type");
if (!includeAppDisabled &&
(this.getItemProperty(id, "appDisabled") == "true" ||
this.getItemProperty(id, "appDisabled") == OP_NEEDS_DISABLE))
continue;
var extVersion = this.getItemProperty(id, "version");
if (type != -1 && (type & desiredType) &&
this.isBlocklisted(id, extVersion, appVersion, toolkitVersion))
items.push(this.getItemForID(id));
}
return items;
},
/**
* Gets a list of items of a specific type
* @param desiredType
@ -6879,6 +7371,7 @@ ExtensionsDataSource.prototype = {
* Load the Extensions Datasource from disk.
*/
loadExtensions: function() {
Blocklist._ensureBlocklist();
var extensionsFile = getFile(KEY_PROFILEDIR, [FILE_EXTENSIONS]);
try {
this._inner = gRDF.GetDataSourceBlocking(getURLSpecFromFile(extensionsFile));
@ -7042,6 +7535,17 @@ ExtensionsDataSource.prototype = {
return null;
},
_rdfGet_blocklistDetailsURL: function(item, property) {
var id = stripPrefix(item.Value, PREFIX_ITEM_URI);
if (this.getItemProperty(id, "blocklisted") == "false")
return null;
var url = getPref("getCharPref", PREF_BLOCKLIST_DETAILS_URL, "");
if (url)
return EM_L(url);
return null;
},
/**
* Get the em:compatible property (whether or not this item is compatible)
*/
@ -7061,6 +7565,48 @@ ExtensionsDataSource.prototype = {
}
return EM_L("true");
},
/**
* Get the em:blocklisted property (whether or not this item is blocklisted)
*/
_rdfGet_blocklisted: function(item, property) {
Blocklist._ensureBlocklist();
var id = stripPrefix(item.Value, PREFIX_ITEM_URI);
var blItem = Blocklist.entries[id];
if (!blItem)
return EM_L("false");
getVersionChecker();
var version = this.getItemProperty(id, "version");
var appVersion = getPref("getCharPref", PREF_EM_APP_EXTENSIONS_VERSION,
gApp.version);
for (var i = 0; i < blItem.length; ++i) {
if (gVersionChecker.compare(version, blItem[i].minVersion) < 0 ||
gVersionChecker.compare(version, blItem[i].maxVersion) > 0)
continue;
var blTargetApp = blItem[i].targetApps[gApp.ID];
if (blTargetApp) {
for (var x = 0; x < blTargetApp.length; ++x) {
if (gVersionChecker.compare(appVersion, blTargetApp[x].minVersion) < 0 ||
gVersionChecker.compare(appVersion, blTargetApp[x].maxVersion) > 0)
continue;
return EM_L("true");
}
}
blTargetApp = blItem[i].targetApps[TOOLKIT_ID];
if (!blTargetApp)
return EM_L("false");
for (x = 0; x < blTargetApp.length; ++x) {
if (gVersionChecker.compare(gApp.platformVersion, blTargetApp[x].minVersion) < 0 ||
gVersionChecker.compare(gApp.platformVersion, blTargetApp[x].maxVersion) > 0)
continue;
return EM_L("true");
}
}
return EM_L("false");
},
/**
* Gets the em:availableUpdateURL - the URL to an XPI update package, if
@ -7131,6 +7677,8 @@ ExtensionsDataSource.prototype = {
}
switch (opType) {
case OP_NEEDS_DISABLE:
if (this.getItemProperty(id, "blocklisted") == "true")
return getLiteral("restartBeforeDisableBlocklisted", [itemName, BundleManager.appName]);
return getLiteral("restartBeforeDisableMessage", [itemName, BundleManager.appName]);
case OP_NEEDS_ENABLE:
return getLiteral("restartBeforeEnableMessage", [itemName, BundleManager.appName]);
@ -7142,6 +7690,10 @@ ExtensionsDataSource.prototype = {
return getLiteral("restartBeforeUpgradeMessage", [itemName, BundleManager.appName]);
}
if (this.getItemProperty(id, "appDisabled") == "true" &&
this.getItemProperty(id, "blocklisted") == "true")
return getLiteral("blocklistedDisabled", [BundleManager.appName, gApp.version]);
if (this.getItemProperty(id, "appDisabled") == "true" &&
this.getItemProperty(id, "compatible") != "true")
return getLiteral("incompatibleExtension", [BundleManager.appName, gApp.version]);

Просмотреть файл

@ -65,6 +65,7 @@ extension[disabled="true"] {
}
extension[disabled="true"][compatible="false"] .extension-item-description,
extension[blocklisted="true"] .extension-item-description,
extension[satisfiesDependencies="false"] .extension-item-description,
extension[incompatibleUpdate="true"] .extension-item-description {
color: #C77173;
@ -120,10 +121,15 @@ extension[itemType="theme"] .extension-icon {
-moz-border-radius: 3px;
}
extension[availableUpdateURL="none"] .extension-badge {
extension[availableUpdateURL="none"] .extension-badge,
extension .extension-details-link {
display: none;
}
extension[blocklisted="true"][blocklistDetailsURL][availableUpdateURL="none"] .extension-details-link {
display: -moz-box;
}
extension[loading="true"] .extension-badge {
display: -moz-box;
width: 16px;

Просмотреть файл

@ -94,6 +94,7 @@ extension[disabled="true"] {
}
extension[disabled="true"][compatible="false"] .extension-item-description,
extension[blocklisted="true"] .extension-item-description,
extension[satisfiesDependencies="false"] .extension-item-description,
extension[incompatibleUpdate="true"] .extension-item-description {
color: #C77173;
@ -141,10 +142,15 @@ extension[itemType="theme"] .extension-icon {
-moz-margin-end: 10px;
}
extension[availableUpdateURL="none"] .extension-badge {
extension[availableUpdateURL="none"] .extension-badge,
extension .extension-details-link {
display: none;
}
extension[blocklisted="true"][blocklistDetailsURL][availableUpdateURL="none"] .extension-details-link {
display: -moz-box;
}
extension[loading="true"] .extension-badge {
display: -moz-box;
width: 16px;