зеркало из https://github.com/mozilla/pjs.git
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:
Родитель
16bb5b7281
Коммит
5d79d38268
|
@ -67,6 +67,12 @@ pref("extensions.ignoreMTimeChanges", false);
|
||||||
// Enables some extra Extension System Logging (can reduce performance)
|
// Enables some extra Extension System Logging (can reduce performance)
|
||||||
pref("extensions.logging.enabled", false);
|
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
|
// App-specific update preferences
|
||||||
|
|
||||||
// Whether or not app updates are enabled
|
// 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.
|
// which tells users what's new in this new update.
|
||||||
pref("app.update.showInstalledUI", false);
|
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
|
// 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
|
// 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
|
// their extensions are being updated and thus reregistered every time the app is started
|
||||||
|
|
|
@ -50,7 +50,7 @@
|
||||||
<!ENTITY optionsUnix.tooltip "Preferences">
|
<!ENTITY optionsUnix.tooltip "Preferences">
|
||||||
<!ENTITY about.tooltip "About">
|
<!ENTITY about.tooltip "About">
|
||||||
<!ENTITY homepage.tooltip "Home Page">
|
<!ENTITY homepage.tooltip "Home Page">
|
||||||
|
<!ENTITY moreInfo.label "More Information">
|
||||||
<!ENTITY installNow.label "Update Now">
|
<!ENTITY installNow.label "Update Now">
|
||||||
|
|
||||||
<!ENTITY getMoreExtensions.label "Get More Extensions">
|
<!ENTITY getMoreExtensions.label "Get More Extensions">
|
||||||
|
|
|
@ -15,6 +15,7 @@ restartBeforeEnableTitle=Enable Extension
|
||||||
restartBeforeDisableTitle=Disable Extension
|
restartBeforeDisableTitle=Disable Extension
|
||||||
restartBeforeEnableMessage=%S will be enabled when %S is restarted.
|
restartBeforeEnableMessage=%S will be enabled when %S is restarted.
|
||||||
restartBeforeDisableMessage=%S will be disabled 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
|
restartBeforeUninstallTitle=Uninstall
|
||||||
restartBeforeUninstallMessage=%S will be uninstalled when %S is restarted.
|
restartBeforeUninstallMessage=%S will be uninstalled when %S is restarted.
|
||||||
restartBeforeInstallMessage=%S will be installed 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
|
uninstallButton=Uninstall
|
||||||
disableButton=Disable
|
disableButton=Disable
|
||||||
cancelButton=Cancel
|
cancelButton=Cancel
|
||||||
|
restartButton=Restart %S
|
||||||
|
laterButton=Later
|
||||||
|
moreInfoText=More information
|
||||||
uninstallTitle=Uninstall %S
|
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:
|
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.
|
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
|
incompatibleThemeName=this Theme
|
||||||
incompatibleExtension=Disabled - not compatible with %S %S
|
incompatibleExtension=Disabled - not compatible with %S %S
|
||||||
needsDependenciesDisabled=Disabled - requires additional items to operate.
|
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.
|
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.
|
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.
|
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
|
missingFileTitle=Missing File
|
||||||
missingFileMessage=%S could not load this item because the file %S was missing.
|
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.
|
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);
|
gExtensionsView.setAttribute("state", gWindowState);
|
||||||
gExtensionManager = Components.classes["@mozilla.org/extensions/manager;1"]
|
gExtensionManager = Components.classes["@mozilla.org/extensions/manager;1"]
|
||||||
.getService(Components.interfaces.nsIExtensionManager)
|
.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)
|
gApp = Components.classes["@mozilla.org/xre/app-info;1"].getService(Components.interfaces.nsIXULAppInfo)
|
||||||
.QueryInterface(Components.interfaces.nsIXULRuntime);
|
.QueryInterface(Components.interfaces.nsIXULRuntime);
|
||||||
|
|
||||||
|
@ -218,10 +218,12 @@ function Startup()
|
||||||
|
|
||||||
// Set Initial Size
|
// Set Initial Size
|
||||||
var win = document.documentElement;
|
var win = document.documentElement;
|
||||||
if (!win.hasAttribute("width") || !win.hasAttribute("height")) {
|
if (!win.hasAttribute("width"))
|
||||||
win.setAttribute("width", isExtensions ? (extensionsStrings.getString("extensionsManagerWidth")) : (extensionsStrings.getString("themesManagerWidth")));
|
win.setAttribute("width", extensionsStrings.getString(
|
||||||
win.setAttribute("height", isExtensions ? (extensionsStrings.getString("extensionsManagerHeight")) : (extensionsStrings.getString("themesManagerHeight")));
|
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
|
// Now look and see if we're being opened by XPInstall
|
||||||
gDownloadManager = new XPInstallDownloadManager();
|
gDownloadManager = new XPInstallDownloadManager();
|
||||||
|
@ -520,6 +522,7 @@ UpdateCheckListener.prototype = {
|
||||||
[brandName]),
|
[brandName]),
|
||||||
message2: strings.getString("updatesAvailableMessage2"),
|
message2: strings.getString("updatesAvailableMessage2"),
|
||||||
title: strings.getString("updatesAvailableTitle"),
|
title: strings.getString("updatesAvailableTitle"),
|
||||||
|
iconClass: "question-icon",
|
||||||
buttons: {
|
buttons: {
|
||||||
accept: { label: strings.getString("updatesAvailableAccept"),
|
accept: { label: strings.getString("updatesAvailableAccept"),
|
||||||
focused: true },
|
focused: true },
|
||||||
|
@ -536,8 +539,8 @@ UpdateCheckListener.prototype = {
|
||||||
this._addons[i].version]);
|
this._addons[i].version]);
|
||||||
names.push(name);
|
names.push(name);
|
||||||
}
|
}
|
||||||
openDialog("chrome://mozapps/content/extensions/list.xul", "",
|
window.openDialog("chrome://mozapps/content/extensions/list.xul", "",
|
||||||
"titlebar,modal", names, params);
|
"titlebar,centerscreen,modal", names, params);
|
||||||
if (params.result == "accept")
|
if (params.result == "accept")
|
||||||
gExtensionManager.addDownloads(this._addons, this._addons.length, true);
|
gExtensionManager.addDownloads(this._addons, this._addons.length, true);
|
||||||
}
|
}
|
||||||
|
@ -672,6 +675,7 @@ function buildContextMenu(aEvent)
|
||||||
var enableMenu = document.getElementById("menuitem_enable_clone");
|
var enableMenu = document.getElementById("menuitem_enable_clone");
|
||||||
if (gExtensionsView.selectedItem &&
|
if (gExtensionsView.selectedItem &&
|
||||||
(gExtensionsView.selectedItem.getAttribute("compatible") == "false" ||
|
(gExtensionsView.selectedItem.getAttribute("compatible") == "false" ||
|
||||||
|
gExtensionsView.selectedItem.getAttribute("blocklisted") == "true" ||
|
||||||
gExtensionsView.selectedItem.disabled))
|
gExtensionsView.selectedItem.disabled))
|
||||||
// don't let the user activate incompatible themes, but show a (disabled) Enable
|
// 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
|
// menuitem to give visual feedback; it's disabled because cmd_enable returns false
|
||||||
|
@ -901,10 +905,12 @@ var gExtensionsViewController = {
|
||||||
(opType == OP_NONE ||
|
(opType == OP_NONE ||
|
||||||
opType == OP_NEEDS_DISABLE) &&
|
opType == OP_NEEDS_DISABLE) &&
|
||||||
selectedItem.getAttribute("satisfiesDependencies") != "false" &&
|
selectedItem.getAttribute("satisfiesDependencies") != "false" &&
|
||||||
|
selectedItem.getAttribute("blocklisted") != "true" &&
|
||||||
selectedItem.getAttribute("compatible") != "false";
|
selectedItem.getAttribute("compatible") != "false";
|
||||||
case "cmd_disable":
|
case "cmd_disable":
|
||||||
return selectedItem &&
|
return selectedItem &&
|
||||||
selectedItem.getAttribute("satisfiesDependencies") != "false" &&
|
selectedItem.getAttribute("satisfiesDependencies") != "false" &&
|
||||||
|
selectedItem.getAttribute("blocklisted") != "true" &&
|
||||||
(opType == OP_NONE ||
|
(opType == OP_NONE ||
|
||||||
opType == OP_NEEDS_ENABLE);
|
opType == OP_NEEDS_ENABLE);
|
||||||
case "cmd_movetop":
|
case "cmd_movetop":
|
||||||
|
@ -1144,12 +1150,13 @@ var gExtensionsViewController = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
gExtensionManager.disableItem(id);
|
gExtensionManager.disableItem(id);
|
||||||
gExtensionsView.selectedItem = document.getElementById(aSelectedItem.id);
|
gExtensionsViewController.onCommandUpdate();
|
||||||
},
|
},
|
||||||
|
|
||||||
cmd_enable: function (aSelectedItem)
|
cmd_enable: function (aSelectedItem)
|
||||||
{
|
{
|
||||||
gExtensionManager.enableItem(getIDFromResourceURI(aSelectedItem.id));
|
gExtensionManager.enableItem(getIDFromResourceURI(aSelectedItem.id));
|
||||||
|
gExtensionsViewController.onCommandUpdate();
|
||||||
#ifdef MOZ_PHOENIX
|
#ifdef MOZ_PHOENIX
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,7 +89,9 @@
|
||||||
</xul:hbox>
|
</xul:hbox>
|
||||||
<xul:hbox>
|
<xul:hbox>
|
||||||
<xul:image class=""/>
|
<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:hbox>
|
||||||
</xul:vbox>
|
</xul:vbox>
|
||||||
<xul:vbox class="extension-install-button-box" pack="end">
|
<xul:vbox class="extension-install-button-box" pack="end">
|
||||||
|
|
|
@ -184,6 +184,12 @@
|
||||||
<binding subject="?extension"
|
<binding subject="?extension"
|
||||||
predicate="http://www.mozilla.org/2004/em-rdf#compatible"
|
predicate="http://www.mozilla.org/2004/em-rdf#compatible"
|
||||||
object="?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"
|
<binding subject="?extension"
|
||||||
predicate="http://www.mozilla.org/2004/em-rdf#homepageURL"
|
predicate="http://www.mozilla.org/2004/em-rdf#homepageURL"
|
||||||
object="?homepage-url"/>
|
object="?homepage-url"/>
|
||||||
|
@ -246,6 +252,8 @@
|
||||||
state="?state" progress="?progress" status="?status"
|
state="?state" progress="?progress" status="?status"
|
||||||
incompatibleUpdate="?incompatibleUpdate"
|
incompatibleUpdate="?incompatibleUpdate"
|
||||||
satisfiesDependencies="?satisfiesDependencies"
|
satisfiesDependencies="?satisfiesDependencies"
|
||||||
|
blocklisted="?blocklisted"
|
||||||
|
blocklistDetailsURL="?blocklistDetailsURL"
|
||||||
updateable="?updateable"/>
|
updateable="?updateable"/>
|
||||||
</action>
|
</action>
|
||||||
</rule>
|
</rule>
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#
|
#
|
||||||
# Contributor(s):
|
# Contributor(s):
|
||||||
# Ben Goodger <ben@mozilla.org>
|
# Ben Goodger <ben@mozilla.org>
|
||||||
|
# Robert Strong <robert.bugzilla@gmail.com>
|
||||||
#
|
#
|
||||||
# Alternatively, the contents of this file may be used under the terms of
|
# 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
|
# 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";
|
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.
|
||||||
*
|
*
|
||||||
* This dialog must be opened modally, the caller can inspect the user action
|
* When using this dialog with window.arguments it must be opened modally, the
|
||||||
* after the dialog closes by inspecting the value of the |result| parameter
|
* caller can inspect the user action after the dialog closes by inspecting the
|
||||||
* on this object which is set to the dlgtype of the button used to close
|
* value of the |result| parameter on this object which is set to the dlgtype
|
||||||
* the dialog.
|
* 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:
|
* window.arguments[1] a JS Object with the following properties:
|
||||||
*
|
*
|
||||||
* title: A title string, to be displayed in the title bar of the dialog.
|
* title: A title string, to be displayed in the title bar of the dialog.
|
||||||
* message1: A message string, displayed above the addon list
|
* message1: A message string, displayed above the addon list
|
||||||
* message2: A message string, displayed below the addon list
|
* message2: A message string, displayed below the addon list
|
||||||
* message3: A bolded 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: {
|
* buttons: {
|
||||||
* accept: { label: "A Label for the Accept button",
|
* accept: { label: "A Label for the Accept button",
|
||||||
* focused: true },
|
* focused: true },
|
||||||
|
@ -64,14 +74,76 @@ const kDialog = "dialog";
|
||||||
*
|
*
|
||||||
* result: The dlgtype of button that was used to dismiss the dialog.
|
* result: The dlgtype of button that was used to dismiss the dialog.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
var gButtons = { };
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
// Fill the addons list
|
var de = document.documentElement;
|
||||||
var items = window.arguments[0];
|
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");
|
var addons = document.getElementById("addonsChildren");
|
||||||
if (items.length > 0)
|
if (items.length > 0)
|
||||||
document.getElementById("addonsTree").hidden = false;
|
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 treeitem = document.createElementNS(kXULNS, "treeitem");
|
||||||
var treerow = document.createElementNS(kXULNS, "treerow");
|
var treerow = document.createElementNS(kXULNS, "treerow");
|
||||||
var treecell = document.createElementNS(kXULNS, "treecell");
|
var treecell = document.createElementNS(kXULNS, "treecell");
|
||||||
|
@ -81,12 +153,9 @@ function init() {
|
||||||
addons.appendChild(treeitem);
|
addons.appendChild(treeitem);
|
||||||
}
|
}
|
||||||
|
|
||||||
var de = document.documentElement;
|
|
||||||
var params = window.arguments[1];
|
|
||||||
|
|
||||||
// Set the messages
|
// Set the messages
|
||||||
var messages = ["message1", "message2", "message3"];
|
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) {
|
if (messages[i] in params) {
|
||||||
var message = document.getElementById(messages[i]);
|
var message = document.getElementById(messages[i]);
|
||||||
message.hidden = false;
|
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
|
// Set the window title
|
||||||
if ("title" in params)
|
if ("title" in params)
|
||||||
document.title = params.title;
|
document.title = params.title;
|
||||||
|
|
||||||
// Set up the buttons
|
// Set up the buttons
|
||||||
if ("buttons" in params) {
|
if ("buttons" in params) {
|
||||||
var buttons = params.buttons;
|
gButtons = params.buttons;
|
||||||
var buttonString = "";
|
var buttonString = "";
|
||||||
for (var buttonType in buttons)
|
for (var buttonType in gButtons)
|
||||||
buttonString += "," + buttonType;
|
buttonString += "," + buttonType;
|
||||||
dump("*** de.toSource = " + de.localName + "\n");
|
|
||||||
de.buttons = buttonString.substr(1);
|
de.buttons = buttonString.substr(1);
|
||||||
for (buttonType in buttons) {
|
for (buttonType in gButtons) {
|
||||||
var button = de.getButton(buttonType);
|
button = de.getButton(buttonType);
|
||||||
button.label = buttons[buttonType].label;
|
button.label = gButtons[buttonType].label;
|
||||||
if (buttons[buttonType].focused)
|
if (gButtons[buttonType].focused)
|
||||||
button.focus();
|
button.focus();
|
||||||
document.addEventListener(kDialog + buttonType, handleButtonCommand, true);
|
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
|
* 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
|
* and report the result back to the caller through the |result| property on
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#
|
#
|
||||||
# Contributor(s):
|
# Contributor(s):
|
||||||
# Ben Goodger <ben@mozilla.org>
|
# Ben Goodger <ben@mozilla.org>
|
||||||
|
# Robert Strong <robert.bugzilla@gmail.com>
|
||||||
#
|
#
|
||||||
# Alternatively, the contents of this file may be used under the terms of
|
# 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
|
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
@ -41,7 +42,7 @@
|
||||||
|
|
||||||
<dialog id="addonList" windowtype="Addons:List"
|
<dialog id="addonList" windowtype="Addons:List"
|
||||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||||
style="width: 35em;"
|
onunload="shutdown();"
|
||||||
buttons="accept,cancel" onload="init();">
|
buttons="accept,cancel" onload="init();">
|
||||||
|
|
||||||
<script type="application/x-javascript"
|
<script type="application/x-javascript"
|
||||||
|
@ -49,18 +50,33 @@
|
||||||
|
|
||||||
<stringbundle id="extensionsBundle"
|
<stringbundle id="extensionsBundle"
|
||||||
src="chrome://mozapps/locale/extensions/extensions.properties"/>
|
src="chrome://mozapps/locale/extensions/extensions.properties"/>
|
||||||
|
<stringbundle id="brandBundle"
|
||||||
|
src="chrome://branding/locale/brand.properties"/>
|
||||||
|
|
||||||
<label id="message1" hidden="true"/>
|
<hbox align="start">
|
||||||
|
<vbox>
|
||||||
|
<image id="infoIcon"/>
|
||||||
|
</vbox>
|
||||||
|
<vbox class="spaced" style="width: 30em;">
|
||||||
|
<label id="message1" class="spaced" hidden="true"/>
|
||||||
<separator class="thin"/>
|
<separator class="thin"/>
|
||||||
<tree id="addonsTree" rows="10" hidecolumnpicker="true" hidden="true">
|
<tree id="addonsTree" rows="8" hidecolumnpicker="true" hidden="true" class="spaced">
|
||||||
<treecols>
|
<treecols>
|
||||||
<treecol flex="1" id="nameColumn" hideheader="true"/>
|
<treecol flex="1" id="nameColumn" hideheader="true"/>
|
||||||
</treecols>
|
</treecols>
|
||||||
<treechildren id="addonsChildren"/>
|
<treechildren id="addonsChildren"/>
|
||||||
</tree>
|
</tree>
|
||||||
<separator class="thin"/>
|
<label id="message2" class="spaced" hidden="true"/>
|
||||||
<label id="message2" hidden="true"/>
|
<label class="bold spaced" id="message3" hidden="true"/>
|
||||||
<separator class="thin"/>
|
<hbox id="moreInfoBox" hidden="true">
|
||||||
<label class="bold" id="message3" 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>
|
</dialog>
|
||||||
|
|
||||||
|
|
|
@ -372,8 +372,8 @@ interface nsIExtensionManager : nsISupports
|
||||||
* interfaces for Gecko 1.8.1. After the 1.8.1 release this interface should
|
* interfaces for Gecko 1.8.1. After the 1.8.1 release this interface should
|
||||||
* not be used.
|
* not be used.
|
||||||
*/
|
*/
|
||||||
[scriptable, uuid(8058922f-9367-45d1-878e-7c5ca27eddf4)]
|
[scriptable, uuid(126d544a-5f34-4277-a3bc-2785ee85a046)]
|
||||||
interface nsIExtensionManager2 : nsIExtensionManager
|
interface nsIExtensionManager_MOZILLA_1_8_BRANCH : nsIExtensionManager
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Cancels a pending uninstall of an item
|
* Cancels a pending uninstall of an item
|
||||||
|
@ -398,6 +398,17 @@ interface nsIExtensionManager2 : nsIExtensionManager
|
||||||
in boolean includeDisabled,
|
in boolean includeDisabled,
|
||||||
out unsigned long itemCount,
|
out unsigned long itemCount,
|
||||||
[retval, array, size_is(itemCount)] out nsIUpdateItem items);
|
[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_LOGGING_ENABLED = "extensions.logging.enabled";
|
||||||
const PREF_EM_UPDATE_INTERVAL = "extensions.update.interval";
|
const PREF_EM_UPDATE_INTERVAL = "extensions.update.interval";
|
||||||
const PREF_XPINSTALL_STATUS_DLG_SKIN = "xpinstall.dialog.progress.skin";
|
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_EXTENSIONS = "extensions";
|
||||||
const DIR_CHROME = "chrome";
|
const DIR_CHROME = "chrome";
|
||||||
|
@ -83,12 +87,14 @@ const FILE_AUTOREG = ".autoreg";
|
||||||
const FILE_INSTALL_MANIFEST = "install.rdf";
|
const FILE_INSTALL_MANIFEST = "install.rdf";
|
||||||
const FILE_CONTENTS_MANIFEST = "contents.rdf";
|
const FILE_CONTENTS_MANIFEST = "contents.rdf";
|
||||||
const FILE_CHROME_MANIFEST = "chrome.manifest";
|
const FILE_CHROME_MANIFEST = "chrome.manifest";
|
||||||
|
const FILE_BLOCKLIST = "blocklist.xml";
|
||||||
|
|
||||||
const UNKNOWN_XPCOM_ABI = "unknownABI";
|
const UNKNOWN_XPCOM_ABI = "unknownABI";
|
||||||
|
|
||||||
const FILE_LOGFILE = "extensionmanager.log";
|
const FILE_LOGFILE = "extensionmanager.log";
|
||||||
|
|
||||||
const FILE_DEFAULT_THEME_JAR = "classic.jar";
|
const FILE_DEFAULT_THEME_JAR = "classic.jar";
|
||||||
|
const TOOLKIT_ID = "toolkit@mozilla.org"
|
||||||
|
|
||||||
const KEY_PROFILEDIR = "ProfD";
|
const KEY_PROFILEDIR = "ProfD";
|
||||||
const KEY_PROFILEDS = "ProfDS";
|
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_ITEM_ROOT = "urn:mozilla:item:root"
|
||||||
const RDFURI_DEFAULT_THEME = "urn:mozilla:item:{972ce4c6-7e08-4474-a285-3208198ce6fd}";
|
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_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_XPINSTALL = "chrome://mozapps/skin/xpinstall/xpinstallItemGeneric.png";
|
||||||
const URI_GENERIC_ICON_THEME = "chrome://mozapps/skin/extensions/themeGeneric.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_BRAND_PROPERTIES = "chrome://branding/locale/brand.properties";
|
||||||
const URI_DOWNLOADS_PROPERTIES = "chrome://mozapps/locale/downloads/downloads.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_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_SUCCESS = 0;
|
||||||
const INSTALLERROR_INVALID_VERSION = -1;
|
const INSTALLERROR_INVALID_VERSION = -1;
|
||||||
|
@ -140,7 +148,9 @@ const INSTALLERROR_INVALID_GUID = -2;
|
||||||
const INSTALLERROR_INCOMPATIBLE_VERSION = -3;
|
const INSTALLERROR_INCOMPATIBLE_VERSION = -3;
|
||||||
const INSTALLERROR_PHONED_HOME = -4;
|
const INSTALLERROR_PHONED_HOME = -4;
|
||||||
const INSTALLERROR_INCOMPATIBLE_PLATFORM = -5;
|
const INSTALLERROR_INCOMPATIBLE_PLATFORM = -5;
|
||||||
|
const INSTALLERROR_BLOCKLISTED = -6;
|
||||||
|
|
||||||
|
const MODE_RDONLY = 0x01;
|
||||||
const MODE_WRONLY = 0x02;
|
const MODE_WRONLY = 0x02;
|
||||||
const MODE_CREATE = 0x08;
|
const MODE_CREATE = 0x08;
|
||||||
const MODE_APPEND = 0x10;
|
const MODE_APPEND = 0x10;
|
||||||
|
@ -744,6 +754,37 @@ function showMessage(titleKey, titleParams, messageKey, messageParams) {
|
||||||
ps.alert(null, title, message);
|
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.
|
* Gets a zip reader for the file specified.
|
||||||
* @param zipFile
|
* @param zipFile
|
||||||
|
@ -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
|
* Installs, manages and tracks compatibility for Extensions and Themes
|
||||||
* @constructor
|
* @constructor
|
||||||
|
@ -2493,6 +2834,9 @@ ExtensionManager.prototype = {
|
||||||
.getService(Components.interfaces.nsIUpdateTimerManager);
|
.getService(Components.interfaces.nsIUpdateTimerManager);
|
||||||
var interval = getPref("getIntPref", PREF_EM_UPDATE_INTERVAL, 86400);
|
var interval = getPref("getIntPref", PREF_EM_UPDATE_INTERVAL, 86400);
|
||||||
tm.registerTimer("addon-background-update-timer", this, interval);
|
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);
|
callback(installManifest, installData.id, location, installData.type);
|
||||||
em._appDisableItem(id);
|
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 {
|
else {
|
||||||
/**
|
/**
|
||||||
* Turns an error code into a message for logging
|
* Turns an error code into a message for logging
|
||||||
|
@ -3190,21 +3539,20 @@ ExtensionManager.prototype = {
|
||||||
while (elements.hasMoreElements()) {
|
while (elements.hasMoreElements()) {
|
||||||
var itemResource = elements.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
|
var itemResource = elements.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
|
||||||
|
|
||||||
// The value of appDisabled is determined by an item being
|
// appDisabled is determined by an item being compatible,
|
||||||
// compatible and satisfying its dependencies.
|
// satisfying its dependencies, and not being blocklisted
|
||||||
id = stripPrefix(itemResource.Value, PREFIX_ITEM_URI);
|
id = stripPrefix(itemResource.Value, PREFIX_ITEM_URI);
|
||||||
if (ds.getItemProperty(id, "compatible") == "true" &&
|
if (ds.getItemProperty(id, "compatible") == "true" &&
|
||||||
|
ds.getItemProperty(id, "blocklisted") == "false" &&
|
||||||
ds.getItemProperty(id, "satisfiesDependencies") == "true")
|
ds.getItemProperty(id, "satisfiesDependencies") == "true")
|
||||||
ds.setItemProperty(id, EM_R("appDisabled"), null);
|
ds.setItemProperty(id, EM_R("appDisabled"), null);
|
||||||
else
|
else
|
||||||
ds.setItemProperty(id, EM_R("appDisabled"), EM_L("true"));
|
ds.setItemProperty(id, EM_R("appDisabled"), EM_L("true"));
|
||||||
|
|
||||||
// The value of userDisabled is set based on the value being
|
// userDisabled is set based on its value being OP_NEEDS_ENABLE or
|
||||||
// OP_NEEDS_ENABLE or OP_NEEDS_DISABLE. This allows us to have an
|
// OP_NEEDS_DISABLE. This allows us to have an item to be enabled
|
||||||
// item to be enabled by the app and disabled by the user during
|
// by the app and disabled by the user during a single restart.
|
||||||
// one restart.
|
var value = stringData(ds.GetTarget(itemResource, EM_R("userDisabled"), true));
|
||||||
var target = ds.GetTarget(itemResource, EM_R("userDisabled"), true);
|
|
||||||
var value = stringData(target);
|
|
||||||
if (value == OP_NEEDS_ENABLE)
|
if (value == OP_NEEDS_ENABLE)
|
||||||
ds.setItemProperty(id, EM_R("userDisabled"), null);
|
ds.setItemProperty(id, EM_R("userDisabled"), null);
|
||||||
else if (value == OP_NEEDS_DISABLE)
|
else if (value == OP_NEEDS_DISABLE)
|
||||||
|
@ -3290,13 +3638,21 @@ ExtensionManager.prototype = {
|
||||||
}
|
}
|
||||||
ds.endUpdateBatch();
|
ds.endUpdateBatch();
|
||||||
|
|
||||||
// Disable all incompatible items and let update enable them if appropriate.
|
var ctr = getContainer(ds, ds._itemRoot);
|
||||||
var currAppID = gApp.ID;
|
var elements = ctr.GetElements();
|
||||||
var items = ds.getIncompatibleItemList(currAppID, currAppVersion,
|
while (elements.hasMoreElements()) {
|
||||||
nsIUpdateItem.TYPE_ADDON, true);
|
var itemResource = elements.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
|
||||||
for (var i = 0; i < items.length; ++i)
|
// appDisabled is determined by an item being compatible,
|
||||||
ds.setItemProperty(items[i].id, EM_R("appDisabled"), EM_L("true"));
|
// satisfying its dependencies, and not being blocklisted
|
||||||
// Update the manifests to reflect the items that were disabled.
|
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);
|
this._updateManifests(true);
|
||||||
|
|
||||||
// Always check for compatibility updates when upgrading
|
// Always check for compatibility updates when upgrading
|
||||||
|
@ -3563,7 +3919,7 @@ ExtensionManager.prototype = {
|
||||||
|
|
||||||
var extensionSectionHeader = "[ExtensionDirs]\r\n";
|
var extensionSectionHeader = "[ExtensionDirs]\r\n";
|
||||||
fos.write(extensionSectionHeader, extensionSectionHeader.length);
|
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 e = validExtensions[i];
|
||||||
var itemLocation = e.location.getItemLocation(e.id).QueryInterface(nsILocalFile);
|
var itemLocation = e.location.getItemLocation(e.id).QueryInterface(nsILocalFile);
|
||||||
var descriptor = getAbsoluteDescriptor(itemLocation);
|
var descriptor = getAbsoluteDescriptor(itemLocation);
|
||||||
|
@ -3639,6 +3995,8 @@ ExtensionManager.prototype = {
|
||||||
* INSTALLERROR_INCOMPATIBLE_PLATFORM
|
* INSTALLERROR_INCOMPATIBLE_PLATFORM
|
||||||
* error, item is not compatible with the operating
|
* error, item is not compatible with the operating
|
||||||
* system or ABI the application was built for.
|
* system or ABI the application was built for.
|
||||||
|
* INSTALLERROR_BLOCKLISTED
|
||||||
|
* error, item is blocklisted
|
||||||
*/
|
*/
|
||||||
_getInstallData: function(installManifest) {
|
_getInstallData: function(installManifest) {
|
||||||
var installData = { id : "",
|
var installData = { id : "",
|
||||||
|
@ -3736,6 +4094,11 @@ ExtensionManager.prototype = {
|
||||||
if (!this.datasource.isCompatible(installManifest, gInstallManifestRoot, undefined))
|
if (!this.datasource.isCompatible(installManifest, gInstallManifestRoot, undefined))
|
||||||
installData.error = INSTALLERROR_INCOMPATIBLE_VERSION;
|
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;
|
return installData;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -4199,6 +4562,11 @@ ExtensionManager.prototype = {
|
||||||
"incompatiblePlatformMessage",
|
"incompatiblePlatformMessage",
|
||||||
[installData.name, BundleManager.appName, osABI]);
|
[installData.name, BundleManager.appName, osABI]);
|
||||||
break;
|
break;
|
||||||
|
case INSTALLERROR_BLOCKLISTED:
|
||||||
|
LOG("Blocklisted Item: Item: \"" + installData.id + "\" version " +
|
||||||
|
installData.version + " was not installed.");
|
||||||
|
showBlocklistMessage([installData], true);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
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 appDisabled and userDisabled RDF property arcs are used to store
|
||||||
* the pending operation for app disabling and user disabling for an item as
|
* 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
|
* 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
|
* 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
|
* operations are then evaluated in order to set the operation to perform
|
||||||
* and notify the observers if the operation has been changed.
|
* 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
|
* @param id
|
||||||
* The ID of the item to be enabled by the application.
|
* 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
|
* 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
|
* operations are then evaluated in order to set the operation to perform
|
||||||
* and notify the observers if the operation has been changed.
|
* 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
|
* @param id
|
||||||
* The ID of the item to be disabled by the application.
|
* The ID of the item to be disabled by the application.
|
||||||
*/
|
*/
|
||||||
|
@ -4660,7 +5028,8 @@ ExtensionManager.prototype = {
|
||||||
else if (userDisabled == OP_NEEDS_ENABLE)
|
else if (userDisabled == OP_NEEDS_ENABLE)
|
||||||
ds.setItemProperty(id, EM_R("userDisabled"), EM_L("true"));
|
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"));
|
ds.setItemProperty(id, EM_R("appDisabled"), EM_L("true"));
|
||||||
else if (appDisabled == OP_NONE)
|
else if (appDisabled == OP_NONE)
|
||||||
ds.setItemProperty(id, EM_R("appDisabled"), EM_L(OP_NEEDS_DISABLE));
|
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
|
* 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.
|
* 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
|
* @param id
|
||||||
* The ID of the item to be enabled by the user.
|
* 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
|
* 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.
|
* 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
|
* @param id
|
||||||
* The ID of the item to be disabled by the user.
|
* The ID of the item to be disabled by the user.
|
||||||
*/
|
*/
|
||||||
|
@ -4802,6 +5171,7 @@ ExtensionManager.prototype = {
|
||||||
var dependentID = dependentItems[i].id;
|
var dependentID = dependentItems[i].id;
|
||||||
ds.updateProperty(dependentID, "satisfiesDependencies");
|
ds.updateProperty(dependentID, "satisfiesDependencies");
|
||||||
if (ds.getItemProperty(dependentID, "satisfiesDependencies") == "true" &&
|
if (ds.getItemProperty(dependentID, "satisfiesDependencies") == "true" &&
|
||||||
|
ds.getItemProperty(dependentID, "blocklisted") == "false" &&
|
||||||
ds.getItemProperty(dependentID, "compatible") == "true")
|
ds.getItemProperty(dependentID, "compatible") == "true")
|
||||||
this._appEnableItem(dependentID);
|
this._appEnableItem(dependentID);
|
||||||
else
|
else
|
||||||
|
@ -4833,6 +5203,42 @@ ExtensionManager.prototype = {
|
||||||
updater.checkForUpdates(items, items.length, versionUpdateOnly, listener);
|
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.
|
* @returns An enumeration of all registered Install Locations.
|
||||||
*/
|
*/
|
||||||
|
@ -5233,7 +5639,7 @@ ExtensionManager.prototype = {
|
||||||
*/
|
*/
|
||||||
QueryInterface: function(iid) {
|
QueryInterface: function(iid) {
|
||||||
if (!iid.equals(Components.interfaces.nsIExtensionManager) &&
|
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.nsITimerCallback) &&
|
||||||
!iid.equals(Components.interfaces.nsIObserver) &&
|
!iid.equals(Components.interfaces.nsIObserver) &&
|
||||||
!iid.equals(Components.interfaces.nsISupports))
|
!iid.equals(Components.interfaces.nsISupports))
|
||||||
|
@ -5494,6 +5900,10 @@ ExtensionItemUpdater.prototype = {
|
||||||
gVersionChecker.compare(appExtensionsVersion, aRemoteItem.maxAppVersion) > 0)
|
gVersionChecker.compare(appExtensionsVersion, aRemoteItem.maxAppVersion) > 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (this._emDS.isBlocklisted(aRemoteItem.id, aRemoteItem.version,
|
||||||
|
undefined, undefined))
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -5631,24 +6041,14 @@ RDFItemUpdater.prototype = {
|
||||||
onXMLLoad: function(aEvent, aItem) {
|
onXMLLoad: function(aEvent, aItem) {
|
||||||
var request = aEvent.target;
|
var request = aEvent.target;
|
||||||
var responseXML = request.responseXML;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5661,9 +6061,6 @@ RDFItemUpdater.prototype = {
|
||||||
this.onDatasourceLoaded(ds, aItem);
|
this.onDatasourceLoaded(ds, aItem);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
onXMLError: function(aEvent, aItem) {
|
onXMLError: function(aEvent, aItem) {
|
||||||
try {
|
try {
|
||||||
var request = aEvent.target;
|
var request = aEvent.target;
|
||||||
|
@ -6038,6 +6435,63 @@ ExtensionsDataSource.prototype = {
|
||||||
return false;
|
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.
|
* Gets a list of items that are incompatible with a specific application version.
|
||||||
* @param appID
|
* @param appID
|
||||||
|
@ -6084,6 +6538,44 @@ ExtensionsDataSource.prototype = {
|
||||||
return items;
|
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
|
* Gets a list of items of a specific type
|
||||||
* @param desiredType
|
* @param desiredType
|
||||||
|
@ -6879,6 +7371,7 @@ ExtensionsDataSource.prototype = {
|
||||||
* Load the Extensions Datasource from disk.
|
* Load the Extensions Datasource from disk.
|
||||||
*/
|
*/
|
||||||
loadExtensions: function() {
|
loadExtensions: function() {
|
||||||
|
Blocklist._ensureBlocklist();
|
||||||
var extensionsFile = getFile(KEY_PROFILEDIR, [FILE_EXTENSIONS]);
|
var extensionsFile = getFile(KEY_PROFILEDIR, [FILE_EXTENSIONS]);
|
||||||
try {
|
try {
|
||||||
this._inner = gRDF.GetDataSourceBlocking(getURLSpecFromFile(extensionsFile));
|
this._inner = gRDF.GetDataSourceBlocking(getURLSpecFromFile(extensionsFile));
|
||||||
|
@ -7042,6 +7535,17 @@ ExtensionsDataSource.prototype = {
|
||||||
return null;
|
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)
|
* Get the em:compatible property (whether or not this item is compatible)
|
||||||
*/
|
*/
|
||||||
|
@ -7062,6 +7566,48 @@ ExtensionsDataSource.prototype = {
|
||||||
return EM_L("true");
|
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
|
* Gets the em:availableUpdateURL - the URL to an XPI update package, if
|
||||||
* present, or a literal string "novalue" if there is no update XPI URL.
|
* present, or a literal string "novalue" if there is no update XPI URL.
|
||||||
|
@ -7131,6 +7677,8 @@ ExtensionsDataSource.prototype = {
|
||||||
}
|
}
|
||||||
switch (opType) {
|
switch (opType) {
|
||||||
case OP_NEEDS_DISABLE:
|
case OP_NEEDS_DISABLE:
|
||||||
|
if (this.getItemProperty(id, "blocklisted") == "true")
|
||||||
|
return getLiteral("restartBeforeDisableBlocklisted", [itemName, BundleManager.appName]);
|
||||||
return getLiteral("restartBeforeDisableMessage", [itemName, BundleManager.appName]);
|
return getLiteral("restartBeforeDisableMessage", [itemName, BundleManager.appName]);
|
||||||
case OP_NEEDS_ENABLE:
|
case OP_NEEDS_ENABLE:
|
||||||
return getLiteral("restartBeforeEnableMessage", [itemName, BundleManager.appName]);
|
return getLiteral("restartBeforeEnableMessage", [itemName, BundleManager.appName]);
|
||||||
|
@ -7142,6 +7690,10 @@ ExtensionsDataSource.prototype = {
|
||||||
return getLiteral("restartBeforeUpgradeMessage", [itemName, BundleManager.appName]);
|
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" &&
|
if (this.getItemProperty(id, "appDisabled") == "true" &&
|
||||||
this.getItemProperty(id, "compatible") != "true")
|
this.getItemProperty(id, "compatible") != "true")
|
||||||
return getLiteral("incompatibleExtension", [BundleManager.appName, gApp.version]);
|
return getLiteral("incompatibleExtension", [BundleManager.appName, gApp.version]);
|
||||||
|
|
|
@ -65,6 +65,7 @@ extension[disabled="true"] {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension[disabled="true"][compatible="false"] .extension-item-description,
|
extension[disabled="true"][compatible="false"] .extension-item-description,
|
||||||
|
extension[blocklisted="true"] .extension-item-description,
|
||||||
extension[satisfiesDependencies="false"] .extension-item-description,
|
extension[satisfiesDependencies="false"] .extension-item-description,
|
||||||
extension[incompatibleUpdate="true"] .extension-item-description {
|
extension[incompatibleUpdate="true"] .extension-item-description {
|
||||||
color: #C77173;
|
color: #C77173;
|
||||||
|
@ -120,10 +121,15 @@ extension[itemType="theme"] .extension-icon {
|
||||||
-moz-border-radius: 3px;
|
-moz-border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
extension[availableUpdateURL="none"] .extension-badge {
|
extension[availableUpdateURL="none"] .extension-badge,
|
||||||
|
extension .extension-details-link {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension[blocklisted="true"][blocklistDetailsURL][availableUpdateURL="none"] .extension-details-link {
|
||||||
|
display: -moz-box;
|
||||||
|
}
|
||||||
|
|
||||||
extension[loading="true"] .extension-badge {
|
extension[loading="true"] .extension-badge {
|
||||||
display: -moz-box;
|
display: -moz-box;
|
||||||
width: 16px;
|
width: 16px;
|
||||||
|
|
|
@ -94,6 +94,7 @@ extension[disabled="true"] {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension[disabled="true"][compatible="false"] .extension-item-description,
|
extension[disabled="true"][compatible="false"] .extension-item-description,
|
||||||
|
extension[blocklisted="true"] .extension-item-description,
|
||||||
extension[satisfiesDependencies="false"] .extension-item-description,
|
extension[satisfiesDependencies="false"] .extension-item-description,
|
||||||
extension[incompatibleUpdate="true"] .extension-item-description {
|
extension[incompatibleUpdate="true"] .extension-item-description {
|
||||||
color: #C77173;
|
color: #C77173;
|
||||||
|
@ -141,10 +142,15 @@ extension[itemType="theme"] .extension-icon {
|
||||||
-moz-margin-end: 10px;
|
-moz-margin-end: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
extension[availableUpdateURL="none"] .extension-badge {
|
extension[availableUpdateURL="none"] .extension-badge,
|
||||||
|
extension .extension-details-link {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension[blocklisted="true"][blocklistDetailsURL][availableUpdateURL="none"] .extension-details-link {
|
||||||
|
display: -moz-box;
|
||||||
|
}
|
||||||
|
|
||||||
extension[loading="true"] .extension-badge {
|
extension[loading="true"] .extension-badge {
|
||||||
display: -moz-box;
|
display: -moz-box;
|
||||||
width: 16px;
|
width: 16px;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче