Bug 1323391 - Sort sites listed in Settings of Site Data, r=Gijs
MozReview-Commit-ID: ExHvd6OJNF7
--- a/browser/components/preferences/in-content/tests/browser_advanced_siteData.js
+++ b/browser/components/preferences/in-content/tests/browser_advanced_siteData.js
@@ -28,16 +28,68 @@ const mockOfflineAppCacheHelper = {
OfflineAppCacheHelper.clear = this.clear;
},
unregister() {
OfflineAppCacheHelper.clear = this.originalClear;
}
};
+const mockSiteDataManager = {
+ sites: new Map([
+ [
+ "https://shopping.xyz.com/",
+ {
+ usage: 102400,
+ host: "shopping.xyz.com",
+ status: Ci.nsIPermissionManager.ALLOW_ACTION
+ }
+ ],
+ [
+ "https://music.bar.com/",
+ {
+ usage: 10240,
+ host: "music.bar.com",
+ status: Ci.nsIPermissionManager.ALLOW_ACTION
+ }
+ ],
+ [
+ "https://news.foo.com/",
+ {
+ usage: 1024,
+ host: "news.foo.com",
+ status: Ci.nsIPermissionManager.DENY_ACTION
+ }
+ ]
+ ]),
+
+ _originalGetSites: null,
+
+ getSites() {
+ let list = [];
+ this.sites.forEach((data, origin) => {
+ list.push({
+ usage: data.usage,
+ status: data.status,
+ uri: NetUtil.newURI(origin)
+ });
+ });
+ return Promise.resolve(list);
+ },
+
+ register() {
+ this._originalGetSites = SiteDataManager.getSites;
+ SiteDataManager.getSites = this.getSites.bind(this);
+ },
+
+ unregister() {
+ SiteDataManager.getSites = this._originalGetSites;
+ }
+};
+
function addPersistentStoragePerm(origin) {
let uri = NetUtil.newURI(origin);
let principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
Services.perms.addFromPrincipal(principal, "persistent-storage", Ci.nsIPermissionManager.ALLOW_ACTION);
}
function getPersistentStoragePermStatus(origin) {
let uri = NetUtil.newURI(origin);
@@ -86,17 +138,17 @@ registerCleanupFunction(function() {
});
add_task(function* () {
yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
addPersistentStoragePerm(TEST_ORIGIN);
yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_BASE_URL + "site_data_test.html");
yield waitForEvent(gBrowser.selectedBrowser.contentWindow, "test-indexedDB-done");
- gBrowser.removeCurrentTab();
+ yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
yield openPreferencesViaOpenPreferencesAPI("advanced", "networkTab", { leaveOpen: true });
// Test the initial states
let cacheUsage = yield getCacheUsage();
let quotaUsage = yield getQuotaUsage(TEST_ORIGIN);
let totalUsage = yield SiteDataManager.getTotalUsage();
Assert.greater(cacheUsage, 0, "The cache usage should not be 0");
@@ -146,10 +198,109 @@ add_task(function* () {
cacheUsage = yield getCacheUsage();
quotaUsage = yield getQuotaUsage(TEST_ORIGIN);
totalUsage = yield SiteDataManager.getTotalUsage();
is(cacheUsage, 0, "The cahce usage should be removed");
is(quotaUsage, 0, "The quota usage should be removed");
is(totalUsage, 0, "The total usage should be removed");
// Test accepting "Clear All Data" ends
- gBrowser.removeCurrentTab();
+ yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
});
+
+add_task(function* () {
+ yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
+
+ mockSiteDataManager.register();
+ let updatePromise = promiseSitesUpdated();
+ yield openPreferencesViaOpenPreferencesAPI("advanced", "networkTab", { leaveOpen: true });
+ yield updatePromise;
+
+ // Open the siteDataSettings subdialog
+ let doc = gBrowser.selectedBrowser.contentDocument;
+ let settingsBtn = doc.getElementById("siteDataSettings");
+ let dialogOverlay = doc.getElementById("dialogOverlay");
+ let dialogPromise = promiseLoadSubDialog("chrome://browser/content/preferences/siteDataSettings.xul");
+ settingsBtn.doCommand();
+ yield dialogPromise;
+ is(dialogOverlay.style.visibility, "visible", "The dialog should be visible");
+
+ let dialogFrame = doc.getElementById("dialogFrame");
+ let frameDoc = dialogFrame.contentDocument;
+ let hostCol = frameDoc.getElementById("hostCol");
+ let usageCol = frameDoc.getElementById("usageCol");
+ let statusCol = frameDoc.getElementById("statusCol");
+ let sitesList = frameDoc.getElementById("sitesList");
+ let mockSites = mockSiteDataManager.sites;
+
+ // Test default sorting
+ assertSortByHost("ascending");
+
+ // Test sorting on the host column
+ hostCol.click();
+ assertSortByHost("descending");
+ hostCol.click();
+ assertSortByHost("ascending");
+
+ // Test sorting on the permission status column
+ statusCol.click();
+ assertSortByStatus("ascending");
+ statusCol.click();
+ assertSortByStatus("descending");
+
+ // Test sorting on the usage column
+ usageCol.click();
+ assertSortByUsage("ascending");
+ usageCol.click();
+ assertSortByUsage("descending");
+
+ mockSiteDataManager.unregister();
+ yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
+
+ function assertSortByHost(order) {
+ let siteItems = sitesList.getElementsByTagName("richlistitem");
+ for (let i = 0; i < siteItems.length - 1; ++i) {
+ let aOrigin = siteItems[i].getAttribute("data-origin");
+ let bOrigin = siteItems[i + 1].getAttribute("data-origin");
+ let a = mockSites.get(aOrigin);
+ let b = mockSites.get(bOrigin);
+ let result = a.host.localeCompare(b.host);
+ if (order == "ascending") {
+ Assert.lessOrEqual(result, 0, "Should sort sites in the ascending order by host");
+ } else {
+ Assert.greaterOrEqual(result, 0, "Should sort sites in the descending order by host");
+ }
+ }
+ }
+
+ function assertSortByStatus(order) {
+ let siteItems = sitesList.getElementsByTagName("richlistitem");
+ for (let i = 0; i < siteItems.length - 1; ++i) {
+ let aOrigin = siteItems[i].getAttribute("data-origin");
+ let bOrigin = siteItems[i + 1].getAttribute("data-origin");
+ let a = mockSites.get(aOrigin);
+ let b = mockSites.get(bOrigin);
+ let result = a.status - b.status;
+ if (order == "ascending") {
+ Assert.lessOrEqual(result, 0, "Should sort sites in the ascending order by permission status");
+ } else {
+ Assert.greaterOrEqual(result, 0, "Should sort sites in the descending order by permission status");
+ }
+ }
+ }
+
+ function assertSortByUsage(order) {
+ let siteItems = sitesList.getElementsByTagName("richlistitem");
+ for (let i = 0; i < siteItems.length - 1; ++i) {
+ let aOrigin = siteItems[i].getAttribute("data-origin");
+ let bOrigin = siteItems[i + 1].getAttribute("data-origin");
+ let a = mockSites.get(aOrigin);
+ let b = mockSites.get(bOrigin);
+ let result = a.usage - b.usage;
+ if (order == "ascending") {
+ Assert.lessOrEqual(result, 0, "Should sort sites in the ascending order by usage");
+ } else {
+ Assert.greaterOrEqual(result, 0, "Should sort sites in the descending order by usage");
+ }
+ }
+ }
+});
+
--- a/browser/components/preferences/siteDataSettings.js
+++ b/browser/components/preferences/siteDataSettings.js
@@ -20,37 +20,78 @@ let gSiteDataSettings = {
// - uri: uri of site; instance of nsIURI
// - status: persistent-storage permission status
// - usage: disk usage which site uses
_sites: null,
_list: null,
init() {
+ function setEventListener(id, eventType, callback) {
+ document.getElementById(id)
+ .addEventListener(eventType, callback.bind(gSiteDataSettings));
+ }
+
this._list = document.getElementById("sitesList");
SiteDataManager.getSites().then(sites => {
this._sites = sites;
- this._sortSites(this._sites, "decending");
+ let sortCol = document.getElementById("hostCol");
+ this._sortSites(this._sites, sortCol);
this._buildSitesList(this._sites);
});
+
+ setEventListener("hostCol", "click", this.onClickTreeCol);
+ setEventListener("usageCol", "click", this.onClickTreeCol);
+ setEventListener("statusCol", "click", this.onClickTreeCol);
},
/**
- * Sort sites by usages
- *
* @param sites {Array}
- * @param order {String} indicate to sort in the "decending" or "ascending" order
+ * @param col {XULElement} the <treecol> being sorted on
*/
- _sortSites(sites, order) {
- sites.sort((a, b) => {
- if (order === "ascending") {
- return a.usage - b.usage;
- }
- return b.usage - a.usage;
+ _sortSites(sites, col) {
+ let isCurrentSortCol = col.getAttribute("data-isCurrentSortCol")
+ let sortDirection = col.getAttribute("data-last-sortDirection") || "ascending";
+ if (isCurrentSortCol) {
+ // Sort on the current column, flip the sorting direction
+ sortDirection = sortDirection === "ascending" ? "descending" : "ascending";
+ }
+
+ let sortFunc = null;
+ switch (col.id) {
+ case "hostCol":
+ sortFunc = (a, b) => {
+ let aHost = a.uri.host.toLowerCase();
+ let bHost = b.uri.host.toLowerCase();
+ return aHost.localeCompare(bHost);
+ }
+ break;
+
+ case "statusCol":
+ sortFunc = (a, b) => a.status - b.status;
+ break;
+
+ case "usageCol":
+ sortFunc = (a, b) => a.usage - b.usage;
+ break;
+ }
+ if (sortDirection === "descending") {
+ sites.sort((a, b) => sortFunc(b, a));
+ } else {
+ sites.sort(sortFunc);
+ }
+
+ let cols = this._list.querySelectorAll("treecol");
+ cols.forEach(c => {
+ c.removeAttribute("sortDirection");
+ c.removeAttribute("data-isCurrentSortCol");
});
+ col.setAttribute("data-isCurrentSortCol", true);
+ col.setAttribute("sortDirection", sortDirection);
+ col.setAttribute("data-last-sortDirection", sortDirection);
},
_buildSitesList(sites) {
// Clear old entries.
while (this._list.childNodes.length > 1) {
this._list.removeChild(this._list.lastChild);
}
@@ -60,10 +101,15 @@ let gSiteDataSettings = {
let size = DownloadUtils.convertByteUnits(data.usage);
let item = document.createElement("richlistitem");
item.setAttribute("data-origin", data.uri.spec);
item.setAttribute("host", data.uri.host);
item.setAttribute("status", prefStrBundle.getString(statusStrId));
item.setAttribute("usage", prefStrBundle.getFormattedString("siteUsage", size));
this._list.appendChild(item);
}
+ },
+
+ onClickTreeCol(e) {
+ this._sortSites(this._sites, e.target);
+ this._buildSitesList(this._sites);
}
};
--- a/browser/components/preferences/siteDataSettings.xul
+++ b/browser/components/preferences/siteDataSettings.xul
@@ -23,16 +23,16 @@
src="chrome://browser/locale/preferences/preferences.properties"/>
<vbox flex="1">
<description>&settings.description;</description>
<separator class="thin"/>
<richlistbox id="sitesList" orient="vertical" flex="1">
<listheader>
- <treecol flex="4" width="50" label="&hostCol.label;"/>
- <treecol flex="2" width="50" label="&statusCol.label;"/>
- <treecol flex="1" width="50" label="&usageCol.label;"/>
+ <treecol flex="4" width="50" label="&hostCol.label;" id="hostCol"/>
+ <treecol flex="2" width="50" label="&statusCol.label;" id="statusCol"/>
+ <treecol flex="1" width="50" label="&usageCol.label;" id="usageCol"/>
</listheader>
</richlistbox>
</vbox>
</window>