Bug 572561: Addons Manager hides Language category if there are no locale packs installed but some are pending install. r=Unfocused, r=dtownsend

This commit is contained in:
Ben Parr 2010-06-24 12:50:04 -07:00
Родитель 4170a7ca93
Коммит fce042dceb
7 изменённых файлов: 358 добавлений и 50 удалений

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

@ -88,6 +88,9 @@ XPCOMUtils.defineLazyGetter(gStrings, "appVersion", function() {
window.addEventListener("load", initialize, false);
window.addEventListener("unload", shutdown, false);
var gPendingInitializations = 1;
__defineGetter__("gIsInitializing", function() gPendingInitializations > 0);
function initialize() {
gCategories.initialize();
gHeader.initialize();
@ -105,6 +108,19 @@ function initialize() {
}
gViewController.loadView(view);
notifyInitialized();
}
function notifyInitialized() {
if (!gIsInitializing)
return;
gPendingInitializations--;
if (!gIsInitializing) {
var event = document.createEvent("Events");
event.initEvent("Initialized", true, true);
document.dispatchEvent(event);
}
}
function shutdown() {
@ -258,6 +274,10 @@ var gViewController = {
return {type: viewType, param: decodeURIComponent(viewParam)};
},
get isLoading() {
return this.currentViewObj.node.hasAttribute("loading");
},
loadView: function(aViewId) {
if (aViewId == this.currentViewId)
return;
@ -616,6 +636,39 @@ function createItem(aObj, aIsInstall, aRequiresRestart) {
return item;
}
function getAddonsAndInstalls(aType, aCallback) {
var addonTypes = null, installTypes = null;
if (aType != null) {
addonTypes = [aType];
installTypes = [aType];
if (aType == "extension") {
addonTypes.push("bootstrapped");
installTypes = addonTypes.concat("");
}
}
var addons = null, installs = null;
AddonManager.getAddonsByTypes(addonTypes, function(aAddonsList) {
addons = aAddonsList;
if (installs != null)
aCallback(addons, installs);
});
AddonManager.getInstallsByTypes(installTypes, function(aInstallsList) {
// skip over upgrade installs and non-active installs
installs = aInstallsList.filter(function(aInstall) {
return !(aInstall.existingAddon ||
aInstall.state == AddonManager.STATE_AVAILABLE);
});
if (addons != null)
aCallback(addons, installs)
});
return {addon: addonTypes, install: installTypes};
}
var gCategories = {
node: null,
@ -648,16 +701,18 @@ var gCategories = {
}, false);
var maybeHidden = ["addons://list/locale", "addons://list/searchengine"];
gPendingInitializations += maybeHidden.length;
maybeHidden.forEach(function(aId) {
var type = gViewController.parseViewId(aId).param;
AddonManager.getAddonsByTypes([type], function(aAddonsList) {
if (aAddonsList.length > 0) {
getAddonsAndInstalls(type, function(aAddonsList, aInstallsList) {
if (aAddonsList.length > 0 || aInstallsList.length > 0) {
self.get(aId).hidden = false;
notifyInitialized();
return;
}
gEventManager.registerInstallListener({
onNewInstall: function(aInstall) {
onDownloadStarted: function(aInstall) {
this._maybeShowCategory(aInstall);
},
@ -680,6 +735,8 @@ var gCategories = {
}
}
});
notifyInitialized();
});
});
},
@ -795,6 +852,7 @@ var gDiscoverView = {
.getService(Ci.nsIURLFormatter)
.formatURLPref(PREF_DISCOVERURL);
gPendingInitializations++;
AddonManager.getAllAddons(function(aAddons) {
var list = {};
aAddons.forEach(function(aAddon) {
@ -809,6 +867,7 @@ var gDiscoverView = {
});
gDiscoverView._browser.homePage = url + "#" + JSON.stringify(list);
notifyInitialized();
});
},
@ -996,33 +1055,21 @@ var gListView = {
gHeader.setName(gStrings.ext.GetStringFromName("header-" + aType));
this.showEmptyNotice(false);
this._types = [aType];
this._installTypes = [aType];
if (aType == "extension") {
this._types.push("bootstrapped");
this._installTypes = this._types.concat("");
}
while (this._listBox.itemCount > 0)
this._listBox.removeItemAt(0);
var self = this;
var addons = null, installs = null;
function updateList() {
if (addons == null || installs == null)
var types = getAddonsAndInstalls(aType, function(aAddonsList, aInstallsList) {
if (gViewController && aRequest != gViewController.currentViewRequest)
return;
for (let i = 0; i < addons.length; i++) {
let item = createItem(addons[i]);
for (let i = 0; i < aAddonsList.length; i++) {
let item = createItem(aAddonsList[i]);
self._listBox.appendChild(item);
}
for (let i = 0; i < installs.length; i++) {
// skip over upgrade installs
if (installs[i].existingAddon)
continue;
let item = createItem(installs[i], true);
for (let i = 0; i < aInstallsList.length; i++) {
let item = createItem(aInstallsList[i], true);
self._listBox.appendChild(item);
}
@ -1031,25 +1078,13 @@ var gListView = {
else
self.showEmptyNotice(true);
gEventManager.registerInstallListener(self);
gViewController.updateCommands();
gViewController.notifyViewChanged();
}
AddonManager.getAddonsByTypes(this._types, function(aAddonsList) {
if (gViewController && aRequest != gViewController.currentViewRequest)
return;
addons = aAddonsList;
updateList();
});
AddonManager.getInstallsByTypes(this._installTypes, function(aInstallsList) {
if (gViewController && aRequest != gViewController.currentViewRequest)
return;
installs = aInstallsList;
updateList();
gEventManager.registerInstallListener(self);
});
this._types = types.addon;
this._installTypes = types.install;
},
hide: function() {
@ -1277,7 +1312,7 @@ var gDragDrop = {
},
onDrop: function(aEvent) {
var dataTransfer = aEvent.dataTransfer;
var dataTransfer = aEvent.dataTransfer;
var urls = [];
// Convert every dropped item into a url

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

@ -49,6 +49,7 @@ _TEST_FILES = \
head.js \
browser_bug562890.js \
browser_bug562899.js \
browser_bug572561.js \
browser_dragdrop.js \
browser_sorting.js \
browser_updatessl.js \

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

@ -28,10 +28,10 @@ function test() {
}
function end_test() {
gManagerWindow.close();
LightweightThemeManager.forgetUsedTheme("test");
finish();
close_manager(gManagerWindow, function() {
LightweightThemeManager.forgetUsedTheme("test");
finish();
});
}
// Tests that loading a second view before the first has not finished loading

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

@ -0,0 +1,94 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Tests that the locale category is shown if there are no locale packs
// installed but some are pending install
var gManagerWindow;
var gProvider;
var gInstallProperties = [{
name: "Locale Category Test",
type: "locale"
}];
var gInstall;
var gExpectedCancel = false;
var gTestInstallListener = {
onInstallStarted: function(aInstall) {
check_hidden(false);
},
onInstallEnded: function(aInstall) {
check_hidden(false);
run_next_test();
},
onInstallCancelled: function(aInstall) {
ok(gExpectedCancel, "Should expect install cancel");
check_hidden(false);
run_next_test();
},
onInstallFailed: function(aInstall) {
ok(false, "Did not expect onInstallFailed");
}
};
function test() {
waitForExplicitFinish();
gProvider = new MockProvider();
open_manager(null, function(aWindow) {
gManagerWindow = aWindow;
run_next_test();
});
}
function end_test() {
close_manager(gManagerWindow, function() {
finish();
});
}
function check_hidden(aExpectedHidden) {
var category = gManagerWindow.gCategories.get("addons://list/locale");
is(category.hidden, aExpectedHidden, "Should have correct hidden state");
}
// Tests that a non-active install does not make the locale category show
add_test(function() {
check_hidden(true);
gInstall = gProvider.createInstalls(gInstallProperties)[0];
gInstall.addTestListener(gTestInstallListener);
check_hidden(true);
run_next_test();
});
// Test that restarting the add-on manager with a non-active install
// does not cause the locale category to show
add_test(function() {
restart_manager(gManagerWindow, null, function(aWindow) {
gManagerWindow = aWindow;
check_hidden(true);
run_next_test();
});
});
// Test that installing the install shows the locale category
add_test(function() {
gInstall.install();
});
// Test that restarting the add-on manager does not cause the locale category
// to become hidden
add_test(function() {
restart_manager(gManagerWindow, null, function(aWindow) {
gManagerWindow = aWindow;
check_hidden(false);
gExpectedCancel = true;
gInstall.cancel();
});
});

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

@ -79,9 +79,9 @@ function test() {
}
function end_test() {
gManagerWindow.close();
finish();
close_manager(gManagerWindow, function() {
finish();
});
}
function test_confirmation(aWindow, aExpectedURLs) {

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

@ -49,9 +49,9 @@ function test() {
}
function end_test() {
gManagerWindow.close();
finish();
close_manager(gManagerWindow, function() {
finish();
});
}
function check_order(aExpectedOrder) {

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

@ -48,7 +48,7 @@ function get_addon_file_url(aFilename) {
}
function wait_for_view_load(aManagerWindow, aCallback) {
if (!aManagerWindow.gViewController.currentViewObj.node.hasAttribute("loading")) {
if (!aManagerWindow.gViewController.isLoading) {
aCallback(aManagerWindow);
return;
}
@ -67,7 +67,15 @@ function open_manager(aView, aCallback) {
ok(aManagerWindow != null, "Should have an add-ons manager window");
is(aManagerWindow.location, MANAGER_URI, "Should be displaying the correct UI");
wait_for_view_load(aManagerWindow, aCallback);
if (!aManagerWindow.gIsInitializing) {
wait_for_view_load(aManagerWindow, aCallback);
return;
}
aManagerWindow.document.addEventListener("Initialized", function() {
aManagerWindow.document.removeEventListener("Initialized", arguments.callee, false);
wait_for_view_load(aManagerWindow, aCallback);
}, false);
}
if ("switchToTabHavingURI" in window) {
@ -77,12 +85,28 @@ function open_manager(aView, aCallback) {
return;
}
openDialog("about:addons").addEventListener("load", function() {
openDialog(MANAGER_URI).addEventListener("load", function() {
this.removeEventListener("load", arguments.callee, false);
setup_manager(this);
}, false);
}
function close_manager(aManagerWindow, aCallback) {
ok(aManagerWindow != null, "Should have an add-ons manager window to close");
is(aManagerWindow.location, MANAGER_URI, "Should be closing window with correct URI");
aManagerWindow.addEventListener("unload", function() {
this.removeEventListener("unload", arguments.callee, false);
aCallback();
}, false);
aManagerWindow.close();
}
function restart_manager(aManagerWindow, aView, aCallback) {
close_manager(aManagerWindow, function() { open_manager(aView, aCallback); });
}
function CertOverrideListener(host, bits) {
this.host = host;
this.bits = bits;
@ -186,14 +210,32 @@ MockProvider.prototype = {
null, false)
},
/**
* Adds an add-on install to the list of installs that this provider exposes
* to the AddonManager, dispatching appropriate events in the process.
*
* @param aInstall
* The add-on install to add
*/
addInstall: function MP_addInstall(aInstall) {
this.installs.push(aInstall);
if (!this.started)
return;
aInstall.callListeners("onNewInstall");
},
/**
* Creates a set of mock add-on objects and adds them to the list of add-ons
* managed by this provider.
*
* @param aAddonProperties
* An array of objects containing properties describing the add-ons
* @return Array of the new MockAddons
*/
createAddons: function MP_createAddons(aAddonProperties) {
var newAddons = [];
aAddonProperties.forEach(function(aAddonProp) {
var addon = new MockAddon(aAddonProp.id);
for (var prop in aAddonProp) {
@ -202,7 +244,32 @@ MockProvider.prototype = {
addon[prop] = aAddonProp[prop];
}
this.addAddon(addon);
newAddons.push(addon);
}, this);
return newAddons;
},
/**
* Creates a set of mock add-on install objects and adds them to the list
* of installs managed by this provider.
*
* @param aInstallProperties
* An array of objects containing properties describing the installs
* @return Array of the new MockInstalls
*/
createInstalls: function MP_createInstalls(aInstallProperties) {
var newInstalls = [];
aInstallProperties.forEach(function(aInstallProp) {
var install = new MockInstall();
for (var prop in aInstallProp) {
install[prop] = aInstallProp[prop];
}
this.addInstall(install);
newInstalls.push(install);
}, this);
return newInstalls;
},
/***** AddonProvider implementation *****/
@ -287,8 +354,13 @@ MockProvider.prototype = {
*/
getInstallsByTypes: function MP_getInstallsByTypes(aTypes, aCallback) {
var installs = this.installs.filter(function(aInstall) {
// Appear to have actually removed cancelled installs from the provider
if (aInstall.state == AddonManager.STATE_CANCELLED)
return false;
if (aTypes && aTypes.length > 0 && aTypes.indexOf(aInstall.type) == -1)
return false;
return true;
});
this._delayCallback(aCallback, installs);
@ -454,3 +526,109 @@ MockAddon.prototype = {
// To be implemented when needed
}
};
/***** Mock AddonInstall object for the Mock Provider *****/
function MockInstall(aName, aType) {
this.name = aName || "";
this.type = aType || "extension";
this.version = "1.0";
this.iconURL = "";
this.infoURL = "";
this.state = AddonManager.STATE_AVAILABLE;
this.error = 0;
this.sourceURL = "";
this.file = null;
this.progress = 0;
this.maxProgress = -1;
this.certificate = null;
this.certName = "";
this.existingAddon = null;
this.addon = null;
this.listeners = [];
// Another type of install listener for tests that want to check the results
// of code run from standard install listeners
this.testListeners = [];
}
MockInstall.prototype = {
install: function() {
switch (this.state) {
case AddonManager.STATE_AVAILABLE:
case AddonManager.STATE_DOWNLOADED:
// Downloading to be implemented when needed
this.state = AddonManager.STATE_INSTALLING;
if (!this.callListeners("onInstallStarted")) {
// Reverting to STATE_DOWNLOADED instead to be implemented when needed
this.state = AddonManager.STATE_CANCELLED;
this.callListeners("onInstallCancelled");
return;
}
// Adding addon to MockProvider to be implemented when needed
this.addon = new MockAddon("", this.name, this.type);
this.state = AddonManager.STATE_INSTALLED;
this.callListeners("onInstallEnded");
break;
case AddonManager.STATE_DOWNLOADING:
case AddonManager.STATE_CHECKING:
case AddonManger.STATE_INSTALLING:
// Installation is already running
return;
default:
ok(false, "Cannot start installing when state = " + this.state);
}
},
cancel: function() {
switch (this.state) {
case AddonManager.STATE_AVAILABLE:
this.state = AddonManager.STATE_CANCELLED;
break;
case AddonManager.STATE_INSTALLED:
this.state = AddonManager.STATE_CANCELLED;
this.callListeners("onInstallCancelled");
break;
default:
// Handling cancelling when downloading to be implemented when needed
ok(false, "Cannot cancel when state = " + this.state);
}
},
addListener: function(aListener) {
if (!this.listeners.some(function(i) i == aListener))
this.listeners.push(aListener);
},
removeListener: function(aListener) {
this.listeners = this.listeners.filter(function(i) i != aListener);
},
addTestListener: function(aListener) {
if (!this.testListeners.some(function(i) i == aListener))
this.testListeners.push(aListener);
},
removeTestListener: function(aListener) {
this.testListeners = this.testListeners.filter(function(i) i != aListener);
},
callListeners: function(aMethod) {
var result = AddonManagerPrivate.callInstallListeners(aMethod, this.listeners,
this, this.addon);
// Call test listeners after standard listeners to remove race condition
// between standard and test listeners
this.testListeners.forEach(function(aListener) {
if (aMethod in aListener)
if (aListener[aMethod].call(aListener, this, this.addon) === false)
result = false;
});
return result;
}
};