Bug 562795: Sorting by name in the digest view is case-sensitive for all types of add-ons. r=Unfocused

This commit is contained in:
Dave Townsend 2010-06-16 20:13:54 -07:00
parent a34dc9b037
commit b121d79b3c
11 changed files with 534 additions and 30 deletions

View File

@ -324,7 +324,10 @@ user_pref("security.default_personal_cert", "Select Automatically"); // Need to
user_pref("network.http.prompt-temp-redirect", false);
user_pref("media.cache_size", 100);
user_pref("security.warn_viewing_mixed", false);
user_pref("extensions.enabledScopes", 3);
// Only load extensions from the application and user profile
// AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_APPLICATION
user_pref("extensions.enabledScopes", 5);
user_pref("geo.wifi.uri", "http://%(server)s/tests/dom/tests/mochitest/geolocation/network_geolocation.sjs");
user_pref("geo.wifi.testing", true);

View File

@ -230,6 +230,22 @@ var AddonManagerInternal = {
callProvider(aProvider, "startup");
},
/**
* Unregisters an AddonProvider.
*
* @param aProvider
* The provider to unregister
*/
unregisterProvider: function AMI_unregisterProvider(aProvider) {
this.providers = this.providers.filter(function(p) {
return p != aProvider;
});
// If we're unregistering after startup call this provider's shutdown.
if (this.started)
callProvider(aProvider, "shutdown");
},
/**
* Shuts down the addon manager and all registered providers, this must clean
* up everything in order for automated tests to fake restarts.
@ -750,6 +766,10 @@ var AddonManagerPrivate = {
AddonManagerInternal.registerProvider(aProvider);
},
unregisterProvider: function AMP_unregisterProvider(aProvider) {
AddonManagerInternal.unregisterProvider(aProvider);
},
shutdown: function AMP_shutdown() {
AddonManagerInternal.shutdown();
},

View File

@ -60,6 +60,7 @@ const SEARCH_SCORE_MATCH_SUBSTRING = 0.3;
const VIEW_DEFAULT = "addons://list/extension";
const INTEGER_FIELDS = ["dateUpdated", "size", "relevancescore"];
var gStrings = {};
XPCOMUtils.defineLazyServiceGetter(gStrings, "bundleSvc",
@ -603,14 +604,11 @@ function createItem(aObj, aIsInstall, aRequiresRestart) {
// the binding handles the rest
item.setAttribute("value", aObj.id);
var updated = "000000000000000"; // HACK: nsIXULSortService doesn't do numerical sorting (bug 379745)
if (aObj.updateDate)
updated = (updated + aObj.updateDate.valueOf()).slice(-14);
item.setAttribute("dateUpdated", updated);
item.setAttribute("dateUpdated", aObj.updateDate.getTime());
var size = Math.floor(Math.random() * 1024 * 1024 * 2);
size = ("00000000000" + size).slice(-10); // HACK: nsIXULSortService doesn't do numerical sorting (bug 379745)
item.setAttribute("size", size); // XXXapi - bug 561261
if (aObj.size)
item.setAttribute("size", aObj.size);
}
return item;
}
@ -943,10 +941,15 @@ var gSearchView = {
onSortChanged: function(aSortBy, aAscending) {
var header = this._listBox.firstChild;
this._listBox.removeChild(header);
var hints = aAscending ? "ascending" : "descending";
if (INTEGER_FIELDS.indexOf(aSortBy) >= 0)
hints += " integer";
var sortService = Cc["@mozilla.org/xul/xul-sort-service;1"].
getService(Ci.nsIXULSortService);
sortService.sort(this._listBox, aSortBy,
aAscending ? "ascending" : "descending");
sortService.sort(this._listBox, aSortBy, hints);
this._listBox.insertBefore(header, this._listBox.firstChild);
},
@ -1055,10 +1058,13 @@ var gListView = {
},
onSortChanged: function(aSortBy, aAscending) {
var hints = aAscending ? "ascending" : "descending";
if (INTEGER_FIELDS.indexOf(aSortBy) >= 0)
hints += " integer";
var sortService = Cc["@mozilla.org/xul/xul-sort-service;1"].
getService(Ci.nsIXULSortService);
sortService.sort(this._listBox, aSortBy,
aAscending ? "ascending" : "descending");
sortService.sort(this._listBox, aSortBy, hints);
},
onNewInstall: function(aInstall) {

View File

@ -294,13 +294,16 @@
<method name="_handleChange">
<parameter name="aSort"/>
<body><![CDATA[
const ASCENDING_SORT_FIELDS = ["name"];
if (aSort == this.sortBy) {
this.ascending = !this.ascending;
return;
}
this.sortBy = aSort;
this.ascending = false;
// Name sorting defaults to ascending, others to descending
this.ascending = ASCENDING_SORT_FIELDS.indexOf(aSort) >= 0;
this._refreshState();
]]></body>
@ -908,9 +911,9 @@
<method name="_updateSize">
<body><![CDATA[
var bytes = this.getAttribute("size"); // this.mAddon.size; //XXXunf api
var bytes = this.getAttribute("size"); // this.mAddon.size;
var formatted = gStrings.dl.GetStringFromName("doneSizeUnknown");
if (bytes > 0) {
if (bytes && bytes > 0) {
let [size, unit] = DownloadUtils.convertByteUnits(bytes);
formatted = gStrings.dl.GetStringFromName("doneSize");
formatted = formatted.replace("#1", size).replace("#2", unit);

View File

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

View File

@ -5,8 +5,6 @@
// Simulates quickly switching between different list views to verify that only
// the last selected is displayed
Components.utils.import("resource://gre/modules/AddonManager.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm");
const xpi = "browser/toolkit/mozapps/extensions/test/browser/browser_installssl.xpi";
@ -15,7 +13,6 @@ var gManagerWindow;
function test() {
waitForExplicitFinish();
Services.prefs.setBoolPref(PREF_LOGGING_ENABLED, true);
// Add a lightweight theme so at least one theme exists
LightweightThemeManager.currentTheme = {
@ -31,7 +28,6 @@ function test() {
}
function end_test() {
Services.prefs.clearUserPref(PREF_LOGGING_ENABLED);
gManagerWindow.close();
LightweightThemeManager.forgetUsedTheme("test");

View File

@ -8,9 +8,6 @@
// Tests are only simulations of the drag and drop events, we cannot really do
// this automatically.
Components.utils.import("resource://gre/modules/AddonManager.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
var gManagerWindow;
// This listens for the next opened window and checks it is of the right url.
@ -74,7 +71,6 @@ WindowOpenListener.prototype = {
function test() {
waitForExplicitFinish();
Services.prefs.setBoolPref(PREF_LOGGING_ENABLED, true);
open_manager(null, function(aWindow) {
gManagerWindow = aWindow;
@ -83,7 +79,6 @@ function test() {
}
function end_test() {
Services.prefs.clearUserPref(PREF_LOGGING_ENABLED);
gManagerWindow.close();
finish();

View File

@ -2,9 +2,6 @@
* http://creativecommons.org/publicdomain/zero/1.0/
*/
Components.utils.import("resource://gre/modules/AddonManager.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
const xpi = RELATIVE_DIR + "addons/browser_installssl.xpi";
const redirect = RELATIVE_DIR + "redirect.sjs?";
const SUCCESS = 0;
@ -13,13 +10,11 @@ var gTests = [];
function test() {
waitForExplicitFinish();
Services.prefs.setBoolPref(PREF_LOGGING_ENABLED, true);
run_next_test();
}
function end_test() {
Services.prefs.clearUserPref(PREF_LOGGING_ENABLED);
var cos = Cc["@mozilla.org/security/certoverride;1"].
getService(Ci.nsICertOverrideService);
cos.clearValidityOverride("nocert.example.com", -1);

View File

@ -0,0 +1,151 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Tests that sorting of add-ons in the list views works correctly
var gManagerWindow;
var gProvider;
function test() {
waitForExplicitFinish();
gProvider = new MockProvider();
gProvider.createAddons([{
id: "test1@tests.mozilla.org",
name: "Test add-on",
description: "foo",
updateDate: new Date(2010, 04, 02, 00, 00, 00),
size: 1
}, {
id: "test2@tests.mozilla.org",
name: "a first add-on",
description: "foo",
updateDate: new Date(2010, 04, 01, 23, 59, 59),
size: 0265
}, {
id: "test3@tests.mozilla.org",
name: "\u010Cesk\u00FD slovn\u00EDk", // Český slovník
description: "foo",
updateDate: new Date(2010, 04, 02, 00, 00, 01),
size: 12
}, {
id: "test4@tests.mozilla.org",
name: "canadian dictionary",
updateDate: new Date(0, 0, 01, 00, 00, 00),
description: "foo",
}, {
id: "test5@tests.mozilla.org",
name: "croatian dictionary",
description: "foo",
updateDate: new Date(2012, 12, 12, 00, 00, 00),
size: 5
}]);
open_manager(null, function(aWindow) {
gManagerWindow = aWindow;
run_next_test();
});
}
function end_test() {
gManagerWindow.close();
finish();
}
function check_order(aExpectedOrder) {
var order = [];
var list = gManagerWindow.document.getElementById("addon-list");
var node = list.firstChild;
while (node) {
var id = node.getAttribute("value");
if (id && id.substring(id.length - 18) != "@tests.mozilla.org")
return;
order.push(node.getAttribute("value"));
node = node.nextSibling;
}
is(order.toSource(), aExpectedOrder.toSource(), "Should have seen the right order");
}
// Tests that ascending name ordering was the default
add_test(function() {
check_order([
"test2@tests.mozilla.org",
"test4@tests.mozilla.org",
"test3@tests.mozilla.org",
"test5@tests.mozilla.org",
"test1@tests.mozilla.org"
]);
run_next_test();
});
// Tests that switching to size ordering works and defaults to descending
add_test(function() {
var sorters = gManagerWindow.document.getElementById("list-sorters");
var nameSorter = gManagerWindow.document.getAnonymousElementByAttribute(sorters, "anonid", "btn-size");
EventUtils.synthesizeMouse(nameSorter, 2, 2, { }, gManagerWindow);
check_order([
"test2@tests.mozilla.org",
"test3@tests.mozilla.org",
"test5@tests.mozilla.org",
"test1@tests.mozilla.org",
"test4@tests.mozilla.org"
]);
run_next_test();
});
// Tests that switching to ascending size ordering works
add_test(function() {
var sorters = gManagerWindow.document.getElementById("list-sorters");
var nameSorter = gManagerWindow.document.getAnonymousElementByAttribute(sorters, "anonid", "btn-size");
EventUtils.synthesizeMouse(nameSorter, 2, 2, { }, gManagerWindow);
check_order([
"test4@tests.mozilla.org",
"test1@tests.mozilla.org",
"test5@tests.mozilla.org",
"test3@tests.mozilla.org",
"test2@tests.mozilla.org"
]);
run_next_test();
});
// Tests that switching to date ordering works and defaults to descending
add_test(function() {
var sorters = gManagerWindow.document.getElementById("list-sorters");
var nameSorter = gManagerWindow.document.getAnonymousElementByAttribute(sorters, "anonid", "btn-date");
EventUtils.synthesizeMouse(nameSorter, 2, 2, { }, gManagerWindow);
check_order([
"test5@tests.mozilla.org",
"test3@tests.mozilla.org",
"test1@tests.mozilla.org",
"test2@tests.mozilla.org",
"test4@tests.mozilla.org"
]);
run_next_test();
});
// Tests that switching to name ordering works and defaults to ascending
add_test(function() {
var sorters = gManagerWindow.document.getElementById("list-sorters");
var nameSorter = gManagerWindow.document.getAnonymousElementByAttribute(sorters, "anonid", "btn-name");
EventUtils.synthesizeMouse(nameSorter, 2, 2, { }, gManagerWindow);
check_order([
"test2@tests.mozilla.org",
"test4@tests.mozilla.org",
"test3@tests.mozilla.org",
"test5@tests.mozilla.org",
"test1@tests.mozilla.org"
]);
run_next_test();
});

View File

@ -12,13 +12,11 @@ var gTests = [];
function test() {
waitForExplicitFinish();
Services.prefs.setBoolPref(PREF_LOGGING_ENABLED, true);
run_next_test();
}
function end_test() {
Services.prefs.clearUserPref(PREF_LOGGING_ENABLED);
var cos = Cc["@mozilla.org/security/certoverride;1"].
getService(Ci.nsICertOverrideService);
cos.clearValidityOverride("nocert.example.com", -1);

View File

@ -2,6 +2,9 @@
* http://creativecommons.org/publicdomain/zero/1.0/
*/
Components.utils.import("resource://gre/modules/AddonManager.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
const RELATIVE_DIR = "browser/toolkit/mozapps/extensions/test/browser/";
const TESTROOT = "http://example.com/" + RELATIVE_DIR;
@ -15,6 +18,12 @@ const PREF_LOGGING_ENABLED = "extensions.logging.enabled";
var gPendingTests = [];
var gTestsRun = 0;
// Turn logging on for all tests
Services.prefs.setBoolPref(PREF_LOGGING_ENABLED, true);
registerCleanupFunction(function() {
Services.prefs.clearUserPref(PREF_LOGGING_ENABLED);
});
function add_test(test) {
gPendingTests.push(test);
}
@ -118,3 +127,330 @@ function addCertOverride(host, bits) {
// This request will fail since the SSL server is not trusted yet
}
}
/***** Mock Provider *****/
function MockProvider(aUseAsyncCallbacks) {
this.addons = [];
this.installs = [];
this.callbackTimers = [];
this.useAsyncCallbacks = (aUseAsyncCallbacks === undefined) ? true : aUseAsyncCallbacks;
var self = this;
registerCleanupFunction(function() {
if (self.started)
self.unregister();
});
this.register();
}
MockProvider.prototype = {
addons: null,
installs: null,
started: null,
apiDelay: 100,
callbackTimers: null,
useAsyncCallbacks: null,
/***** Utility functions *****/
/**
* Register this provider with the AddonManager
*/
register: function MP_register() {
AddonManagerPrivate.registerProvider(this);
},
/**
* Unregister this provider with the AddonManager
*/
unregister: function MP_unregister() {
AddonManagerPrivate.unregisterProvider(this);
},
/**
* Adds an add-on to the list of add-ons that this provider exposes to the
* AddonManager, dispatching appropriate events in the process.
*
* @param aAddon
* The add-on to add
*/
addAddon: function MP_addAddon(aAddon) {
this.addons.push(aAddon);
if (!this.started)
return;
AddonManagerPrivate.callInstallListeners("onExternalInstall", null, aAddon,
null, false)
},
/**
* 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
*/
createAddons: function MP_createAddons(aAddonProperties) {
aAddonProperties.forEach(function(aAddonProp) {
var addon = new MockAddon(aAddonProp.id);
for (var prop in aAddonProp) {
if (prop == "id")
continue;
addon[prop] = aAddonProp[prop];
}
this.addAddon(addon);
}, this);
},
/***** AddonProvider implementation *****/
/**
* Called to initialize the provider.
*/
startup: function MP_startup() {
this.started = true;
},
/**
* Called when the provider should shutdown.
*/
shutdown: function MP_shutdown() {
this.callbackTimers.forEach(function(aTimer) {
aTimer.cancel();
});
this.callbackTimers = [];
this.started = false;
},
/**
* Called to get an Addon with a particular ID.
*
* @param aId
* The ID of the add-on to retrieve
* @param aCallback
* A callback to pass the Addon to
*/
getAddonByID: function MP_getAddon(aId, aCallback) {
for (let i = 0; i < this.addons.length; i++) {
if (this.addons[i].id == aId) {
this._delayCallback(aCallback, this.addons[i]);
return;
}
}
},
/**
* Called to get Addons of a particular type.
*
* @param aTypes
* An array of types to fetch. Can be null to get all types.
* @param callback
* A callback to pass an array of Addons to
*/
getAddonsByTypes: function MP_getAddonsByTypes(aTypes, aCallback) {
var addons = this.addons.filter(function(aAddon) {
if (aTypes && aTypes.length > 0 && aTypes.indexOf(aAddon.type) == -1)
return false;
return true;
});
this._delayCallback(aCallback, addons);
},
/**
* Called to get Addons that have pending operations.
*
* @param aTypes
* An array of types to fetch. Can be null to get all types
* @param aCallback
* A callback to pass an array of Addons to
*/
getAddonsWithOperationsByTypes: function MP_getAddonsWithOperationsByTypes(aTypes, aCallback) {
var addons = this.addons.filter(function(aAddon) {
if (aTypes && aTypes.length > 0 && aTypes.indexOf(aAddon.type) == -1)
return false;
return aAddon.pendingOperations != 0;
});
this._delayCallback(aCallback, addons);
},
/**
* Called to get the current AddonInstalls, optionally restricting by type.
*
* @param aTypes
* An array of types or null to get all types
* @param aCallback
* A callback to pass the array of AddonInstalls to
*/
getInstallsByTypes: function MP_getInstallsByTypes(aTypes, aCallback) {
var installs = this.installs.filter(function(aInstall) {
if (aTypes && aTypes.length > 0 && aTypes.indexOf(aInstall.type) == -1)
return false;
return true;
});
this._delayCallback(aCallback, installs);
},
/**
* Called when a new add-on has been enabled when only one add-on of that type
* can be enabled.
*
* @param aId
* The ID of the newly enabled add-on
* @param aType
* The type of the newly enabled add-on
* @param aPendingRestart
* true if the newly enabled add-on will only become enabled after a
* restart
*/
addonChanged: function MP_addonChanged(aId, aType, aPendingRestart) {
// Not implemented
},
/**
* Update the appDisabled property for all add-ons.
*/
updateAddonAppDisabledStates: function MP_updateAddonAppDisabledStates() {
// Not needed
},
/**
* Called to get an AddonInstall to download and install an add-on from a URL.
*
* @param aUrl
* The URL to be installed
* @param aHash
* A hash for the install
* @param aName
* A name for the install
* @param aIconURL
* An icon URL for the install
* @param aVersion
* A version for the install
* @param aLoadGroup
* An nsILoadGroup to associate requests with
* @param aCallback
* A callback to pass the AddonInstall to
*/
getInstallForURL: function MP_getInstallForURL(aUrl, aHash, aName, aIconURL,
aVersion, aLoadGroup, aCallback) {
// Not yet implemented
},
/**
* Called to get an AddonInstall to install an add-on from a local file.
*
* @param aFile
* The file to be installed
* @param aCallback
* A callback to pass the AddonInstall to
*/
getInstallForFile: function MP_getInstallForFile(aFile, aCallback) {
// Not yet implemented
},
/**
* Called to test whether installing add-ons is enabled.
*
* @return true if installing is enabled
*/
isInstallEnabled: function MP_isInstallEnabled() {
return false;
},
/**
* Called to test whether this provider supports installing a particular
* mimetype.
*
* @param aMimetype
* The mimetype to check for
* @return true if the mimetype is supported
*/
supportsMimetype: function MP_supportsMimetype(aMimetype) {
return false;
},
/**
* Called to test whether installing add-ons from a URI is allowed.
*
* @param aUri
* The URI being installed from
* @return true if installing is allowed
*/
isInstallAllowed: function MP_isInstallAllowed(aUri) {
return false;
},
/***** Internal functions *****/
/**
* Delay calling a callback to fake a time-consuming async operation.
* The delay is specified by the apiDelay property, in milliseconds.
* Parameters to send to the callback should be specified as arguments after
* the aCallback argument.
*
* @param aCallback Callback to eventually call
*/
_delayCallback: function MP_delayCallback(aCallback) {
var params = Array.splice(arguments, 1);
if (!this.useAsyncCallbacks) {
aCallback.apply(null, params);
return;
}
var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
// Need to keep a reference to the timer, so it doesn't get GC'ed
var pos = this.callbackTimers.length;
this.callbackTimers.push(timer);
var self = this;
timer.initWithCallback(function() {
self.callbackTimers.splice(pos, 1);
aCallback.apply(null, params);
}, this.apiDelay, timer.TYPE_ONE_SHOT);
}
};
/***** Mock Addon object for the Mock Provider *****/
function MockAddon(aId, aName, aType, aRestartless) {
// Only set required attributes
this.id = aId || "";
this.name = aName || "";
this.type = aType || "extension";
this.version = "";
this.isCompatible = true;
this.providesUpdatesSecurely = true;
this.blocklistState = 0;
this.appDisabled = false;
this.userDisabled = false;
this.scope = AddonManager.SCOPE_PROFILE;
this.isActive = true;
this.creator = "";
this.pendingOperations = 0;
this.permissions = 0;
this._restartless = aRestartless || false;
}
MockAddon.prototype = {
isCompatibleWith: function(aAppVersion, aPlatformVersion) {
return true;
},
findUpdates: function(aListener, aReason, aAppVersion, aPlatformVersion) {
// Tests can implement this if they need to
},
uninstall: function() {
// To be implemented when needed
},
cancelUninstall: function() {
// To be implemented when needed
}
};