Bug 600874 - Show restart notification for add-on updates [r=mfinkle]

This commit is contained in:
Wesley Johnston 2010-10-18 15:02:00 -04:00
parent 2ade5ac2c5
commit 16806f811e
9 changed files with 501 additions and 6 deletions

View File

@ -764,7 +764,11 @@ AddonInstallListener.prototype = {
_updating: false,
onInstallEnded: function(aInstall, aAddon) {
// XXX fix updating stuff
let element = ExtensionsView.getElementForAddon(aAddon.id);
if (!element)
return;
this._updating = element.hasAttribute("updating");
if (aAddon.pendingOperations & AddonManager.PENDING_INSTALL)
ExtensionsView.showRestart(element.hasAttribute("updating") ? "update" : "normal");
@ -773,15 +777,11 @@ AddonInstallListener.prototype = {
if (!ExtensionsView.visible)
return;
let element = ExtensionsView.getElementForAddon(aInstall.sourceURI.spec);
if (!element)
return;
element.setAttribute("opType", "needs-restart");
element.setAttribute("status", "success");
// If we are updating an add-on, change the status
if (element.hasAttribute("updating")) {
if (this._updating) {
let strings = Elements.browserBundle;
element.setAttribute("updateStatus", strings.getFormattedString("addonUpdate.updated", [aAddon.version]));
element.removeAttribute("updating");

View File

@ -40,6 +40,8 @@ topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
relativesrcdir = mobile/chrome
TESTXPI = $(CURDIR)/$(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)/addons
ADDONSRC = $(srcdir)/addons
include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/rules.mk
@ -58,6 +60,7 @@ _BROWSER_FILES = \
browser_click_content.js \
browser_contacts.js \
browser_find.js \
browser_addons.js \
browser_forms.html \
browser_forms.js \
browser_mainui.js \
@ -77,7 +80,21 @@ _BROWSER_FILES = \
browser_viewport.sjs \
browser_no_title.html \
browser_english_title.html \
browser_install.xml \
browser_upgrade.rdf\
$(NULL)
libs:: $(_BROWSER_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
libs::
rm -rf $(TESTXPI)
$(NSINSTALL) -D $(TESTXPI)
if [ -d $(ADDONSRC) ]; then \
$(EXIT_ON_ERROR) \
for dir in $(ADDONSRC)/*; do \
base=`basename $$dir` ; \
(cd $$dir && zip $(TESTXPI)/$$base.xpi *) \
done \
fi

View File

@ -0,0 +1,9 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
function install(data, reason) {}
function startup(data, reason) {}
function shutdown(data, reason) {}
function uninstall(data, reason) {}

View File

@ -0,0 +1,24 @@
<?xml version="1.0"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>addon1@tests.mozilla.org</em:id>
<em:version>1.0</em:version>
<em:updateURL>http://example.com/browser/mobile/chrome/browser_upgrade.rdf</em:updateURL>
<em:bootstrap>true</em:bootstrap>
<em:targetApplication>
<Description>
<em:id>toolkit@mozilla.org</em:id>
<em:minVersion>0</em:minVersion>
<em:maxVersion>*</em:maxVersion>
</Description>
</em:targetApplication>
<!-- Front End MetaData -->
<em:name>Install Tests</em:name>
</Description>
</RDF>

View File

@ -0,0 +1,22 @@
<?xml version="1.0"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>addon2@tests.mozilla.org</em:id>
<em:version>2.0</em:version>
<em:targetApplication>
<Description>
<em:id>toolkit@mozilla.org</em:id>
<em:minVersion>0</em:minVersion>
<em:maxVersion>*</em:maxVersion>
</Description>
</em:targetApplication>
<!-- Front End MetaData -->
<em:name>Install Tests</em:name>
</Description>
</RDF>

View File

@ -0,0 +1,23 @@
<?xml version="1.0"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>addon1@tests.mozilla.org</em:id>
<em:version>3.0</em:version>
<em:updateURL>http://example.com/browser/mobile/chrome/browser_upgrade.rdf</em:updateURL>
<em:targetApplication>
<Description>
<em:id>toolkit@mozilla.org</em:id>
<em:minVersion>0</em:minVersion>
<em:maxVersion>*</em:maxVersion>
</Description>
</em:targetApplication>
<!-- Front End MetaData -->
<em:name>Install Tests</em:name>
</Description>
</RDF>

View File

@ -0,0 +1,315 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
Components.utils.import("resource://gre/modules/AddonManager.jsm");
Components.utils.import("resource://gre/modules/AddonUpdateChecker.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/NetUtil.jsm");
const RELATIVE_DIR = "browser/mobile/chrome/";
const TESTROOT = "http://example.com/" + RELATIVE_DIR;
const TESTROOT2 = "http://example.org/" + RELATIVE_DIR;
const PREF_LOGGING_ENABLED = "extensions.logging.enabled";
const PREF_SEARCH_MAXRESULTS = "extensions.getAddons.maxResults";
const CHROME_NAME = "mochikit";
const PREF_AUTOUPDATE_DEFAULT = "extensions.update.autoUpdateDefault"
const PREF_GETADDONS_BROWSESEARCHRESULTS = "extensions.getAddons.search.browseURL";
const PREF_GETADDONS_GETSEARCHRESULTS = "extensions.getAddons.search.url";
const PREF_GETADDONS_GETRECOMMENDED = "extensions.getAddons.recommended.url";
const PREF_GETADDONS_BROWSERECOMMENDED = "extensions.getAddons.recommended.browseURL";
const SEARCH_URL = TESTROOT + "browser_details.xml";
var addons = [{
id: "addon1@tests.mozilla.org",
name : "Install Tests",
iconURL: "http://example.com/icon.png",
homepageURL: "http://example.com/",
version: "1.0",
description: "Test add-on",
sourceURL: TESTROOT + "addons/browser_install1_1.xpi"
},
{
id: "addon2@tests.mozilla.org",
name : "Install Tests 2",
iconURL: "http://example.com/icon.png",
homepageURL: "http://example.com/",
version: "1.0",
description: "Test add-on 2",
sourceURL: TESTROOT + "addons/browser_install1_2.xpi"
}];
var gPendingTests = [];
var gTestsRun = 0;
var gTestStart = null;
var gDate = new Date(2010, 7, 1);
var gApp = document.getElementById("bundle_brand").getString("brandShortName");
var gCategoryUtilities;
var gSearchCount = 0;
var gProvider = null;
function test() {
waitForExplicitFinish();
Services.prefs.setCharPref(PREF_GETADDONS_GETRECOMMENDED, TESTROOT + "browser_install.xml");
Services.prefs.setCharPref(PREF_GETADDONS_BROWSERECOMMENDED, TESTROOT + "browser_install.xml");
Services.prefs.setCharPref(PREF_GETADDONS_BROWSESEARCHRESULTS, TESTROOT + "browser_install.xml");
Services.prefs.setCharPref(PREF_GETADDONS_GETSEARCHRESULTS, TESTROOT + "browser_install.xml");
Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
run_next_test();
}
function end_test() {
close_manager(function() {
Services.prefs.clearUserPref(PREF_GETADDONS_GETRECOMMENDED);
Services.prefs.clearUserPref(PREF_GETADDONS_BROWSERECOMMENDED);
Services.prefs.clearUserPref(PREF_GETADDONS_GETSEARCHRESULTS);
Services.prefs.clearUserPref(PREF_GETADDONS_BROWSESEARCHRESULTS);
finish();
});
}
function add_test(test) {
gPendingTests.push(test);
}
function run_next_test() {
if (gTestsRun > 0)
info("Test " + gTestsRun + " took " + (Date.now() - gTestStart) + "ms");
if (gPendingTests.length == 0) {
end_test();
return;
}
gTestsRun++;
var test = gPendingTests.shift();
if (test.name)
info("Running test " + gTestsRun + " (" + test.name + ")");
else
info("Running test " + gTestsRun);
gTestStart = Date.now();
test();
}
function checkAttribute(aElt, aAttr, aVal) {
ok(aElt.hasAttribute(aAttr), "Element has " + aAttr + " attribute");
if(aVal)
is(aElt.getAttribute(aAttr), aVal, "Element has " + aAttr + " attribute with value " + aVal);
}
function installExtension(elt, aListener) {
elt.parentNode.ensureElementIsVisible(elt);
elt.install.addListener(aListener)
var button = document.getAnonymousElementByAttribute(elt, "class", "addon-install hide-on-install hide-on-restart");
ok(!!button, "Extension has install button");
ExtensionsView.installFromRepo(elt);
}
function isRestartShown(aShown, isUpdate) {
let msg = document.getElementById("addons-messages");
ok(!!msg, "Have message box");
let notification = msg.getNotificationWithValue("restart-app");
is(!!notification, aShown, "Restart exists = " + aShown);
if(notification) {
let label = "";
dump("Label: " + notification.label + "\n");
if(isUpdate)
label = "Add-ons updated. Restart to complete changes."
else
label = "Restart to complete changes.";
is(notification.label, label, "Restart shows correct message");
}
msg.removeAllNotifications(true)
}
function checkAddonListing(aAddon, elt) {
ok(!!elt, "Element exists for addon");
checkAttribute(elt, "id", "urn:mozilla:item:" + aAddon.id);
checkAttribute(elt, "addonID", aAddon.id);
checkAttribute(elt, "typeName", "search");
checkAttribute(elt, "name", aAddon.name);
checkAttribute(elt, "version", aAddon.version);
checkAttribute(elt, "iconURL", aAddon.iconURL);
checkAttribute(elt, "description", aAddon.description)
checkAttribute(elt, "homepageURL", aAddon.homepageURL);
checkAttribute(elt, "sourceURL", aAddon.sourceURL);
ok(elt.install, "Extension has install property");
}
function checkUpdate(aAddon, aSettings) {
let os = Services.obs;
let ul = new updateListener(aSettings);
os.addObserver(ul, "addon-update-ended", false);
ExtensionsView.updateAll();
//aAddon.findUpdates(new updateListener(aSettings), AddonManager.UPDATE_WHEN_USER_REQUESTED, "4.0", "4.0");
}
function get_addon_element(aId) {
var node = document.getElementById("addons-list").firstChild;
while (node) {
if (("urn:mozilla:item:" + aId) == node.id)
return node;
node = node.nextSibling;
}
return null;
}
function open_manager(aView, aCallback) {
var panelButton = document.getElementById("tool-panel-open")
panelButton.click();
var addonsButton = document.getElementById("tool-addons");
addonsButton.click();
ExtensionsView.init();
ExtensionsView._delayedInit();
// XXX - give the list time to add addons from the repo
// find a better way to do this
setTimeout(function() {
aCallback();
}, 2000);
}
function close_manager(aCallback) {
var prefsButton = document.getElementById("tool-preferences");
prefsButton.click();
BrowserUI.hidePanel();
aCallback();
}
// Installs a bootstrapped (restartless) addon first, and then
// updates it to a non-bootstrapped addon. Checks to make sure
// restart notifications are shown at the right time
// We currently don't handle bootstrapped addons very well, i.e.
// they aren't moved from the browse area into the main area
add_test(function() {
open_manager(null, function() {
var elt = get_addon_element("addon1@tests.mozilla.org");
checkAddonListing(addons[0], elt);
installExtension(elt, new installListener({
showRestart: false,
willFail: false,
onComplete: function(aAddon) {
checkUpdate(aAddon, {
showRestart: true,
willFail: false,
addon: addons[0],
onComplete: function(aAddon) {
close_manager(run_next_test);
}
});
}
}));
});
});
// Installs a non-bootstrapped addon and checks to make sure
// the correct restart notifications are shown
add_test(function() {
open_manager(null, function() {
var elt = get_addon_element("addon2@tests.mozilla.org");
checkAddonListing(addons[1], elt);
installExtension(elt, new installListener({
showRestart: true,
willFail: false,
onComplete: run_next_test,
}));
});
})
function installListener(aSettings) {
this.willFail = aSettings.willFail;
this.onComplete = aSettings.onComplete;
this.showRestart = aSettings.showRestart;
}
installListener.prototype = {
onNewInstall : function(install) { },
onDownloadStarted : function(install) {
info("download started");
},
onDownloadProgress : function(install) {
info("download progress");
},
onDownloadEnded : function(install) {
info("download ended");
},
onDownloadCancelled : function(install) {
info("download cancelled");
},
onDownloadFailed : function(install) {
if(this.willFail)
ok(false, "Install failed");
info("download failed");
},
onInstallStarted : function(install) {
info("Install started");
},
onInstallEnded : function(install, addon) {
// this needs to fire after the extension manager's install ended
let self = this;
setTimeout(function() {
info("Install ended");
isRestartShown(self.showRestart, false);
if(self.onComplete)
self.onComplete(addon);
}, 0);
},
onInstallCancelled : function(install) {
info("Install cancelled");
},
onInstallFailed : function(install) {
if(this.willFail)
ok(false, "Install failed");
info("install failed");
},
onExternalInstall : function(install, existing, needsRestart) { },
};
function updateListener(aSettings) {
this.willFail = aSettings.willFail;
this.onComplete = aSettings.onComplete;
this.showRestart = aSettings.showRestart;
this.addon = aSettings.addon;
}
updateListener.prototype = {
observe: function (aSubject, aTopic, aData) {
switch(aTopic) {
case "addon-update-ended" :
info("Update ended");
// this needs to fire after the extension manager's install ended
let self = this;
let json = aSubject.QueryInterface(Ci.nsISupportsString).data;
let update = JSON.parse(json);
if(update.id == this.addon.id) {
let element = get_addon_element(update.id);
ok(!!element, "Have element for upgrade");
let addon = element.addon;
setTimeout(function() {
isRestartShown(self.showRestart, true);
if(self.onComplete)
self.onComplete(addon);
}, 100);
let os = Services.obs;
os.removeObserver(this, "addon-update-ended", false);
}
break;
}
},
}

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8" ?>
<searchresults total_results="2">
<addon>
<name>Install Tests</name>
<type id='1'>Extension</type>
<guid>addon1@tests.mozilla.org</guid>
<version>1.0</version>
<icon>http://example.com/icon.png</icon>
<homepage>http://example.com/</homepage>
<authors>
<author>
<name>Test Creator</name>
<link>http://example.com/creator.html</link>
</author>
</authors>
<status id='4'>Public</status>
<summary>Test add-on</summary>
<description>Test add-on</description>
<compatible_applications>
<application>
<name>Fennec</name>
<appID>{a23983c0-fd0e-11dc-95ff-0800200c9a66}</appID>
<min_version>0</min_version>
<max_version>5</max_version>
</application>
</compatible_applications>
<compatible_os>ALL</compatible_os>
<install size="2">http://example.com/browser/mobile/chrome/addons/browser_install1_1.xpi</install>
</addon>
<addon>
<name>Install Tests 2</name>
<type id='1'>Extension</type>
<guid>addon2@tests.mozilla.org</guid>
<version>1.0</version>
<icon>http://example.com/icon.png</icon>
<homepage>http://example.com/</homepage>
<authors>
<author>
<name>Test Creator</name>
<link>http://example.com/creator.html</link>
</author>
</authors>
<status id='4'>Public</status>
<summary>Test add-on 2</summary>
<description>Test add-on 2</description>
<compatible_applications>
<application>
<name>Fennec</name>
<appID>{a23983c0-fd0e-11dc-95ff-0800200c9a66}</appID>
<min_version>0</min_version>
<max_version>5</max_version>
</application>
</compatible_applications>
<compatible_os>ALL</compatible_os>
<install size="2">http://example.com/browser/mobile/chrome/addons/browser_install1_2.xpi</install>
</addon>
</searchresults>

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:extension:addon1@tests.mozilla.org">
<em:updates>
<Seq>
<li>
<Description>
<em:version>2.0</em:version>
<em:targetApplication>
<Description>
<em:id>toolkit@mozilla.org</em:id>
<em:minVersion>0</em:minVersion>
<em:maxVersion>*</em:maxVersion>
<em:updateLink>http://example.com/browser/mobile/chrome/addons/browser_install1_3.xpi</em:updateLink>
<em:updateHash>sha1:8c4d2f5c9eadc8850558a9a9618aae25886dbe7c</em:updateHash>
</Description>
</em:targetApplication>
</Description>
</li>
</Seq>
</em:updates>
</Description>
</RDF>